
접종 기록 조회 API는 보통 로그인한 사용자만 호출할 수 있도록 만듭니다. 그런데 로그인 여부만 확인하고 "이 기록의 주인이 지금 요청한 사람인가"를 확인하지 않으면, 로그인한 누구나 다른 사람의 기록을 볼 수 있게 됩니다. 이것이 객체 참조 취약점(IDOR, Insecure Direct Object Reference)입니다.
독감 시즌과 가을 여행 시즌에는 접종 기록 조회 기능이 자주 쓰이고, 기록 ID는 URL이나 응답에 그대로 드러나기 쉽습니다. 이 글에서는 테스트 계정 두 개로 서로의 기록 ID를 교차 요청해 보고, 응답에 화면에 필요 없는 개인정보 필드가 섞였는지 확인하는 방법을 정리합니다. 모든 점검은 허가받은 테스트 환경과 본인 소유 테스트 계정으로만 진행한다는 전제입니다.
로그인 뒤에서 IDOR이 더 자주 놓이는 이유
개발 단계에서는 "인증 미들웨어가 붙어 있으니 안전하다"고 판단하기 쉽습니다. 인증 미들웨어는 토큰이 유효한지만 확인하고, 그 토큰의 사용자가 요청한 객체의 소유자인지는 각 핸들러가 직접 확인해야 합니다. 이 확인이 빠진 엔드포인트가 하나라도 있으면 로그인 체계 전체가 의미를 잃습니다.
OWASP API Security Top 10은 객체 수준 권한 부여 실패(BOLA)를 가장 앞에 두고 있고, 웹 애플리케이션 전반에서는 접근 통제 실패(Broken Access Control)로 분류됩니다. 목록 조회보다 단건 조회, 수정, 삭제 같은 요청에서 빠지기 쉬우니 점검 대상을 메서드별로 미리 나눠 두는 것이 좋습니다.
테스트 계정 두 개와 기록 ID 준비하기
계정 A와 계정 B를 같은 권한 등급으로 만들고, 각각 접종 기록을 하나 이상 등록해 둡니다. 두 계정의 소속이나 가족 구성원 설정이 다르면 원인 분석이 쉬워집니다. 테스트 계정에는 실제 개인정보 대신 더미 값을 넣는 것이 원칙입니다.
준비가 끝나면 각 계정으로 기록 목록을 조회해 기록 ID를 메모합니다. 이때 ID가 순번인지, 추측하기 어려운 UUID인지도 함께 적어 둡니다. 다만 UUID라고 해서 안전한 것은 아닙니다. 기록 ID가 목록 응답, 로그, 공유 링크 등으로 노출되면 소유자 검증이 없는 한 같은 문제가 그대로 남습니다.
교차 요청으로 소유자 검증 확인하기
핵심은 A의 세션으로 B의 기록 ID를 요청하고, 반대로 B의 세션으로 A의 기록 ID를 요청해 보는 것입니다. 한쪽 방향만 확인하면 조회는 막혀 있는데 수정은 열려 있는 경우를 놓칠 수 있습니다. 요청 전에 두 계정의 토큰이 서로 섞이지 않았는지 쿠키와 헤더 값을 한 번 확인하세요.

응답 판정은 상태 코드와 본문을 함께 봅니다. 403이나 404가 나오고 본문에 기록 내용이 없으면 정상입니다. 200이 나오면서 다른 사람의 접종 항목과 접종일이 보이면 소유자 검증 누락으로 판단하고, 요청과 응답을 증적으로 남깁니다.
조회와 수정, 삭제는 따로 본다
GET 요청이 막혀 있어도 PUT이나 DELETE는 열려 있을 수 있습니다. 반대로 상태 변경 요청은 빈 본문이나 성공 메시지로 응답하는 경우가 있어서, 응답만 보고 판단하면 안 됩니다. 테스트 계정의 화면이나 조회 API로 실제 값이 바뀌었는지 다시 확인해야 합니다.
삭제 요청은 테스트 데이터에만 보내고, 점검 후에는 더미 데이터를 다시 만들어 두는 것이 좋습니다. 운영 데이터가 섞인 환경에서는 쓰기 요청 점검 자체를 범위에서 빼고, 조회 중심으로만 진행하는 방법도 있습니다.
응답 필드에서 불필요한 개인정보 찾기
소유자 검증이 맞게 되어 있어도 응답이 과하면 문제가 남습니다. 화면에는 접종 명칭과 접종일만 보이는데, API 응답에는 생년월일, 연락처, 내부 사용자 번호, 기관 내부 코드까지 함께 들어 있는 경우가 있습니다. 프론트엔드가 쓰지 않는 값이라도 브라우저 개발자 도구나 프록시 로그에는 그대로 남습니다.
예시로 가정한 상황입니다. 기록 조회 점검에서 화면에는 접종명과 접종일만 나왔지만, 응답 JSON에는 내부 사용자 번호와 전화번호가 함께 들어 있었습니다. 개발팀은 "프론트에서 안 쓰니 괜찮다"고 판단했는데, 점검팀은 외부로 나가는 응답 자체가 문제라고 보고 필드 목록을 명세와 대조하도록 권고했습니다.
확인 방법은 간단합니다. 응답 JSON의 키를 모두 뽑아 화면 설계서나 API 명세와 한 줄씩 맞춰 봅니다. 명세에 없는 키나 화면에서 쓰지 않는 키가 있으면 응답 DTO에서 제외하거나, 별도 권한 등급에서만 노출되도록 나눕니다.

다음 표는 점검할 때 쓰는 기준입니다. 각 항목을 판정할 때 상태 코드만 보지 말고 본문과 실제 데이터 변화를 함께 확인하세요.
| 점검 항목 | 확인 방법 | 정상 기준 | 위험 신호 |
|---|---|---|---|
| 타 계정 기록 조회 | 계정 A 세션으로 B의 기록 ID 호출 | 403 또는 404, 본문 없음 | 200과 타인 접종 내역 노출 |
| 타 계정 기록 수정·삭제 | 계정 A 세션으로 B의 기록 ID에 PUT·DELETE | 거부되고 저장 값 그대로 | 응답은 성공인데 값이 바뀜 |
| 기록 ID 형식 | 순번 여부와 추측 가능성 확인 | 추측이 가능해도 소유자 검증으로 차단 | 검증 없이 ID 형식에만 의존 |
| 응답 필드 | 응답 키와 화면 명세 대조 | 화면에서 쓰는 항목만 포함 | 전화번호·내부 번호 등 불필요 필드 |
| 오류 메시지 | 기록 존재 여부가 드러나는지 확인 | 동일한 일반 메시지 반환 | "해당 사용자의 기록입니다" 같은 힌트 |
기록 남기고 재점검하는 순서
발견 사항은 재현 가능한 요청 한 개로 줄여서 기록합니다. 요청 메서드와 경로, 사용한 계정 식별자, 응답 상태와 필드 목록을 적으면 개발자가 바로 재현할 수 있습니다. 심각도는 노출되는 정보의 종류와 쓰기 권한 여부를 기준으로 나눠 판단합니다.
ISMS-P 관점에서는 접근 통제와 개인정보 처리에 관한 내부 기준에 맞춰 조치 이력을 남기는 것이 중요합니다. 어떤 항목으로 분류할지, 어느 기준을 적용할지는 내부 보안 담당 부서와 인증 기관의 해석을 확인하세요. 아래 순서대로 진행하면 재점검까지 한 흐름으로 끝낼 수 있습니다.
- 테스트 계정 A·B를 만들고 각각 접종 기록을 하나씩 등록합니다.
- 기록 목록 API로 두 계정의 기록 ID를 메모하고 ID 형식을 적어 둡니다.
- A 세션으로 B의 기록을 조회·수정·삭제 요청하고, B도 같은 방식으로 반복합니다.
- 응답 키 목록을 화면 명세와 대조해 불필요한 필드를 표시합니다.
- 수정 후 같은 요청을 다시 보내고, 테스트 DB에서 값이 바뀌지 않았는지 확인합니다.
자주 묻는 질문
질문: 기록 ID가 UUID면 IDOR 점검을 생략해도 되나요?
아닙니다. UUID는 추측을 어렵게 할 뿐이고, 목록 응답이나 로그, 공유 링크 등으로 ID가 새어 나가면 소유자 검증이 없는 한 그대로 악용됩니다. 추측 가능성과 권한 검증은 별개의 항목이므로 따로 점검해야 합니다.
질문: 교차 요청 점검을 운영 서버에서 해도 되나요?
원칙적으로는 테스트 환경에서 진행하는 것이 맞습니다. 운영에서 불가피하게 확인해야 한다면 사전에 범위, 시간, 담당자를 합의하고, 실제 고객 기록 ID는 건드리지 않도록 테스트 계정 데이터로만 한정합니다. 허용 범위는 내부 보안 담당자와 먼저 확인하세요.
질문: 응답 필드를 줄이면 프론트 기능이 깨지지 않을까요?
명세 대조를 먼저 하면 대부분 걱정을 줄일 수 있습니다. 실제로 쓰는 필드 목록을 프론트엔드 코드와 함께 확인하고, 새 DTO를 만든 뒤 스테이징에서 화면 전체 흐름을 한 번 돌려 보세요. 꼭 필요한 경우에만 권한 등급에 따라 필드를 열어 두면 됩니다.
정리
- 로그인 여부가 아니라 객체 소유자 검증을 엔드포인트마다 확인합니다.
- 두 테스트 계정으로 조회·수정·삭제를 교차 요청하고, 상태 코드와 실제 값 변화를 함께 봅니다.
- 응답 필드는 화면 명세와 대조해 화면에 쓰지 않는 개인정보를 걷어냅니다.
오늘 할 일: 접종 기록 조회 API 하나를 골라 계정 A·B로 기록 ID를 교차 요청하고, 결과를 표 기준에 맞춰 적어 보세요.
댓글