DB Key를 URI나 파라미터로 노출하는 것은 위험할까?
TL;DR
Auto‑Increment ID를 그대로 URI에 노출하면 예측 가능성 때문에 brute‑force·IDOR 등 여러 공격에 당할 수 있는 취약점이 발생한다. 대안은 식별자를 암·복호화처리하거나, 처음부터 예측 불가능한 식별자를 쓰는 것이다. 후자가 훨씬 단순하고 실용적이다.
계기가 된 논쟁
“URL 에서
id
가 보이는 건 안티패턴이다.”
당시 그 동료는 /products/1
같은 요청을 문제 삼으며, 문자 단위 1:1 매핑으로 /products/iksd
→ 내부적으로 1234
로 처리하도록 구현해 두었다. 나는 REST 관련 문서를 찾아가며 “왜 그렇게 해요? 일반적인 REST API 경로 잖아요”라고 맞섰다.
실제로 대표적인 REST 참고 문서에서도 id
가 포함된 URI를 예시로 쓰고 있다.
- restfulapi.net →
/customers/{customerId}/accounts
- www.abstractapi.com/guides/api-glossary/uri →
GET /users/123
- dzone.com/articles/7-rules-for-rest-api-uri-design-1 →
/students/3248234/courses/2005/fall
그런데 시간이 지나 생각해 보니, 그 동료가 걱정한 건 auto increment id(이하 aid) 가 갖는 특유의 보안·운영 리스크였다. (읽으면서 눈치챈 사람이 이미 있겠지만 저런 1:1 매핑은 공격을 당할 때 10분의 시간도 못벌어줄 가능성이 크다.)
Auto‑Increment ID가 노출될 때 발생하는 보안 취약점
- 예측 가능한 식별자
1, 2, 3 …
처럼 순차 증가하니 공격자가 손쉽게 다음·이전 레코드를 시도할 수 있다. - 데이터 규모 유추
/posts/123456
→ “대략 12만 건쯤 있겠군.” - IDOR(인증·인가 우회)
다른 사용자의 ID 를 추측해 접근을 시도할 여지가 커진다. - 브루트포스 자동화
ID 범위를 스크립트로 훑어보며 데이터 수집·파괴 가능. - 경쟁사·외부 분석
주문·가입 속도 등 내부 지표가 간접적으로 노출된다. - 다른 취약점과 결합
SQL 인젝션, 캐시 조작 등 2차 공격의 열쇠가 될 수 있다.
운영 측면에서 aid가 안고 있는 기술적 부담
# | 위험 요소 | 이유 |
---|---|---|
1 | 키 충돌 | 마이크로서비스별 DB 분리 시 같은 123 이 여러 곳에서 생성될 수 있음 |
2 | 전역 고유성 부재 | 시스템 간 통합 추적이 어려움 |
3 | Hotspot 문제 | 순차 증가 키는 특정 샤드·파티션에 쓰기 집중 |
4 | 백업·복원 충돌 | 복제/마이그레이션 시 중복 키 충돌 가능 |
5 | 비즈니스 로직 의존 | 많은 코드·캐시가 aid 를 전제하면 변경 난이도 급증 |
6 | 테스트 ID 재사용 | 로컬·CI 환경에서 aid 중복으로 테스트 충돌 |
두 가지 대응 전략
A — 식별자 암·복호화(E2EE)
금융권 등 일부 영역에서는 TLS에 더해서 HTTP 등 통신 페이로드를 한 번 더 암호화 하기도 한다. 구현 예시는 다음과 같다.
1. 클라이언트는 서버의 공개키(Public Key)를 보유
2. 클라이언트가 AES 세션 키 생성
3. 세션 키를 서버 공개키로 암호화 → 전송
4. 서버는 개인키로 복호화하여 세션 키 획득
5. 이후 AES‑256 으로 Payload 암·복호화
문제는 복잡도다. 키 교환, 키 보관, JS 메모리 상 키 노출, 디버깅 난이도… aid 가 가진 문제를 근본적으로 해결하지 못한 채 난이도만 올릴 수 있다.
B — 예측 불가능한 전역 고유 식별자 사용
- UUID v7, ULID, KSUID, NanoId, Snowflake, Sonyflake …
대체 식별자는 시간 정보 + 무작위 비트 를 섞어 정렬 성능 과 예측 불가능성 을 동시에 확보한다. - 주요 DB 인덱스(B‑tree 등)에서 무작위성으로 인한 성능 저하를 줄이려면 상위 비트에 타임스탬프를 배치 하는 설계가 일반적이다.
- 의존성도 낮다. 서버·클라이언트 어느 한쪽에서 생성해도 충돌 확률이 무시할 만하다.
- 일반적으로 uuid의 생성 조차도 DB 부하의 여지가 있고, 서버측에서 생성하는 경우에, SELECT 쿼리 날리는 비용조차 아껴서 트랜잭션 내에서 더 자유롭게 처리할 수 있어 서버에서 처리한다.
개인적으로는 B 가 ‘보안·운영·개발 생산성’ 세 마리 토끼를 모두 잡는 현실적 해법이라 본다.
JWT·URI 파라미터에 memberId
를 넣어도 될까?
최근 사이드 프로젝트에서 “JWT 안에 memberId
(aid) 를 그대로 넣어도 되나?” 라는 대화를 나누는 것이 봤다.
바로 거기에서 이 포스팅이 출발했다. 내 결론은 아래와 같다.
- aid 그대로라면 → URI·JWT·로그 그 어디든 노출을 최소화해야 한다. 앞서 본 모든 리스크가 적용된다.
- UUID v7 등 opaque ID 라면 → 평문 노출만으로는 특별한 보안 위협이 없다. -> 아직 이게 위험하다고 생각한다면 A의 방법으로 가서 종단간 암호화 말고는 방법이 없다. 어떤 정보도 기준에 따라 민감정보일 수 있고, TLS로 이미 암호화 통신을 하여 패킷을 감청해도 복호화할 수 없다고 보는 것인데, 평문 노출이 문제가 된다면 모든 평문을 없애야 하는 것이다.
맺으며
“DB Key를 URI에 노출해도 될까?” 라는 질문의 핵심은 식별자의 예측 가능성 이다. 만약 여러분이 아직 aid 를 URI·파라미터·토큰에 노출하고 있다면, 다음 스프린트에서 전역 고유 식별자 로의 전환을 검토해 보자. 종단간 암호화 E2EE 같은 복잡한 로직을 적용하기 전에 말이다.
다른 사람들의 의견
GetMapping시 URI에 PK 식별자가 노출되는 문제: 대체키를 사용한 해결 [키설계] UUID와 increment PK는 언제 사용해야할까?