ログから個人情報を消しても事案を追いかける
한국어 원문으로 표시합니다.
목표
이체 API 로그에서 개인정보 후보를 찾아 검사숫자로 오탐을 걸러 내고, 마스킹과 결정적 가명 토큰으로 반출본을 만든다. 그러고도 사고 고객의 사건을 토큰만으로 추적하고, 두 번 돌려도 같은 결과가 나오며 남은 누출이 없음을 증명한다.
왜 중요한가
장애 조사에 쓰는 로그에는 자유 문장이 섞여 있고 그 안에 계좌번호와 카드번호가 그대로 적혀 있다. 그대로 넘길 수는 없지만, 전부 별표로 덮으면 "같은 계좌에서 세 번 실패했다" 는 사실까지 사라진다. 정규식은 모양만 보므로 16자리 정산 일련번호까지 카드로 오해한다. 검사숫자를 한 번 더 걸면 오탐이 열 배 줄고, 조사에 필요한 식별자를 죽이지 않는다. 가린 값에 HMAC 결정적 토큰을 달면 같은 계좌는 언제나 같은 토큰이 되어 이어 볼 수 있고, 키가 없으면 되돌릴 수 없다. 스크러빙 파이프라인은 재시도되므로 두 번 돌려도 결과가 같아야 한다.
단계
- /root/mask/gen_logs.py 를 만들어 실행해 /root/mask/app.log(JSON Lines 500줄 이상)와 /root/mask/customers.csv(고객 30명 이상)를 만든다. 계좌 30개 이상, 검사숫자를 통과하는 카드 40개 이상, 통과하지 못하는 16자리 수 20개 이상을 섞고, 가장 많이 나오는 계좌 하나가 2위보다 3줄 이상 많게 한다.
- /root/mask/find_candidates.py 로 네 종류의 후보를 모두 찾아 /root/mask/candidates.json 에 적는다(total·by_kind·items).
- /root/mask/luhn.py 를 만들고(유효하면 종료 코드 0), 카드 후보를 /root/mask/candidates_luhn.json 의 card_verified·not_card 로 가른다.
- /root/mask/scrub.py 로 /root/mask/app.scrubbed.log 를 만든다. 계좌·카드·전화·이메일을 아래 참고의 규칙대로 가리고, 카드가 아닌 16자리 수는 그대로 둔다.
- /root/mask/keys/scrub.key 를 만들고 scrub.py 가 각 줄에 tokens(acct·card)를 붙이게 한 뒤 /root/mask/app.scrubbed.log 를 다시 만든다.
- app.log 에서 가장 많이 나온 계좌의 토큰을 구해, 반출본만 보고 /root/mask/trace.json 에 top_account_token·event_count·req_ids·first_ts·last_ts 를 적는다.
- /root/mask/leakscan.py 를 만들고, scrub.py 를 반출본에 다시 돌려도 결과가 같은지 확인한 뒤 검사 결과를 /root/mask/leak_report.json 에 남긴다.
- /root/mask/policy.json 과 /root/mask/scrub_report.json 을 만든다. 보고서의 숫자는 실제 파일에서 세고, policy_sha256 은 정책 파일을 sha256 으로 잰 값이다.
참고
- 로그 한 줄의 키는 ts·req_id·event·actor·channel·amount·msg 일곱 개이고, ts 는 2026-03-04T10:22:31+09:00 모양입니다.
- 찾는 모양: 계좌
9\d{2}-\d{2}-\d{6}· 카드 후보(?<!\d)\d{16}(?!\d)· 전화01[016789]-\d{3,4}-\d{4}· 이메일[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,} - 가리는 규칙: 계좌
912-31-457820→***-**-**7820, 카드9012345678901234→************1234, 전화010-3388-7412→010-****-7412, 이메일nara.kim@example.com→n***@example.com. 검사숫자에 떨어지는 16자리 수는 건드리지 않습니다. - 토큰: 키 파일 내용의 앞뒤 공백을 지운 바이트를 HMAC-SHA256 키로 쓰고, 메시지는
용도|숫자만남긴값, 16진수 앞 16자리에acct_·card_를 붙입니다. 모든 줄에 tokens.acct 와 tokens.card 를 둡니다(없으면 빈 배열). - 키 만들기:
openssl rand -hex 32 > /root/mask/keys/scrub.key - 채점기는 여러분이 만든 프로그램을 자기가 만든 번호와 표본 파일로 직접 실행하고, 토큰을 여러분의 키로 다시 계산해 대조합니다.
- 흔한 실수: 16자리 일련번호까지 가려 조사 단서를 없애기, 마스킹한 뒤 다시 스크러빙해 별표를 또 덧씌우기, 누출 보고서에 유출된 값을 그대로 적기.
- 여기 나오는 사람·계좌·카드·연락처는 전부 합성입니다. 실제 기관의 번호대를 쓰지 마세요.
합성 이체 로그 스냅샷 만들기
/root/mask/gen_logs.py 를 만들어 실행해 /root/mask/app.log 와 /root/mask/customers.csv 를 만드세요.
현장에서는 데이터가 먼저 있지만 여기서는 우리가 만듭니다. 계좌·카드·연락처는 지어낸 값이어야 하고, 카드번호는 검사숫자가 맞아야 합니다. 16자리인데 검사숫자가 틀린 정산 일련번호도 섞으세요 — 다음 단계의 오탐이 바로 그것입니다.
정규식으로 후보를 하나도 빠짐없이 찾기
/root/mask/find_candidates.py 로 계좌·카드·전화·이메일 후보를 찾아 /root/mask/candidates.json 에 적으세요.
읽을 파일은 1단계에서 만든 /root/mask/app.log 입니다. 이 단계는 재현율이 전부입니다. 오탐은 다음 단계에서 걷어 내니 16자리 숫자는 일단 전부 카드 후보로 넣으세요. line 은 1부터 세고, value 는 그 줄에 실제로 있는 문자열이어야 합니다.
검사숫자로 카드와 일련번호 가르기
/root/mask/luhn.py 를 만들고, 카드 후보를 /root/mask/candidates_luhn.json 의 card_verified 와 not_card 로 가르세요.
가를 대상은 2단계에서 만든 /root/mask/candidates.json 의 card 후보입니다. 오른쪽 끝에서 두 번째 자리부터 한 칸 걸러 두 배로 만들고, 두 배가 10 이상이면 9를 뺍니다. 모두 더해 10으로 나누어떨어지면 통과입니다. 채점기는 자기가 만든 번호로 luhn.py 를 직접 실행하므로 종료 코드 계약을 지켜야 합니다.
규칙대로 가리고 일련번호는 남기기
/root/mask/scrub.py 를 만들어 /root/mask/app.scrubbed.log 를 만드세요. 카드가 아닌 16자리 수는 그대로 둡니다.
읽을 파일은 /root/mask/app.log 이고 결과는 /root/mask/app.scrubbed.log 입니다. 지시문 참고의 네 가지 치환 모양을 정확히 지키세요. 줄 수는 원본과 같아야 하고, amount 같은 숫자 필드는 건드리지 않습니다. 채점기는 원본에서 기대값을 다시 계산해 한 글자까지 대조합니다.
같은 계좌에 같은 가명 토큰 달기
/root/mask/keys/scrub.key 를 만들고 scrub.py 가 각 줄에 tokens 를 붙이게 한 뒤 /root/mask/app.scrubbed.log 를 다시 만드세요.
4단계에서 만든 /root/mask/scrub.py 를 고쳐 씁니다. 값만 해시하면 경우의 수가 적어 사전 공격으로 되돌아옵니다. 비밀 키를 쓰는 HMAC 이어야 합니다. 용도를 메시지에 넣지 않으면 계좌와 카드가 같은 숫자일 때 같은 토큰이 됩니다.
토큰만으로 사고 고객의 사건 따라가기
가장 많이 나온 계좌의 토큰으로 반출본을 조사해 /root/mask/trace.json 을 만드세요.
계좌를 고르는 일까지만 원본을 봅니다. 그다음부터는 app.scrubbed.log 의 tokens.acct 만 보고 셉니다. 결과 파일에 계좌번호를 다시 적으면 지금까지 한 일이 무의미해집니다.
두 번 돌려도 같은지, 남은 누출이 없는지
/root/mask/leakscan.py 를 만들고, scrub.py 를 반출본에 다시 돌려 결과가 같은지 확인한 뒤 /root/mask/leak_report.json 을 남기세요.
이미 가린 값이 규칙에 다시 걸리지 않는지, 토큰이 이미 있으면 다시 계산하지 않는지 보세요. 검사기는 16자리라고 다 잡으면 안 됩니다 — 일련번호까지 누출로 세면 아무도 쓰지 않습니다.
반출 정책과 스크러빙 보고서 남기기
/root/mask/policy.json 과 /root/mask/scrub_report.json 을 만드세요. 보고서의 숫자는 실제 파일에서 셉니다.
정책의 mask 값은 남길 글자 수이고 key_id 는 /root/mask/keys/scrub.key 의 sha256 앞 12자리입니다. 보고서의 masked 는 중복을 포함한 건수, tokens 는 서로 다른 토큰의 수이며, policy_sha256 은 정책 파일 전체를 잰 값입니다.