이 값을 로그에 남겨도 되나
이메일을 SHA-256으로 해싱해도 익명화가 아닙니다. 값별로 무엇이 드러나는지 실측과 함께 정리했습니다.
디버깅을 위해 값을 로그에 찍습니다. 그 로그는 대체로 오래 남고, 여러 사람이 보고, 외부 서비스로도 갑니다.
값별 판단
값 판단 이유
JWT 전체 안 됨 페이로드가 그대로 읽히고 서명까지 있으면 재사용 가능
세션 ID 안 됨 로그를 읽으면 세션을 탈취한다
bcrypt 해시 안 됨 오프라인 대입 공격의 재료
API 키 전체 안 됨 그대로 쓸 수 있다
API 키 앞 8자 대체로 됨 키를 특정하되 복원은 못 하는 절충
UUID v4 됨 아무 정보도 담지 않는다
UUID v1 / v7 / ULID 주의 생성 시각이 드러난다
JWT 의 sub 클레임 대체로 됨 내부 ID. 그 자체가 개인을 특정하면 개인정보
이메일 주소 주의 개인정보
이메일의 SHA-256 주의 해시해도 복원된다
해시가 익명화가 아닌 이유
"개인정보를 해싱해서 로그에 남기면 안전하다"는 처리가 흔합니다. 재 봤습니다.
SHA-256 계산 속도 158만 회/초 (단일 코어)
유출 이메일 1억 건 훑기 63초
유출된 이메일 목록은 공개적으로 유통됩니다. 그 목록을 전부 해싱해서 로그의 해시와 대조하면 됩니다. 1억 건에 1분입니다.
전화번호는 더 짧습니다.
010-XXXX-XXXX 조합 수 1억 개
전수 계산 63초
목록조차 필요 없습니다. 가능한 모든 번호를 만들어 해싱하면 됩니다.
입력 공간이 좁으면 해시는 되돌아갑니다. 이메일과 전화번호가 정확히 그런 경우입니다. 비밀번호와 다른 점은 사용자가 고르는 값이 아니라는 것입니다 — 형식이 정해져 있고 후보가 열거 가능합니다.
그래도 해싱해야 한다면
느린 해시를 쓰고 솔트를 붙입니다.
SHA-256 1억 건에 63초
PBKDF2 1억 건에 76일 (60만 회 반복, 단일 코어)
10만 배 차이입니다. 다만 76일도 절대적인 안전은 아닙니다 — GPU와 병렬화를 쓰면 줄어듭니다.
솔트가 더 중요합니다. 서비스별로 다른 솔트를 쓰면 미리 계산된 표를 그대로 쓸 수 없고, 다른 서비스의 유출 데이터와 대조할 수도 없습니다.
가장 확실한 것은 원본과 무관한 식별자로 바꾸는 것입니다. 내부 사용자 ID나 UUID v4를 로그에 쓰고, 그 매핑을 별도 저장소에 두는 방식입니다.
시간이 드러나는 식별자
UUID v4는 무작위라 아무것도 담지 않습니다. 그런데 정렬 가능한 식별자는 다릅니다.
UUID v7 앞 48비트가 유닉스 밀리초
ULID 앞 10자가 타임스탬프
UUID v1 타임스탬프와 (구현에 따라) MAC 주소
계정 ID가 ULID이면 그 계정이 언제 만들어졌는지 누구나 알 수 있습니다. 대부분 문제가 아니지만, 가입 시각이 민감한 서비스도 있습니다.
UUID v1은 노드 필드에 MAC 주소가 들어가는 구현이 남아 있습니다. 그 경우 어느 기계에서 만들었는지까지 나갑니다.
로그가 가는 곳
로그를 어디까지 보내는지도 함께 봐야 합니다.
외부 로그 수집 서비스로 보내면 그 서비스의 보관 정책과 접근 권한이 적용됩니다. 오류 추적 도구는 예외 발생 시점의 변수를 통째로 담기도 합니다 — 요청 객체가 그대로 들어가면 헤더의 Authorization도 함께 갑니다.
프레임워크의 기본 마스킹 목록을 확인하는 것이 먼저입니다. 대체로 password·token·secret 같은 키 이름을 거르는데, 필드 이름이 다르면 걸러지지 않습니다.
확인 순서
- 오류 추적 도구가 요청 객체를 통째로 담는지 봅니다. 헤더가 함께 갑니다
- 마스킹 목록에 실제로 쓰는 필드 이름이 들어 있는지 확인합니다
- 개인정보를 해싱해서 남기고 있다면 그것을 익명화로 세지 않습니다
- 정렬 가능한 식별자를 외부에 노출하면 생성 시각이 함께 나갑니다
- 로그 보관 기간을 봅니다. 오래 남을수록 유출 시 영향이 큽니다
tools.onuel.dev에서 확인해 볼 수 있습니다. JWT 디코더에 토큰을 넣으면 페이로드가 키 없이 읽히는 것이 보이고, SHA-256에 같은 이메일을 두 번 넣으면 항상 같은 값이 나오는 것 — 대조가 가능하다는 뜻 — 을 확인할 수 있습니다. 느린 해시는 PBKDF2와 bcrypt 쪽이고, 시각을 담지 않는 식별자는 UUID v4입니다.
- #로깅
- #보안
- #개인정보