LabHub
배우기 러닝패스 코스

은행 현장의 언어 · 가리고도 따라갈 수 있게 · 이론

가리고 나서도 그 사건을 따라갈 수 있는가

LabHub 에서 이어서 보기

한 줄 요약

로그와 반출 파일에서 개인정보를 지우는 일은 "안 보이게 만들기" 가 아니라, 조사에 필요한 연결성은 남기고 사람은 못 알아보게 만들기 다. 그 경계를 정하는 것이 마스킹 규칙과 결정적 가명값이고, 그것이 제대로 됐는지는 기계로 다시 재서 증명한다.

왜 이게 필요했나

장애 조사를 하다 보면 애플리케이션 로그를 통째로 받아 보게 된다. 그런데 이체 API 의 로그에는 자유 문장이 섞여 있고, 그 문장 안에 계좌번호와 카드번호, 전화번호와 이메일이 그대로 적혀 있는 경우가 흔하다. 개발자가 나쁜 마음으로 적은 것이 아니라, 장애를 빨리 찾으려고 "요청 전문을 통째로" 찍은 줄 하나가 몇 년째 남아 있는 것이다.

이 로그를 분석 담당이나 공급사에 넘기려면 개인정보를 걷어 내야 한다. [개인정보 보호법](https://www.law.go.kr/%EB%B2%95%EB%A0%B9/%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%20%EB%B3%B4%ED%98%B8%EB%B2%95)은 목적을 이룬 개인정보를 지체 없이 파기하도록 하고([제21조](https://www.law.go.kr/%EB%B2%95%EB%A0%B9/%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%20%EB%B3%B4%ED%98%B8%EB%B2%95/%EC%A0%9C21%EC%A1%B0)), 조사에 필요한 것은 사람의 신원이 아니라 "같은 계좌에서 몇 번 실패했는가" 같은 연결성이다. 그런데 계좌번호를 전부 별표로 덮어 버리면 그 연결성까지 사라진다. 같은 사람이 세 번 실패한 것인지, 세 사람이 한 번씩 실패한 것인지 구분할 수 없게 된다. 가려야 하는데, 이어 볼 수는 있어야 한다. 이 두 요구를 동시에 만족시키는 것이 이 주제의 전부다.

어떻게 동작하나

스크러빙은 세 단계로 나뉜다. 찾고, 가르고, 바꾼다.

찾기 는 정규식이다. [re 모듈](https://docs.python.org/3/library/re.html)로 계좌번호 모양, 16자리 숫자, 휴대전화 모양, 이메일 모양을 훑는다. 문제는 정규식이 "모양" 만 본다는 것이다. 은행 로그에는 16자리 숫자가 카드번호 말고도 널려 있다. 정산 일련번호, 전문 추적번호, 배치 작업 식별자가 전부 16자리다. 이것까지 별표로 덮으면 조사에 필요한 식별자가 사라진다.

가르기 가 여기서 들어온다. 카드번호에는 검사숫자가 붙어 있다. 오른쪽 끝에서 두 번째 자리부터 한 칸 걸러 두 배로 만들고, 두 배가 10 이상이면 자릿수를 더한다(또는 9를 뺀다). 모두 더한 값이 10의 배수면 통과다. 흔히 뤼른(Luhn) 알고리즘이라 부르고, 카드번호 체계 표준인 ISO/IEC 7812-1 이 이 검사숫자 방식을 규정한다(표준 원문은 유료라 이 글에서는 링크하지 않는다). 무작위 16자리 숫자가 우연히 이 검사를 통과할 확률은 10분의 1이므로, 검사숫자를 붙이면 오탐이 열 배 줄어든다. 완전히 없어지지는 않는다는 뜻이기도 하다.

바꾸기 는 두 가지를 함께 한다. 하나는 사람이 눈으로 확인할 수 있게 뒤 네 자리만 남기는 마스킹이고, 다른 하나는 기계가 이어 보게 하는 가명 토큰이다. 토큰은 [hmac 모듈](https://docs.python.org/3/library/hmac.html)로 만든다. 값 자체를 해시하면 안 된다. 계좌번호의 경우의 수는 적어서, 단순 SHA-256 은 사전 공격으로 되돌려진다. 비밀 키를 쓰는 HMAC 이어야 키를 모르는 쪽에서 되돌릴 수 없다. 키는 [secrets 모듈](https://docs.python.org/3/library/secrets.html)이나 openssl rand 로 만들고, 로그와 같은 곳에 두지 않는다.

토큰의 메시지에는 용도 를 함께 넣는다. acct|91231457820card|91231457820 이 서로 다른 값이 되게 하는 것이다. 이렇게 해 두면 어느 한 쪽의 토큰 표가 새어 나가도 다른 쪽을 맞춰 볼 수 없다.

원본  "계좌 912-31-457820 카드 9012345678901234 일련번호 9900112233445566"          │                    │                      │          │                    │                      └ 검사숫자 실패 → 카드 아님 → 그대로 둔다          │                    └ 검사숫자 통과 → ************1234          └ ***-**-**7820  +  tokens.acct = ["acct_1f3c…"]  (같은 계좌면 언제나 같은 값)

현장에서 만나는 모습

첫째, 두 번 돌리면 달라지는 스크러버 다. 파이프라인은 재시도를 한다. 이미 가려진 줄을 다시 스크러빙했는데 *--**7820 에서 별표를 또 세거나, 토큰을 다시 계산해 다른 값이 나오면 대사가 깨진다. 이미 처리된 표시를 남기고, 규칙이 자기 출력에 다시 걸리지 않도록 만든다.

둘째, 가림과 삭제를 헷갈리는 것 이다. 뒤 네 자리를 남기는 마스킹은 되돌릴 수 없지만, 이미 계좌번호 목록을 가진 사람에게는 후보를 좁혀 주는 단서가 된다. 소수의 계좌만 다루는 파일에서는 뒤 네 자리만으로 한 사람이 특정되기도 한다. 그래서 반출 파일에서는 마스킹한 값 자체를 조사 키로 쓰지 않고 토큰을 쓴다.

셋째, 키 관리가 없는 가명화 다. 키를 로그와 같은 디렉터리에 두거나, 코드에 박아 두거나, 아무도 키가 바뀐 시점을 모르면 가명값의 의미가 매번 달라진다. 보고서에 키 식별자(키의 해시 앞자리)를 적어 두면 "이 토큰들은 어느 키로 만든 것인가" 를 나중에 말할 수 있다.

실무에서 진짜 중요한 것

다음 실습에서 할 것

합성 이체 로그 640줄을 직접 만들고, 후보를 찾고, 검사숫자로 카드와 일련번호를 가른 뒤, 마스킹과 가명 토큰을 붙인 반출본을 만든다. 그러고 나서 원본 계좌번호를 한 번도 쓰지 않고 사고 고객의 사건을 토큰만으로 추적하고, 두 번 돌려 같은지와 남은 누출이 0인지를 확인해 정책과 보고서로 마무리한다. 채점기는 여러분의 키로 토큰을 다시 계산하고, 여러분의 프로그램을 자기가 만든 번호로 직접 실행해 본다.