인증(Authentication) vs 인가(Authorization) - 보안의 두 기둥 (feat. JWT)
1. 프롤로그 - "로그인했는데 왜 안 돼요?"
주니어 개발자 시절, 난생처음 사내 어드민 페이지를 만들 때였습니다. "부장님, 로그인 기능 완벽하게 구현했습니다! 이제 누구나 들어와서 매출 데이터를 실시간으로 볼 수 있어요."
저는 칭찬을 기대하며 뿌듯하게 말했지만, 부장님의 표정은 사색이 되었습니다. "야! 인턴이 들어와서 임원 연봉 테이블 보면 어떡하려고 그래? 권한 체크 안 했어?"
저는 당황해서 되물었습니다. "아이디랑 비번 맞아서 들어갔으면(로그인), 다 볼 수 있는 거 아닌가요? 그게 권한 체크 아닌가요?"
그날 저는 보안의 가장 기초적이고 중요한 개념, 인증(Authentication)과 인가(Authorization)의 차이를 뼈저리게, 그리고 아주 혹독하게 배웠습니다. 이 두 단어는 영어 철자도 비슷해서(Auth...) 개발자 십중팔구가 헷갈립니다. 오늘 이 둘을 확실하게 구분해 드리겠습니다.
2. 처음엔 뭐가 이해가 안 갔나
가장 헷갈렸던 건 "로그인 자체가 권한 부여 아닌가?"라는 생각이었습니다. 집 열쇠를 열고 들어가면(로그인), 냉장고를 열든 TV를 보든(권한) 내 맘대로 할 수 있잖아요? 그래서 웹사이트도 로그인만 하면 만사형통이라고 생각했습니다.
하지만 웹 애플리케이션은 집이 아니라 "호텔"이나 "거대 기업"에 가깝습니다. 로비(로그인)를 통과했다고 해서, CEO 집무실이나 전산실(특정 리소스)에 마음대로 들어갈 수 있는 건 아니니까요.
또한, HTTP 상태 코드인 401 Unauthorized와 403 Forbidden도 저를 미치게 만들었습니다. "권한 없음"이라면서 왜 401을 주고, "금지됨"이라면서 왜 403을 주는 걸까요?
3. 어떤 포인트에서 이해가 됐나
이 개념을 평생 잊지 않게 해준 비유는 "공항 보안 검색"과 "호텔 카드키"였습니다.
비유 1 - 공항 (Airport)
- 인증 (Authentication) = 여권 검사
- 질문: "당신 누구세요? (Who are you?)"
- 행동: 여권 사진과 실물을 대조합니다. 신원이 확인되면 공항 안으로 들어갈 수 있습니다.
- 실패 시:
401 Unauthorized(넌 누군지 모르겠으니 나가라).
- 인가 (Authorization) = 탑승권 검사
- 질문: "당신, 이 비행기에 탈 자격이 있나요? (Can you do this?)"
- 행동: 탑승권을 확인합니다. 당신이 누군지는 알지만, 이코노미 승객이 퍼스트 클래스 라운지에 들어갈 순 없습니다.
- 실패 시:
403 Forbidden(누군진 알겠는데, 넌 여기 못 들어와).
비유 2 - 호텔 (Hotel)
- 인증: 프론트 데스크에서 체크인하고 '카드키(Token)'를 발급받는 과정.
- 인가: 엘리베이터에 카드키를 찍었을 때, '내 방 층'만 눌리는 것. (다른 층이나 펜트하우스는 못 감).
이 비유를 대입하니 모든 게 명쾌해졌습니다. "아, 로그인은 카드키를 받는 과정(인증)이고, 관리자 페이지 접근은 그 키로 VIP 룸을 여는 과정(인가)이구나!"
4. 깊게 파고들기 - 기술적 구현 (JWT와 OAuth)
(1) 인증(Authentication)의 핵심: JWT (JSON Web Token)
현대 웹에서 가장 많이 쓰이는 '디지털 여권'이 바로 JWT입니다.
JWT는 .을 구분자로 세 부분으로 나뉩니다: Header.Payload.Signature
- Header: "나 JWT야. HS256 알고리즘 썼어." (메타데이터)
- Payload: "내 유저 ID는 'user123'이고, 권한은 'admin'이야." (실제 데이터)
- Signature: "이거 위조 아님. 내 비밀키로 서명함." (위변조 방지)
서버는 로그인 성공 시 이 JWT를 발급해 주고, 클라이언트는 요청할 때마다 이 토큰을 헤더에 실어 보냅니다. 서버는 Signature만 확인하면 DB를 뒤지지 않고도 "아, 얘 'user123' 맞네"라고 인증할 수 있습니다.
(2) 인가(Authorization)의 핵심: RBAC vs ABAC
인증된 사용자에게 권한을 어떻게 줄까요?
- RBAC (Role-Based Access Control): 가장 대중적입니다. "역할(Role)"에 따라 권한을 줍니다.
- Admin: 모든 권한.
- Manager: 읽기/쓰기.
- User: 읽기 전용.
- 구현이 쉽지만, "User 중에서 내 글만 수정 가능" 같은 세밀한 제어는 어렵습니다.
- ABAC (Attribute-Based Access Control): 더 유연하고 복잡합니다. "속성(Attribute)"을 봅니다.
- "부서가 HR이고(User Attribute), 접속 시간이 업무 시간(9-18)이며(Env Attribute), 접근하려는 파일이 '연봉' 태그가 있으면(Resource Attribute) 허용."
- AWS IAM Policy가 대표적인 ABAC 스타일입니다.
5. 실제로의 실수들 (Security Pitfalls)
실수 1 - 프론트엔드에서만 막기 (Client-Side Security)
가장 흔한 실수입니다. 리액트(React)에서 isAdmin이 false면 "관리자 버튼"을 숨겼다고 해서 안전하다고 착각합니다.
하지만 해커는 프론트엔드 UI를 보지 않습니다. curl이나 Postman으로 백엔드 API를 직접 찌릅니다.
반드시 백엔드 API에서 권한 체크를 한 번 더 해야 합니다.
실수 2 - 수평적 권한 상승 (IDOR: Insecure Direct Object References)
로그인은 했지만, 남의 데이터를 훔쳐보는 경우입니다.
GET /orders/123(내 주문) -> OKGET /orders/124(남의 주문) -> 이걸 막아야 함!
단순히 "로그인한 유저인가?"(인증)만 체크하고, "이 124번 주문의 주인이 너인가?"(인가)를 체크 안 하면 털립니다. 페이스북 초기에도 있었던 아주 유명하고 위험한 취약점입니다.
// 나쁜 예: 인증만 체크함
app.get('/orders/:id', ensureLoggedIn, (req, res) => {
const order = db.findOrder(req.params.id);
res.json(order); // 124번 주문을 123번 유저에게 그냥 보여줌!
});
// 좋은 예: 인가(소유권)도 체크함
app.get('/orders/:id', ensureLoggedIn, (req, res) => {
const order = db.findOrder(req.params.id);
if (order.userId !== req.user.id) { // 소유권 확인
return res.status(403).send("남의 주문입니다.");
}
res.json(order);
});
6. MSA(마이크로서비스) 환경에서의 전략
서비스가 커져서 MSA로 전환하면 인증/인가는 더 복잡해집니다.
- API Gateway 패턴: 유저가 들어오는 입구(Gateway)에서 인증을 한 번에 처리합니다.
- 게이트웨이: "여권(Token) 확인됐어. 넌 'user123'이야." -> 내부 서비스로 전달.
- 개별 서비스: 게이트웨이가 넘겨준 유저 정보를 믿고 인가만 처리합니다.
- 주문 서비스: "어, 'user123'이네? 자기 주문 조회는 가능하지." (인가 수행)
이렇게 하면 모든 마이크로서비스마다 로그인 로직을 중복해서 짤 필요가 없어집니다.
7. 자주 묻는 질문 (FAQ)
Q1. OAuth 2.0은 인증인가요, 인가인가요?
엄밀히 따지면 인가(Authorization) 프레임워크입니다. "내 구글 계정의 프로필 정보에 접근할 수 있는 권한을 이 앱(Client)에게 위임/인가한다"는 개념입니다. 하지만 실제로는 "구글 아이디로 로그인" 같은 소셜 인증 수단으로 더 많이 쓰입니다. (이를 위해 OpenID Connect라는 레이어가 추가되었습니다).
Q2. 로그아웃은 어떻게 구현하나요?
JWT는 발급되면 서버가 뺏을 수 없습니다(Stateless). 그래서 "진짜 로그아웃"은 어렵습니다.
- 방법 1: 토큰 만료 시간(Expiration Time)을 짧게(15분) 잡습니다.
- 방법 2: Redis 같은 DB에 "로그아웃된 토큰 목록(Blacklist)"을 저장해서, 요청 올 때마다 확인합니다. (이러면 Stateless의 장점이 좀 사라지긴 합니다).
8. 요약 및 마무리
보안 사고의 절반은 이 두 개념을 헷갈려서 발생합니다.
- Authentication (인증): "Who are you?" (너 누구야?) -> 여권/카드키 확인 -> 실패 시 401.
- Authorization (인가): "Can you do this?" (너 이거 할 자격 있어?) -> 탑승권/접근권한 확인 -> 실패 시 403.
집 현관문을 열었다고(인증) 안방 금고까지 열 수 있는 건 아닙니다(인가). 개발하실 때 항상 "여권(인증) 챙기셨나요? 그럼 이제 탑승권(인가) 확인하겠습니다"라는 공항 직원의 멘트를 떠올리시길 바랍니다.