이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 권한 우회 사고에서 패치보다 먼저 산정해야 하는 영향 범위는 무엇인가?
- BOLA/IDOR 사고는 어떤 요청과 로그로 재현하고 차단해야 하는가?
- 임시 완화책과 영구 수정, 재발 방지 테스트를 어떻게 분리하는가?
개요
권한 우회 취약점 대응은 누가 볼 수 없던 무엇을 보거나 바꾸었는지를 복원하는 일이다.
대표적인 형태는 URL, query parameter, request body, header 안의 object id를 바꿔 다른 사용자의 resource에 접근하는 BOLA/IDOR이다.
이 사고는 인증 성공 로그만 보면 정상 요청처럼 보인다.
따라서 대응은 endpoint 차단보다 먼저 resource ownership, action, 조회 조건, 응답 데이터 범위를 확인해야 한다.
공격 시나리오
- 공격자는 자신의 주문 상세 API를 호출한 뒤
orderId만 다른 값으로 바꾼다. - Controller는 로그인 여부만 확인하고 Service나 Repository에서 소유자 조건을 확인하지 않는다.
- 목록 API에는 권한 조건이 있지만 상세, export, batch, 관리자 대리 조회에는 조건이 빠져 있다.
- 공격자는 낮은 빈도로 여러 id를 조회해 rate limit과 오류 경보를 피한다.
- 취약점이 공개되면 이미 열람된 데이터와 수정된 데이터가 섞여 영향 범위 산정이 어려워진다.
취약한 요청/응답 예시
GET /api/orders/9001 HTTP/1.1
Authorization: Bearer member-1004-token
Accept: application/jsonHTTP/1.1 200 OK
Content-Type: application/json
{
"orderId": 9001,
"ownerId": 2002,
"receiverName": "Kim",
"address": "Seoul ...",
"phone": "010-1234-5678"
}로그인한 사용자와 resource owner가 다른데도 응답이 200이면 권한 우회 사고로 본다.
주제별 핵심 판단
- 인증 성공은 권한 허용의 증거가 아니다.
- 영향 범위는 endpoint 기준이 아니라 resource type, action, actor, 기간 기준으로 산정한다.
- 수정 API 사고는 데이터 원복 가능성까지 별도로 봐야 한다.
- 읽기 API 사고도 개인정보, 결제 정보, 영업 비밀이 포함되면 심각도가 높다.
- 임시 차단은 endpoint disable, feature flag, read-only 전환, gateway rule 중 업무 피해가 작은 방법을 고른다.
- 영구 수정은 Controller annotation만이 아니라 Repository 조건과 policy test까지 포함한다.
개선된 요청/응답 예시
GET /api/orders/9001 HTTP/1.1
Authorization: Bearer member-1004-token
Accept: application/jsonHTTP/1.1 403 Forbidden
Content-Type: application/json
X-Request-Id: req-9c2e41aa
{
"error": "forbidden",
"message": "요청한 작업을 수행할 수 없습니다."
}level=WARN event=authz.decision actor=member:1004 action=order.read resource=order:9001 owner=member:2002 result=denied reason=owner_mismatch traceId=req-9c2e41aa거부 로그에는 판단 이유를 남기되 응답에는 다른 사용자의 존재 여부를 자세히 설명하지 않는다.
방어 설계
- 모든 object id 기반 API는 actor, resource, action 기준의 권한 결정을 가진다.
- Service 계층에서 소유자, 조직, 역할, 위임 권한을 함께 검증한다.
- Repository 조회에는 가능한 한 owner 조건을 포함해 잘못된 resource가 올라오지 않게 한다.
- 관리자, 고객지원, batch, 내부 API는 별도 예외 정책과 감사 로그를 가진다.
- 임시 완화책은 배포 전까지 공격 경로를 막되, 증거 조회에 필요한 로그와 DB는 보존한다.
- 사고 기간 동안 접근된 resource 목록을 뽑을 수 있도록 access log와 audit log를 연결한다.
- 응답 필드 중 민감 정보는 권한 단계별로 분리해 과다 노출을 줄인다.
- 재발 방지에는 비소유자 테스트와 권한 matrix 리뷰가 포함되어야 한다.
Spring/HTTP 예시
@Service
class OrderService {
public OrderDetail getOrder(long orderId, MemberPrincipal actor) {
var order = orderRepository.findById(orderId)
.orElseThrow(NotFoundException::new);
if (!orderPolicy.canRead(actor, order)) {
audit.warn("authz.decision", Map.of(
"actor", actor.id(),
"action", "order.read",
"resource", "order:" + orderId,
"owner", order.ownerId(),
"result", "denied",
"reason", "owner_mismatch"
));
throw new AccessDeniedException("forbidden");
}
return OrderDetail.from(order);
}
}@Query("select o from Order o where o.id = :orderId and o.ownerId = :memberId")
Optional<Order> findOwnedOrder(long orderId, long memberId);정책 검증과 Repository 조건은 서로 보완 관계다.
둘 중 하나만 두면 예외 경로에서 다시 빠질 수 있다.
운영 로그와 감사 지점
level=INFO event=incident.scope.estimated type=authorization_bypass endpoint=/api/orders/{id} affectedResources=184 affectedActors=7 from=2026-06-30T09:10:00Z to=2026-07-01T02:40:00Z traceId=ir-20260701-003
level=WARN event=authz.bypass.mitigated action=feature_flag_disabled feature=order-detail-export reason=incident_response traceId=ir-20260701-003- 허용된 민감 조회도 actor, resource, action, result를 남긴다.
- 403 거부만 보지 말고 200 응답 중 actor와 owner가 다른 요청을 역추적한다.
- Export, download, search, admin impersonation 이벤트를 일반 read와 분리한다.
- 영향 범위 산정 쿼리의 기준 시각, 데이터 소스, 누락 가능성을 보고서에 남긴다.
- 임시 차단 해제 시점과 영구 수정 배포 시점을 별도 이벤트로 남긴다.
실전 판단 기준
- 권한 우회 사고는
고쳤다보다무엇이 노출되었는가가 먼저다. - 404와 403 선택은 정보 노출, UX, 감사 요구를 함께 보고 정한다.
- 관리자 기능은 권한 우회 예외가 아니라 더 강한 감사 대상이다.
- Batch와 내부 API는 사용자 요청이 아니어도 동일한 policy를 통과해야 한다.
- DB에서 owner 조건을 강제할 수 있으면 애플리케이션 실수를 줄일 수 있다.
- 피해 공지는 개인정보 포함 여부와 법적 요구를 확인한 뒤 결정한다.
- 재발 방지 완료 기준은 같은 유형의 비소유자 테스트가 CI에 들어가는 것이다.
테스트 포인트
- 정상 사용자와 비소유자 사용자로 상세, 목록, 검색, export API를 모두 호출한다.
- role이 같은 사용자끼리 resource id를 바꿔 접근할 수 없는지 확인한다.
- 관리자 대리 조회가 별도 권한과 감사 로그를 요구하는지 확인한다.
- Repository 메서드에 owner 또는 tenant 조건이 빠진 호출이 없는지 확인한다.
- 403 응답이 resource 존재 여부와 owner 정보를 노출하지 않는지 확인한다.
- 사고 기간 access log로 영향 resource 목록을 재현할 수 있는지 확인한다.
위험 신호!
- Controller에서
isAuthenticated()만 확인하고 object ownership을 확인하지 않는다. - 목록 API만 권한 조건이 있고 상세/export API에는 조건이 없다.
- 관리자 권한이면 모든 tenant 데이터를 이유 없이 조회할 수 있다.
- 권한 거부 로그에 actor, action, resource, reason이 없다.
- 사고 대응 중 endpoint를 끄면서 access log 보존을 놓친다.
- 재발 방지가
권한 체크 추가한 줄로만 끝난다.
확인 질문
확인 질문
- 권한 우회 사고에서 패치 전 먼저 확인할 것은 무엇인가?
- 어떤 actor가 어떤 resource에 어떤 action으로 접근했는지와 민감 데이터가 포함되었는지다.
- 인증 성공 로그만으로 부족한 이유는 무엇인가?
- 로그인한 사용자가 특정 resource에 접근할 권한이 있는지는 별도 판단이기 때문이다.
- 좋은 재발 방지 테스트는 무엇을 포함하는가?
- 정상 소유자, 비소유자, 관리자, 고객지원, batch 경로를 같은 policy 기준으로 검증한다.