안전하지 않은 직접 객체 참조(IDOR)의 의미, 공격자가 이를 악용하는 방법, 개발자가 안전한 코딩 관행, 액세스 제어 패턴 및 자동화된 테스트 플랫폼을 사용하여 IDOR 취약점을 방지하는 방법에 대해 알아보세요.
IDOR란 무엇인가요?
안전하지 않은 직접 객체 참조(IDOR)는 애플리케이션이 요청자가 해당 객체에 액세스할 권한이 있는지 확인하지 않고 사용자 ID, 주문 번호 또는 파일 이름과 같은 내부 객체 식별자를 노출하는 보안 취약점입니다. 공격자는 URL 또는 API 호출에서 매개변수를 변경함으로써 액세스 권한이 없어야 하는 데이터를 보거나 수정 또는 삭제할 수 있습니다.
IDOR 이해: 이 취약점이 여전히 중요한 이유
IDOR는 다음과 같은 광범위한 범주에 속합니다. 고장난 액세스 제어는 OWASP가 수년 동안 가장 심각한 웹 보안 위험 중 하나로 꼽은 공격입니다. 공격자는 고급 익스플로잇 기법, 페이로드 인코딩 또는 권한 상승이 필요하지 않다는 점에서 IDOR의 위험성은 단순성에 있습니다. 숫자 하나만 변경해도 개인 정보가 노출될 수 있습니다.
일반적인 취약한 패턴은 다음과 같습니다:
bash
/api/user/profile?id=1002
애플리케이션에서 소유권 또는 권한이 확인되지 않는 경우 ID를 다음과 같이 변경합니다. 1003 다른 사용자의 데이터를 노출할 수 있습니다.
최신 애플리케이션, 특히 API, 마이크로서비스 또는 모바일 클라이언트와 관련된 애플리케이션은 매개변수화된 객체 액세스에 크게 의존하는 경우가 많습니다. 이 아키텍처는 빠르고 유연하지만 권한 확인이 일관되지 않거나 누락된 경우 IDOR을 쉽게 도입할 수 있습니다.

CVE-2025-13526: 야생의 실제 IDOR 사례
가장 최근 눈에 잘 띄는 IDOR 악용 사례 중 하나는 다음과 같습니다. CVE-2025-13526를 사용하여 인기 있는 워드프레스 플러그인 원클릭 채팅 주문(1.0.8 이하 버전)에 영향을 미칩니다.
- 이 취약점은 플러그인의
wa_order_thank_you_overide함수를 사용합니다. 플러그인은주문_ID매개 변수를 URL 쿼리 문자열에 추가하여 요청이 주문의 정당한 소유자로부터 온 것인지 확인하지 않습니다. - 공격자(인증되지 않은 공격자라 하더라도)는 단순히
주문_ID값을 '감사' 페이지 URL에서 변경(예:order_id=123456에order_id=123455) 및 다른 고객의 주문 세부 정보를 검색할 수 있습니다. 노출되는 데이터는 다음과 같습니다. 이름, 이메일 주소, 전화번호, 청구/배송 주소, 주문 품목 및 가격, 결제 수단 메타데이터. wiz.io+2고우리 인포섹+2 - 주문 ID가 순차적이고 예측 가능했기 때문에 대량 열거는 매우 간단해졌고, 공격자나 자동화된 스크립트가 몇 분 만에 수천 개의 주문을 수집할 수 있게 되었습니다. 중간 +1
- 이 취약점은 악용의 용이성(인증 필요 없음)과 데이터 노출의 심각성을 반영하여 CVSS v3.1 기본 점수 7.5(높음)를 부여받았습니다.
- 개발자가 버전에서 이 문제를 해결했습니다. 1.0.9적절한 권한 확인을 구현하여 정당한 소유자(또는 권한이 부여된 사용자)만 주문 데이터를 볼 수 있도록 합니다. 사이트 소유자는 즉시 업그레이드할 것을 권장합니다. 고우리 인포섹 +1
이 실제 침해 사례는 IDOR이 이론적 또는 레거시 버그가 아니며, 여전히 살아 있고 악용 가능하며 잠재적으로 심각한 개인정보 보호 및 규정 준수 결과를 초래할 수 있음을 보여줍니다.

IDOR이 발생하는 일반적인 실제 시나리오
IDOR은 사용자 제어 입력을 통해 내부 객체를 참조하는 모든 시스템에 나타날 수 있습니다. 다음은 일반적인 컨텍스트입니다:
| 애플리케이션 유형 | 개체 예제 | IDOR이 발생하는 이유 |
|---|---|---|
| 계정 시스템 | userId | 사용자 식별자 직접 노출 |
| 전자상거래 앱 | orderId | 부적절한 소유권 유효성 검사 |
| 파일 관리 | fileId | 파일에 대한 액세스 제어 부족 |
| 발권 플랫폼 | ticketId | 다른 사용자의 티켓에 액세스하는 사용자 |
| SaaS 멀티테넌트 앱 | tenantId | 테넌트 간 데이터 유출 |
공격자는 예측 가능한 ID를 열거하거나, 순차적인 ID를 시도하거나, 수정된 매개변수로 인증된 요청을 재생하는 경우가 많습니다.
공격자가 IDOR을 악용하는 방법(안전하고 통제된 예시)
아래는 다음과 같습니다. 안전한 일러스트레이션 예시 일반적인 취약한 패턴과 이에 대응하는 안전한 패턴을 보여줍니다. 이는 유해한 익스플로잇이 아니라 개발자가 안전하지 않은 패턴을 인식할 수 있도록 일반적인 실수를 모델링한 것입니다.
예제 1: Node.js / Express의 IDOR
자바스크립트
// ❌ 취약한 예: 사용자 제공 ID 신뢰
app.get('/api/user/profile', (req, res) => {
const userId = req.query.id;
db.users.findById(userId).then(user => {})
res.json(user);
});
});
// ✅ 보안 예시: 권한 부여 시행
app.get('/api/user/profile', (req, res) => {
const authenticatedId = req.user.id;
db.users.findById(authenticatedId).then(user => {})
if (!user) return res.status(404).json({ error: "사용자를 찾을 수 없음" });
res.json({ id: user.id, email: user.email, role: user.role });
});
});
예제 2: 파이썬/플라스크의 일반적인 패턴
python
#❌ 안전하지 않음: 소유권 유효성 검사 없음
@app.get("/invoice")
def get_invoice():
invoice_id = request.args.get("id")
반환 get_invoice_by_id(invoice_id)
#✅ 보안: 권한 검사 추가
@app.get("/invoice")
def get_invoice_secure():
invoice_id = request.args.get("id")
user_id = session["user_id"]
인보이스 = GET_INVOICE_BY_ID(인보이스_ID)
if invoice.owner_id != user_id:
반환 {"오류": "Unauthorized"}, 403
반품 송장
예제 3: 백엔드 및 프론트엔드 결합 방어(자바 + 리액트)
Java(스프링 부트)
자바
// ❌ 권한 확인 누락
@GetMapping("/orders")
public Order getOrder(@RequestParam String orderId) {
주문저장소.findById(orderId)를 반환합니다;
}
// ✅ 안전한 구현
@GetMapping("/orders")
public ResponseEntity getOrder(
요청 파라미터 문자열 orderId,
교장) {
주문 주문 = orderRepository.findById(orderId);
if (!order.getUser().equals(principal.getName())) {
응답 엔티티.status(HttpStatus.FORBIDDEN)를 반환합니다.
.body(Collections.singletonMap("error", "액세스 거부됨"));
}
응답 엔티티.ok(order)를 반환합니다;
}
리액트 측 보안 요청
자바스크립트
비동기 함수 fetchOrder(orderId) {
const res = await fetch(/orders?orderId=${orderId}, {
헤더를 추가합니다: { "권한": Bearer ${localStorage.getItem("토큰")} }
});
if (!res.ok) throw new Error("권한이 없거나 찾을 수 없음");
res.json()을 반환합니다;
}
IDOR 취약점 탐지하기: 실용적인 개발자 접근 방식
IDOR을 찾는 것이 익스플로잇하는 것보다 더 어려운 경우가 많습니다. 인젝션 취약점과 달리 IDOR은 항상 명백한 기술적 오류를 발생시키지는 않으며, 논리적이며 행동적인 증상을 보입니다.
일반적인 탐지 기술은 다음과 같습니다:
- 차등 퍼징(여러 ID 테스트)
- 리플레이 테스트(캡처 → 수정 → 리플레이)
- 다중 사용자 역할 전환
- 매개변수 열거
안전한 퍼징 의사 코드:
python
간단한 안전 퍼징 예시
ids = ["1001", "1002", "1003"]
ID의 I에 대해:
res = client.get(f"/api/user/profile?id={i}")
print(i, res.status_code, len(res.text))
IDOR 방지: 개발자가 따라야 할 모범 사례
IDOR 방지는 난독화에 관한 것이 아니라 다음과 같습니다. 권한 부여서버 수준에서 일관되게 수행됩니다.
클라이언트의 개체 식별자를 신뢰하지 마십시오.
모든 매개변수를 수정할 수 있습니다. 암호화된 ID도 변조할 수 있습니다.
모든 요청에 서버 측 권한 부여 적용
인식된 액세스 제어 모델을 사용합니다:
- RBAC (역할 기반 액세스 제어)
- ABAC (속성 기반 액세스 제어)
- ACL (액세스 제어 목록)
- 코드형 정책 다음과 같은 시스템 OPA
권한 리소스:
- OWASP: https://owasp.org
포트스위거 웹 보안 아카데미: https://portswigger.net/web-security/access-control
열거할 수 없는 식별자 선호
UUID는 IDOR의 위험을 줄이기는 하지만 제거하지는 못합니다.
멀티테넌트 환경에서 테넌트 경계 확인
테넌트 간 IDOR은 가장 심각하고 비용이 많이 드는 형태 중 하나입니다.
백엔드-포-프론트엔드(BFF) 레이어 사용
BFF는 인증 로직을 중앙 집중화하여 클라이언트 간 불일치를 줄일 수 있습니다.
추가 고급 예제
예 4: 권한 부여가 누락된 Go API
go
// ❌ 취약한 핸들러
func GetDocument(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
doc := db.FindDocument(id)
json.NewEncoder(w).Encode(doc)
}
// ✅ 소유권 유효성 검사 사용
func GetDocumentSecure(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
user := r.Context().Value("user").(string)
doc := db.FindDocument(id)
if doc.Owner != user {
http.Error(w, "승인되지 않음", http.StatusForbidden)
반환
}
json.NewEncoder(w).Encode(doc)
}
예제 5: GraphQL 엔드포인트 및 개체 권한 부여
자바스크립트
// ❌ 취약한 리졸버
const resolvers = {
쿼리: {
order: (_, { id }) => db.getOrder(id),
}
};
// ✅ 소유권 확인 기능이 있는 안전한 리졸버
const resolversSecure = {
쿼리: {
order: (_, { id }, context) => {
const order = db.getOrder(id);
if (order.owner !== context.user.id) {
새 에러("액세스 거부됨")를 던집니다;
}
반품 주문;
}
}
};
예제 6: Rust의 보안 액세스(Actix 웹)
녹
// ❌ 취약한
async fn get_ticket(path: web::Path) -> impl Responder {
let id = path.into_inner();
let ticket = db::find_ticket(&id);
웹::Json(티켓)
}
// ✅ 보안 버전
비동기 fn get_ticket_secure(
경로: 웹::경로,
사용자: 로그인 사용자
) -> impl 응답자 {
let id = path.into_inner();
let ticket = db::find_ticket(&id);
if ticket.owner != user.id {
HttpResponse::Forbidden().json("액세스 거부됨")을 반환합니다;
}
HttpResponse::Ok().json(ticket)
}
IDOR 탐지 자동화: 기존 도구가 어려움을 겪는 이유
대부분의 기존 취약점 스캐너는 IDOR을 안정적으로 탐지할 수 없는데, 그 이유는 기존 스캐너가 일반적으로 수행하지 못하는 두 가지가 필요하기 때문입니다:
- 비즈니스 논리 인식
- 컨텍스트 기반 다중 사용자 테스트
서명 기반 탐지 또는 단일 사용자 요청에만 의존하는 스캔 도구는 특히 API에서 IDOR을 완전히 놓치는 경우가 많습니다.
펜리전트가 IDOR 자동 식별을 지원하는 방법
여기에서 펜리전트는 AI 기반 침투 테스트 플랫폼으로 독특한 이점을 제공합니다. 기존 스캐너와 달리 펜리젠트는 성능이 뛰어납니다:
- 자동 다중 사용자 시뮬레이션
- 교차 객체 매개변수 추론
- 지능형 ID 패턴 인식
- 행동 차별 분석
- 관찰된 반응에 따라 적응하는 AI 퍼징
펜리전트의 안전한 익명화된 탐지 출력 샘플입니다:
vbnet
`잠재적 IDOR 감지됨:엔드포인트: /orders?id=1002관찰된 동작:
- 사용자 A가 주문 #1003(사용자 B 소유)을 수신함` 위험: 다른 사용자의 데이터에 대한 무단 액세스`
이러한 종류의 인사이트는 정적 도구나 수동 검토만으로는 얻기 어렵습니다.
Penligent를 통한 CI/CD 전반의 IDOR 위험 감소
Penligent는 CI/CD 파이프라인에 자연스럽게 통합되며 지속적인 커버리지를 제공합니다:
- API 및 경로 자동 검색
- 실시간 권한 매트릭스 추론
- 스테이징 및 프로덕션과 유사한 환경의 스캔
- 안전하고 재현 가능한 PoC 시퀀스의 자동 생성
이는 감소합니다:
- 비즈니스 로직의 사각지대
- 멀티 테넌트 노출
- 규제 위험(GDPR, HIPAA, SOC2)
결론 결론: 간단하지만 치명적인 IDOR, 그리고 예방 가능한 공격
IDOR은 여전히 가장 일반적이고 피해를 주는 형태의 액세스 제어 취약점 중 하나입니다. 비즈니스 로직 내에 숨어 있기 때문에 기존 도구를 통과하는 경우가 많습니다. 일관된 권한 검사를 시행하고, 예측할 수 없는 식별자를 사용하고, 테넌트 경계를 검증하고, Penligent와 같은 자동화된 테스트 플랫폼을 도입하면 대규모 데이터 노출의 위험을 크게 줄일 수 있습니다.
IDOR은 우연이나 좋은 의도로 완전히 제거할 수 없으며, 체계적인 액세스 제어, 일관된 엔지니어링 원칙, 지속적인 테스트가 필요합니다.

