로그인했다고 다 내 것은 아니다: 인증과 인가 입문

아프로·2026년 10월 29일

로그인했다고 다 내 것은 아니다 표지

이 글은 인프런 강의 「백엔드 개발자를 위한 서버 보안 실무」 섹션 3을 입문자용으로 풀어 쓴 글이에요.

숫자 하나만 바꿨을 뿐인데

앨리스가 쇼핑몰에 로그인해서 내 주문 상세를 엽니다. 주소창 끝에 주문 번호 1001이 보여요. 호기심에 숫자를 하나 올려 1002로 바꿔 봅니다.

화면에 다른 사람의 이름, 주소, 전화번호가 뜹니다. 특별한 해킹 도구도 필요 없었어요. 브라우저 주소창이면 충분했습니다. 반복문 하나만 돌리면 전체 주문을 차례로 가져갈 수도 있겠죠.

이 API는 "로그인한 사용자만 부를 수 있게" 잘 막혀 있었어요. 문제는 로그인했는지만 확인하고, 이 주문이 요청한 사람의 것인지는 묻지 않았다는 점입니다.

인증과 인가는 다른 질문이다

인증은 누구세요, 인가는 이거 당신 거예요라는 질문

  • 인증(Authentication): "누구세요?" 비밀번호, 세션, 토큰, MFA가 여기에 속해요.
  • 인가(Authorization): "이거 당신 거예요? 이 기능을 쓸 수 있는 역할인가요?"

회사 출입증이 있다고 사장실이나 다른 팀 금고까지 열 수 있는 건 아니죠. 출입증 확인이 인증, 방마다 다시 확인하는 게 인가예요. 실무에서는 인증보다 인가 쪽 실수가 훨씬 자주 터집니다.

IDOR와 BOLA: 주인을 묻지 않는 API

IDOR 정상, 공격, 방어 흐름

앞의 사례처럼 객체의 주인을 확인하지 않는 결함을 IDOR, API 맥락에서는 BOLA라고 부릅니다. OWASP API Security Top 10에서 1번을 차지하는, 가장 흔한 API 취약점이에요.

원리 수준의 방어는 한 줄입니다.

조회 조건에 "id"만이 아니라 "id + 로그인한 사용자" 를 함께 넣는다.

주문 번호를 추측하기 어려운 값으로 바꾸는 건 도움은 되지만 검사가 아니에요. 번호는 결국 어딘가에서 새어 나갑니다. 인가는 두 층으로 생각하면 정리가 쉬워요. 객체 수준(이 데이터의 주인인가)과 기능 수준(이 기능을 쓸 역할인가). 둘 중 하나라도 빠지면 그 틈으로 권한 상승이 일어납니다.

비밀번호는 '느린 해시'로

인증의 출발점인 비밀번호 저장도 짚고 갈게요. 오래된 서비스에서 흔히 보는 모습은 빠른 해시 함수 하나로, 솔트 없이 저장한 비밀번호예요. 평문이 아니니 안전해 보이죠.

빠른 해시와 느린 해시 비교

하지만 이 테이블이 유출되면 이야기가 달라집니다. 솔트가 없으니 같은 비밀번호는 같은 해시가 되고, 추측 한 번에 여러 계정이 한꺼번에 풀려요. 빠른 해시라 대량 대입도 쉽습니다. 그래서 비밀번호는 계정마다 다른 솔트 + 일부러 느리게 만든 전용 함수(Argon2id, bcrypt 등)로 저장해요.

세션과 JWT, 그리고 "끊을 수 있는가"

세션과 JWT 비교

로그인 상태를 유지하는 두 방식의 차이는 "새어 나갔을 때 끊을 수 있는가" 에서 갈립니다. 세션은 서버가 기억하니 지우면 끝이에요. JWT는 서버가 기억하지 않고 서명만 확인하니, 만료가 30일이라면 30일 동안 막을 방법이 마땅치 않습니다.

어느 방식이든 원칙은 같아요. 토큰은 페이지의 스크립트가 읽을 수 없는 곳에 두고, 만료는 짧게, 필요하면 서버가 폐기할 수 있는 장치를 둡니다.

로그인 화면을 두드리는 공격들

  • 무차별 대입: 한 계정에 비밀번호를 바꿔 가며 시도합니다.
  • 크리덴셜 스터핑: 다른 사이트에서 유출된 아이디·비밀번호 쌍을, 수많은 IP에서 계정마다 딱 한 번씩 넣어 봐요. 하나하나는 평범한 실패처럼 보여서 IP 기준 제한에 잘 걸리지 않습니다.

그래서 계정 단위로 실패가 쌓이면 점점 기다리게 하고, 유출된 비밀번호인지 확인하고, 수상하면 추가 인증을 요구해요.

그런데 추가 인증(MFA)을 켜도 안심은 금물이에요. 진짜와 똑같이 생긴 피싱 사이트가 사용자의 OTP를 몇 초 만에 진짜 서비스로 중계하면, 로그인 세션이 그대로 넘어갑니다. 사이트 주소에 묶인 인증인 패스키가 주목받는 이유예요.

정리

  • 인증은 "누구세요", 인가는 "이거 당신 거예요". 사고는 인가에서 더 많이 난다.
  • 조회 조건에는 항상 "주인"을 함께. 번호를 숨기는 건 검사가 아니다.
  • 비밀번호는 느린 해시로, 토큰은 스크립트가 못 읽는 곳에 짧게.

강의에서는 이걸 직접 공격하고 고쳐 봅니다

섹션 3에서는 번호만 바꿔 남의 주문을 여는 IDOR, 유출된 해시 복원, 토큰 탈취, 크리덴셜 스터핑을 실습 서버에서 재현하고 하나씩 막습니다.

  • Spring Security와 Express로 객체·기능 수준 인가를 구현하는 코드
  • 옛 해시를 사용자 불편 없이 새 알고리즘으로 옮기는 방법
  • OAuth·OIDC를 안전하게 붙이는 법, 패스키와 세션 탈취 대응
  • 누가·무엇을·어떤 결과로를 표로 만든 인가 테스트로 CI에서 회귀 막기

강의 보러 가기: https://inf.run/XvwyH


profile
고인물 개발자(OG)

0개의 댓글