보안 아티클
·7 IDOR인가실전

인가(Authorization) 취약점 실전 — URL의 숫자 하나가 전체 고객 데이터를 연다

IDOR는 스캐너가 자동으로 잡기 어려운 대표적인 취약점입니다. 실제 패턴과 코드 레벨에서 막는 법.


URL의 /orders/1042/orders/1043으로 바꿨을 뿐인데 다른 사람의 주문 내역이 보인다면, 그건 우연이 아니라 인가(Authorization) 취약점입니다. 업계에서는 IDOR(Insecure Direct Object Reference)라고 부르며, 자동 스캐너가 잡기 가장 어려운 취약점 중 하나입니다.

인증과 인가는 다르다

로그인이 되는지(인증, Authentication)와, 로그인한 사람이 그 데이터를 볼 자격이 있는지(인가, Authorization)는 완전히 다른 문제입니다. 대부분의 서비스는 로그인은 꼼꼼히 만들지만, "이 사용자가 이 리소스의 주인이 맞는가"는 깜빡하기 쉽습니다.

실제로 이렇게 뚫린다

GET /api/orders/1042    → 내 주문 (정상)
GET /api/orders/1043    → 다른 사람 주문이 그대로 보임 (취약)

서버 코드가 대략 이렇게 생겼을 때 발생합니다.

// 취약한 코드: id로 조회만 하고, 소유자 확인이 없음
app.get('/api/orders/:id', async (req, res) => {
  const order = await db.order.findUnique({ where: { id: req.params.id } });
  res.json(order);
});

로그인 여부만 확인하고, "이 주문이 로그인한 사용자의 것인가"는 확인하지 않습니다. 숫자 ID를 순서대로 바꿔보는 것만으로 전체 고객의 주문을 훑을 수 있습니다.

왜 스캐너가 놓치기 쉬운가

일반적인 자동 스캐너는 "이 API가 200을 반환하는가", "SQL 인젝션이 되는가" 같은 패턴은 잘 잡습니다. 하지만 IDOR는 응답이 정상적으로 200과 정상 데이터를 반환하기 때문에, 그 데이터가 "누구 것인지" 비즈니스 맥락을 이해해야 문제를 알 수 있습니다. 이건 단순 패턴 매칭보다 한 단계 더 깊은 분석이 필요합니다.

고치는 법

// 수정: 조회 조건에 소유자(userId)를 반드시 포함
app.get('/api/orders/:id', async (req, res) => {
  const order = await db.order.findFirst({
    where: { id: req.params.id, userId: req.user.id },
  });
  if (!order) return res.status(404).end();
  res.json(order);
});

원칙은 하나입니다. "조회 조건에 항상 현재 로그인한 사용자를 포함시킨다." ID만으로 조회하는 쿼리가 있다면 전부 의심하세요.

테스트하는 법

가장 확실한 방법은 계정 두 개를 만들어, A 계정으로 로그인한 채 B 계정의 리소스 ID를 직접 요청해 보는 겁니다. 응답이 200과 정상 데이터로 오면 문제가 있는 겁니다. 404나 403이 나와야 정상입니다.


IDOR는 화려한 해킹 기술이 필요 없습니다. 브라우저 주소창에서 숫자 하나 바꾸는 것만으로 발견되고 악용됩니다. 그래서 더 위험합니다.

WhiteHat Code의 딥스캔은 코드의 비즈니스 맥락을 함께 분석해 이런 인가 누락 패턴을 찾아냅니다. 무료 스캔으로 지금 확인해 보세요.

AI 공격에 맞서, AI로 준비하세요.

스캔

보안 아티클