~/VibeHandbook
$39

챕터 18 · 04

인증 vs 인가: 누구나 호출할 수 있는 엔드포인트

이 두 단어는 비슷하게 들리고, 진짜 침해는 그 차이 속에 산다.

  • **인증(Authentication)**은 *당신은 누구인가?*다 — 로그인, 신원 증명.
  • **인가(Authorization)**는 *당신은 무엇을 할 수 있는가?*다 — 로그인한 사용자가 데이터에 동작을 수행해도 되는지.

AI는 인증은 꽤 잘한다; 라이브러리가 대부분을 처리한다. 인가는 AI가 일상적으로 실패하는 곳인데, 인가는 당신 앱의 규칙에 특화되어 있고 AI는 그 규칙을 모르기 때문이다. 교과서적인 재앙:

// VULNERABLE: 로그인했는지는 점검하지만, 당신이 누구인지는 점검하지 않는다
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  res.json(db.getAllUsers()); // 로그인한 사용자라면 누구나 이걸 칠 수 있다
});

그 엔드포인트는 "보호"되어 있다 — 로그인해야만 한다. 하지만 30초 전에 가입한 사람을 포함해 어떤 로그인 사용자든 그것을 호출해 모든 사용자의 데이터를 내려받을 수 있다. 인증은 있고, 인가는 없다. 해결책은 동작 그 자체에 권한을 점검하는 것이다:

// SAFE: 이 사용자가 실제로 admin인지 확인한다
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  if (!req.user.isAdmin) return res.status(403).send("Forbidden");
  res.json(db.getAllUsers());
});

같은 함정이 도처에서 축소판으로 나타난다: 주문 #1234를 반환하면서 그 주문이 요청한 사용자의 것인지 점검하지 않는 엔드포인트. 누구나 URL의 숫자를 바꿔서 남의 주문을 읽을 수 있다. 규칙은 화려하지 않고 절대적이다: 데이터를 다루는 모든 엔드포인트에서 인가를 점검하고, 클라이언트가 보낸 ID를 신뢰하지 말라. 숨겨진 URL이 숨겨져 있다는 이유로 안전하다고 가정하지 말라 — "아무도 이게 존재하는 줄 모른다"는 보안 통제가 아니다.

오프라인으로 보고 싶으세요?

PDF + EPUB + 다운로드형 프롬프트 라이브러리 + 버전 업데이트를 받으세요.

$ PDF 받기 — $39