Skip to content
isdnetworks
Go back

로그에 개인정보가 그대로 있었다

주문 처리를 추적하려고 로그를 자세히 남기게 했다. 며칠 뒤 로그를 보다가 이런 줄을 봤다.

[주문] 접수 name=홍길동 phone=010-1234-5678 addr=서울시 ... card=1234-56**-****-7890

이름과 연락처와 주소와 카드 번호 일부가 다 있다.

Table of contents

Open Table of contents

증상 — 통째로 찍고 있었다

문제가 된 코드다.

log_message('info', '[주문] 접수 ' . json_encode($_POST, JSON_UNESCAPED_UNICODE));

json_encode($_POST) 라 무엇이 들어왔는지 다 보여 편했다. 그게 문제였고 안 남겨야 할 것도 다 남는다.

로그는 개발자가 보고 백업에 들어가고 여러 곳으로 복사된다. 원본 표는 접근 권한을 좁혀 뒀는데 로그 파일에는 그런 것이 없었다.

추적을 위한 편의가 다른 위험을 만들고 있었다.

남길 것을 골랐다

전부 찍는 대신 필요한 것만 골랐다.

log_message('info', sprintf(
    '[주문] 접수 order=%s items=%d amount=%d pay=%s',
    $orderNo, count($items), $amount, $payType
));

orderamountpay 만 있으면 추적에 충분했다. 이름이나 연락처가 필요하면 주문번호로 조회해서 보므로 로그에 있을 이유가 없다.

식별이 필요한 경우도 있었다. 그때는 일부만 남겼다.

function maskPhone($p) {
    $p = preg_replace('/[^0-9]/', '', $p);
    if (strlen($p) < 8) return '***';
    return substr($p, 0, 3) . '****' . substr($p, -4);
}
phone=010****5678

substr 로 앞뒤만 남기니 대조는 되고 전체는 안 남는다. 이름은 성만 남겼다.

function maskName($n) {
    return mb_substr($n, 0, 1) . str_repeat('*', max(1, mb_strlen($n) - 1));
}

이미 쌓인 것을 봤다

앞으로를 고치는 것과 별개로 이미 있는 것을 확인했다.

$ grep -rlE "010-[0-9]{4}-[0-9]{4}" /var/log/app/

grep -rlE 로 석 달치가 나왔고 파일이 90개쯤이었다. 보관 기간을 확인하니 로그를 1년 두고 있었는데 개인정보가 든 채로 1년은 길다.

1. 개인정보가 든 옛 로그를 정리한다
2. 보관 기간을 줄인다 (1년 → 3개월)
3. 앞으로는 안 남긴다

1번은 지우는 것과 가리는 것 중에 골라야 했다. 문제 추적에 필요한 부분이 있어서 가리기로 했다.

sed -i -E 's/(01[0-9])-[0-9]{4}-([0-9]{4})/\1****\2/g' /var/log/app/*.log

sed -i 는 되돌릴 수 없으니 사본을 먼저 뜨고 시험 파일로 확인한 뒤에 돌렸다. logrotate 의 보관 개수도 줄이고 백업에 들어간 것도 확인했다.

앞을 막는 것만 하고 끝냈으면 이미 나간 것은 그대로 남았을 것이다.

기준과 검사와 권한

무엇을 어디까지 남길지 기준을 적었다.

남긴다
  식별자 (주문번호, 회원번호, 요청 식별자)
  분류 값 (상태, 유형, 결제수단)
  수치 (금액, 개수, 소요 시간)
  오류 내용

가리고 남긴다
  연락처 (앞3 + 뒤4)
  이름 (성만)

안 남긴다
  주민번호·카드번호·계좌번호 (일부라도)
  비밀번호·토큰·키
  주소 전체
  메일 주소 전체

식별자만 있으면 필요할 때 조회하므로 로그에 값을 둘 이유가 없다.

기준을 정해도 새로 짜는 코드에서 또 나올 수 있어서 배포 전에 훑게 했다.

PATTERNS='json_encode\(\$_POST|json_encode\(\$_REQUEST|print_r\(\$_POST'
if grep -rnE "$PATTERNS" --include=*.php application/; then
  echo "요청 전체를 로그에 남기는 코드가 있습니다"
  exit 1
fi

json_encode($_POST)print_r($_POST) 같은 패턴을 잡는다. 완벽하지 않지만 흔한 경우는 걸린다.

내용을 정리해도 파일을 아무나 읽으면 소용없어서 권한도 봤다.

$ ls -l /var/log/app/
-rw-r--r-- 1 www-data www-data ... app.log

0644 라 같은 서버의 누구나 읽을 수 있었다. umask 기본값 때문에 그렇게 만들어진 것이었다.

$ chmod 640 /var/log/app/*.log
$ chown www-data:adm /var/log/app/*.log

읽을 사람을 그룹으로 묶고 0640 으로 좁혔다. 새로 생기는 파일도 그렇게 되게 설정을 바꿨다.

정리


Share this post on:

Previous Post
관리자와 사용자가 같은 조회를 썼다
Next Post
재고를 실시간으로만 맞추려니 어긋난 걸 못 잡았다