로그에서 개인정보를 가리는 규칙을 Monolog 프로세서로 넣었더니 필요한 정보까지 가려졌다. \d{8,} 처럼 숫자가 여러 자리이면 가리는 넓은 규칙 하나로 처리하고 있었다.
Table of contents
Open Table of contents
원인 — 필요한 정보까지 가린 규칙
전화번호와 카드번호를 가리려는 의도였는데 order_no 도 같은 조건에 걸렸다. 어느 주문인지가 **** 로 안 보이니 로그를 남기는 의미가 사라졌다.
넓은 규칙은 잡아야 할 것을 확실히 잡는 대신 안 잡아야 할 것도 함께 잡는다. 무엇을 가릴지만 정하고 무엇을 안 가릴지를 안 정한 것이 원인이었다.
조치 — 가릴 것과 가리지 말 것
주민등록번호와 카드번호와 전화번호와 계좌번호와 비밀번호를 MASK.md 의 가릴 목록으로 적었다. order_no 와 product_no 와 내부 member_id 와 금액과 건수를 안 가릴 목록으로 함께 적었다.
가릴 것만 적으면 규칙이 넓어졌을 때 그것을 막을 기준이 없다. 안 가릴 목록이 규칙의 상한을 정하는 역할을 했다.
주의 — 형태로 가른 뒤 놓치는 것
두 가지 방법이 있었는데 넓은 규칙에 order_no 예외를 붙이는 것과 종류마다 형태에 맞는 규칙으로 나누는 것이다. 뒤쪽을 골랐다. 이유는 예외를 붙이면 다음에 안 가려야 할 번호가 생길 때마다 또 붙여야 해서다. 대신 형태로 잡으면 형태가 다른 표기를 놓치는 것을 감수했다. 나누니 주문 번호는 그대로 남고 개인정보만 가려졌다.
구분 기호가 없거나 공백으로 쓰인 경우는 규칙에 넣었는데 +82 국제 표기 형식은 처음에 놓쳤다. 시험 문자열 목록을 만들어 확인하고 나서 그 형식을 추가했다.
검증 — 안 가려져야 하는 시험 입력
시험 목록에 가려져야 하는 입력만 넣으면 넓은 규칙이 그대로 통과한다. 그래서 가려지면 안 되는 입력도 함께 넣었다.
PHPUnit 에서 주문 번호와 금액이 가려지지 않아야 통과하게 하니 규칙이 넓어지는 순간 시험이 실패한다. 안 가려져야 하는 입력이 규칙의 상한을 실행 가능한 형태로 만든 셈이다.
배경 — 안 찍는 것이 먼저
한 줄에서 여러 건이 가려지면 그 자리를 보게 했더니 Log::info($member) 로 회원 정보를 통째로 찍는 코드가 나왔다. 가리는 것보다 필요한 항목만 찍는 것이 나았다.
안 찍는 것과 그 자리에서 가리는 것과 Monolog 프로세서에서 가리는 것 셋을 다 뒀는데 마지막 하나에만 기대면 그것이 실패할 때 그대로 남기 때문이다. 규칙이 여럿이 되어 처리가 느려진 것은 ctype_digit 이 안 걸리는 줄을 먼저 걸러 내는 값싼 검사를 앞에 두어 해결했다.
정리
- 넓은 규칙 하나가 필요한 것까지 삼킨다
- 가릴 것만 정하면 규칙의 상한을 정할 기준이 없다
- 안 가릴 목록이 그 상한 역할을 한다
- 형태로 나눠 잡되 다른 표기를 놓치는지 확인한다
- 시험에 가려지면 안 되는 입력을 함께 넣는다
- 그 입력이 규칙의 상한을 실행 가능한 형태로 만든다
- 가리는 것보다 애초에 안 찍는 것이 먼저다
- 값싼 검사를 앞에 두면 비싼 검사의 횟수가 준다