
인증(Authentication) vs 인가(Authorization): 보안의 두 기둥 (feat. JWT)
로그인과 권한 체크를 혼동해서 발생했던 실제 보안 사고 사례와, 이를 '공항 보안 검색'과 '호텔 카드키'에 비유하여 명쾌하게 정리했습니다. JWT 구조부터 OAuth 2.0, 그리고 MSA 환경에서의 인증 전략까지.

로그인과 권한 체크를 혼동해서 발생했던 실제 보안 사고 사례와, 이를 '공항 보안 검색'과 '호텔 카드키'에 비유하여 명쾌하게 정리했습니다. JWT 구조부터 OAuth 2.0, 그리고 MSA 환경에서의 인증 전략까지.
프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

Debug에선 잘 되는데 Release에서만 죽나요? 범인은 '난독화'입니다. R8의 원리, Mapping 파일 분석, 그리고 Reflection을 사용하는 라이브러리를 지켜내는 방법(@Keep)을 정리해봤습니다.

비트코인은 블록체인의 일부입니다. 이중 지불 문제(Double Spending), 작업 증명(PoW)과 지분 증명(PoS)의 차이, 스마트 컨트랙트, 그리고 Web 3.0이 가져올 미래까지. 개발자 관점에서 본 블록체인의 모든 것.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

주니어 개발자 시절, 난생처음 사내 어드민 페이지를 만들 때였습니다. "부장님, 로그인 기능 완벽하게 구현했습니다! 이제 누구나 들어와서 매출 데이터를 실시간으로 볼 수 있어요."
저는 칭찬을 기대하며 뿌듯하게 말했지만, 부장님의 표정은 사색이 되었습니다. "야! 인턴이 들어와서 임원 연봉 테이블 보면 어떡하려고 그래? 권한 체크 안 했어?"
저는 당황해서 되물었습니다. "아이디랑 비번 맞아서 들어갔으면(로그인), 다 볼 수 있는 거 아닌가요? 그게 권한 체크 아닌가요?"
그날 저는 보안의 가장 기초적이고 중요한 개념, 인증(Authentication)과 인가(Authorization)의 차이를 뼈저리게, 그리고 아주 혹독하게 배웠습니다. 이 두 단어는 영어 철자도 비슷해서(Auth...) 개발자 십중팔구가 헷갈립니다. 오늘 이 둘을 확실하게 구분해 드리겠습니다.
가장 헷갈렸던 건 "로그인 자체가 권한 부여 아닌가?"라는 생각이었습니다. 집 열쇠를 열고 들어가면(로그인), 냉장고를 열든 TV를 보든(권한) 내 맘대로 할 수 있잖아요? 그래서 웹사이트도 로그인만 하면 만사형통이라고 생각했습니다.
하지만 웹 애플리케이션은 집이 아니라 "호텔"이나 "거대 기업"에 가깝습니다. 로비(로그인)를 통과했다고 해서, CEO 집무실이나 전산실(특정 리소스)에 마음대로 들어갈 수 있는 건 아니니까요.
또한, HTTP 상태 코드인 401 Unauthorized와 403 Forbidden도 저를 미치게 만들었습니다. "권한 없음"이라면서 왜 401을 주고, "금지됨"이라면서 왜 403을 주는 걸까요?
이 개념을 평생 잊지 않게 해준 비유는 "공항 보안 검색"과 "호텔 카드키"였습니다.
401 Unauthorized (넌 누군지 모르겠으니 나가라).403 Forbidden (누군진 알겠는데, 넌 여기 못 들어와).이 비유를 대입하니 모든 게 명쾌해졌습니다. "아, 로그인은 카드키를 받는 과정(인증)이고, 관리자 페이지 접근은 그 키로 VIP 룸을 여는 과정(인가)이구나!"
현대 웹에서 가장 많이 쓰이는 '디지털 여권'이 바로 JWT입니다.
JWT는 .을 구분자로 세 부분으로 나뉩니다: Header.Payload.Signature
서버는 로그인 성공 시 이 JWT를 발급해 주고, 클라이언트는 요청할 때마다 이 토큰을 헤더에 실어 보냅니다. 서버는 Signature만 확인하면 DB를 뒤지지 않고도 "아, 얘 'user123' 맞네"라고 인증할 수 있습니다.
인증된 사용자에게 권한을 어떻게 줄까요?
가장 흔한 실수입니다. 리액트(React)에서 isAdmin이 false면 "관리자 버튼"을 숨겼다고 해서 안전하다고 착각합니다.
하지만 해커는 프론트엔드 UI를 보지 않습니다. curl이나 Postman으로 백엔드 API를 직접 찌릅니다.
반드시 백엔드 API에서 권한 체크를 한 번 더 해야 합니다.
로그인은 했지만, 남의 데이터를 훔쳐보는 경우입니다.
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);
});
서비스가 커져서 MSA로 전환하면 인증/인가는 더 복잡해집니다.
이렇게 하면 모든 마이크로서비스마다 로그인 로직을 중복해서 짤 필요가 없어집니다.
엄밀히 따지면 인가(Authorization) 프레임워크입니다. "내 구글 계정의 프로필 정보에 접근할 수 있는 권한을 이 앱(Client)에게 위임/인가한다"는 개념입니다. 하지만 실제로는 "구글 아이디로 로그인" 같은 소셜 인증 수단으로 더 많이 쓰입니다. (이를 위해 OpenID Connect라는 레이어가 추가되었습니다).
JWT는 발급되면 서버가 뺏을 수 없습니다(Stateless). 그래서 "진짜 로그아웃"은 어렵습니다.
보안 사고의 절반은 이 두 개념을 헷갈려서 발생합니다.
집 현관문을 열었다고(인증) 안방 금고까지 열 수 있는 건 아닙니다(인가). 개발하실 때 항상 "여권(인증) 챙기셨나요? 그럼 이제 탑승권(인가) 확인하겠습니다"라는 공항 직원의 멘트를 떠올리시길 바랍니다.