사용자 번호 8461039 계정을 확인해 달라는 문의를 받았다. 그 번호로 WHERE user_no = ? 를 돌리니 0건이 나왔다.
Table of contents
Open Table of contents
화면 번호가 변환된 값이었다
그 사람이 화면에서 그 번호를 보고 적어 준 것이므로 존재하지 않을 리가 없었다. 그러면 내가 WHERE 에 넣은 값이 user_no 와 다르다는 뜻이라 grep -rn 으로 화면에 그 번호를 찍는 코드를 찾았다.
echo encodeId($user->user_no);
user_no 가 그대로 안 나가고 변환된다. 데이터베이스에는 1204 같은 작은 수인데 화면에는 8461039 로 보인다.
왜 이렇게 하는지 물어보니 이유가 둘 있었다. user_no 가 AUTO_INCREMENT 라 순차 번호가 그대로 보이면 규모가 드러난다. 회원 번호가 1204면 회원이 천 명대라는 것이 보인다. 그리고 /user/1204 를 /user/1205 로 바꾸면 다른 사람 화면이 나올 수 있다.
원인 — 되돌리는 함수가 있었다
변환 함수 옆에 되돌리는 함수도 있었다.
function encodeId($n) { ... }
function decodeId($s) { ... }
조회할 때는 되돌린 값을 쓴다.
$userNo = decodeId($input);
$user = $this->db->where('user_no', $userNo)->get('user')->row();
이것을 몰라서 화면 번호로 직접 조회하고 있었다. decodeId 로 문의받은 값을 바꾸니 실제 user_no 가 나왔다.
문의를 확인할 때마다 변환을 손으로 하는 것은 번거로워서 관리자 화면에 변환 도구를 넣었다.
입력: 8461039
결과: user_no = 1204
반대 방향도 넣어서 데이터베이스에서 본 번호로 화면 주소를 만들 수 있게 했다. 이것을 만들고 나서 확인 시간이 크게 줄었다.
로그에 무엇을 남길지 정했다
로그에도 어느 번호를 남길지 문제가 됐다. 화면 번호를 남기면 문의와 대조하기 쉽고 내부 번호를 남기면 조회하기 쉽다.
log_message('info', "주문 처리 user={$userNo}(disp:" . encodeId($userNo) . ")");
둘 다 남겼다. 길어지지만 어느 쪽에서 와도 grep 으로 찾을 수 있다.
변환이 안 되는 값의 처리
잘못된 값을 넣으면 decodeId 가 이상한 수를 돌려줬다.
decodeId('abc'); // 0
decodeId(''); // 0
WHERE user_no = 0 은 어차피 0건이라 잘못된 입력과 없는 사용자가 구분이 안 된다. 이 둘은 답이 달라야 한다.
function decodeId($s) {
if (!preg_match('/^\d{7,10}$/', $s)) {
return false;
}
...
}
$userNo = decodeId($input);
if ($userNo === false) {
return error('잘못된 번호 형식입니다');
}
preg_match 로 형식을 먼저 보고 안 맞으면 조회 전에 오류로 알린다.
문서화와 남은 정리
사용자 번호만 변환하는지도 확인했다.
$ grep -rn "encodeId\|decodeId" --include=*.php application/ | wc -l
38
주문 번호와 상품 번호도 같은 방식이었다. 각각 변환 결과가 겹치면 안 되는데 같은 함수를 쓰면 user_no=1204 와 order_no=1204 가 같은 문자열이 된다.
encodeId($n, 'user');
encodeId($n, 'order');
대상별로 다른 값을 섞는 인자가 있었다. 다만 몇 군데에서 인자를 안 주고 있어서 기본값으로 처리돼 겹칠 수 있었고 전부 인자를 주게 고쳤다.
이 규칙이 어디에도 안 적혀 있어서 docs/ 에 적었다.
## 식별자 표기
데이터베이스의 순차 번호(user_no, order_no 등)는 화면과 주소에
그대로 나가지 않는다. encodeId()로 변환한 값이 나간다.
- 화면 번호 → 내부 번호: decodeId($s, $type)
- 내부 번호 → 화면 번호: encodeId($n, $type)
- 두 번째 인자를 반드시 준다. 안 주면 대상 간에 값이 겹친다
- 관리자 화면의 변환 도구: /admin/tools/id
안 적으면 이 시스템을 처음 보는 사람이 똑같이 0건을 받고 똑같이 헤맨다. 실제로 나 말고도 같은 일을 겪은 사람이 있었다.
정리
- 화면 식별자가
user_no와 다르면 그 값으로 조회가 안 된다 - 변환하는 이유는
AUTO_INCREMENT규모 노출과 순차 접근을 막는 것이다 decodeId를 찾아서 쓰고 변환 도구를 만들어 두면 확인이 빨라진다- 로그에 양쪽 번호를 다 남겨 어느 쪽으로도
grep되게 한다 decodeId가 0을 돌려주면 없는 대상과 구분이 안 된다preg_match로 형식을 보고 조회 전에 오류로 알린다- 대상별로 인자를 줘야 값이 안 겹친다
- 안 적으면 사람마다 같은 일을 겪는다