업데이트됨 1개월 전
인터넷의 모든 보호된 리소스는 같은 질문을 던집니다: 당신은 누구이며, 어떻게 당신이 진실을 말하고 있다는 걸 알 수 있나요?
Authorization 헤더는 그 질문에 대한 답입니다. HTTP 요청에 담긴 한 줄로, 서버가 요구하는 증명—비밀번호, 토큰, 암호화 서명—을 전달합니다. 올바르게 보내면 접근이 허용됩니다. 잘못되면 401 Unauthorized를 받고 다시 시도해야 합니다.
인증의 구조
모든 Authorization 헤더는 동일한 패턴을 따릅니다:
스킴(scheme)은 서버에게 어떻게 자신을 증명하는지 알려줍니다. 인증 정보는 증명 그 자체입니다. 서로 다른 스킴은 오래된 질문에 대한 서로 다른 답을 나타냅니다: 너무 많은 것을 드러내지 않고 내가 나임을 어떻게 증명할까요?
Basic 인증: 직접적인 방식
Basic 인증은 단순합니다. 사용자명과 비밀번호를 콜론으로 구분하고 Base64로 인코딩하여 전송합니다:
그 알 수 없는 문자열은 alice:secret123으로 디코딩됩니다. 누구든 디코딩할 수 있습니다:
이것이 Basic 인증의 핵심입니다: Base64는 변장이지 진짜 위장이 아닙니다. 인증 정보를 알 수 없어 보이게 만들지만, 누구든 쉽게 되돌릴 수 있습니다. 모든 요청마다 실제 비밀번호를 전송하는 셈입니다—단지 로그에서 조금 덜 노골적으로 보이도록 꾸민 것에 불과합니다.
끔찍하게 들리고, 어떤 면에서는 그렇습니다. 하지만 Basic 인증이 살아남은 이유는 하나입니다: HTTPS. 연결 전체가 암호화되어 있다면, 비밀번호가 Base64로만 인코딩되어 있어도 문제없습니다. 암호화가 실제 보안을 처리하고, Basic 인증은 형식만 처리합니다.
Basic 인증을 사용하면 좋은 경우:
- VPN 뒤의 내부 도구
- 개발 환경
- 직접 제어하는 API와 통신하는 간단한 스크립트
- 단순함이 대안의 복잡성보다 가치 있는 곳
절대로 일반 HTTP에서는 사용하지 마세요. 엽서에 비밀번호를 적어 보내는 것과 같습니다.
Bearer 토큰: 소지가 곧 증명
Bearer 토큰은 방식을 뒤집습니다. 모든 요청마다 당신이 누구인지 증명하는 대신, 한 번 증명하고 토큰을 받아 그 토큰을 인증 정보로 제시합니다:
"bearer"라는 이름은 정확합니다: 토큰을 소지한 사람이 접근 권한을 갖습니다. 물리적인 열쇠처럼—당신이 누구인지는 중요하지 않고, 손에 열쇠를 가지고 있는지만 중요합니다.
이것이 의미하는 바는 생각보다 큽니다. 누군가 Bearer 토큰을 훔치면, 토큰이 만료될 때까지 그 사람이 당신이 됩니다. "하지만 나는 진짜 앨리스입니다"라는 호소는 통하지 않습니다. 소지가 곧 증명입니다.
JWT: 스스로를 증명하는 토큰
오늘날 대부분의 Bearer 토큰은 JWT—JSON Web Token입니다. 영리한 방식입니다: 서버가 데이터베이스에서 조회해야 하는 무작위 문자열이 아니라, JWT는 자신의 유효성 증명을 자체적으로 담고 있습니다.
JWT는 점으로 구분된 세 부분으로 구성됩니다:
헤더: 토큰이 어떻게 서명되었는지 알려줍니다 (알고리즘, 유형) 페이로드: 클레임을 포함합니다—당신이 누구인지, 언제 만료되는지, 무엇을 할 수 있는지 서명: 헤더와 페이로드가 변조되지 않았다는 암호화 증명
서버는 비밀 키로 서명을 검증하여 JWT의 유효성을 확인할 수 있습니다. 데이터베이스 조회가 필요 없습니다. 이 덕분에 JWT는 확장성이 뛰어납니다—키를 가진 모든 서버가 모든 토큰을 검증할 수 있습니다.
토큰 생명주기
토큰에는 리듬이 있습니다:
- 한 번 인증: 로그인 엔드포인트에 실제 인증 정보 전송
- 토큰 수신: 액세스 토큰(단기)과 종종 리프레시 토큰(장기)을 받음
- 액세스 토큰 사용: Authorization 헤더에 포함
- 필요 시 갱신: 액세스 토큰이 만료되면 리프레시 토큰으로 새 토큰 획득
- 결국 재인증: 리프레시 토큰도 만료되면 처음부터 시작
단기 액세스 토큰(분~시간)은 하나가 도난당했을 때 피해를 제한합니다. 더 안전하게 보관되는 리프레시 토큰은 사용자가 비밀번호를 계속 재입력하지 않고도 로그인 상태를 유지할 수 있게 합니다.
Digest 인증: 전송 대신 해시
Digest 인증은 암호화 없이 Basic 인증의 취약점을 해결하려 했습니다. 비밀번호를 직접 전송하는 대신, 서버가 제공한 무작위 값("nonce")과 혼합된 비밀번호의 해시를 보냅니다:
서버도 같은 해시 계산을 수행합니다. 결과가 일치하면, 비밀번호를 알고 있다는 증거입니다.
영리하지만 복잡합니다. 현대 애플리케이션은 HTTPS를 통한 Bearer 토큰을 선호하여 Digest 인증을 대부분 버렸습니다. 암호화가 보안을 처리하고, 인증 방식이 신원을 처리합니다.
API 키: 단순화된 신뢰
많은 API는 복잡성을 완전히 건너뜁니다:
또는 사용자 정의 헤더에:
API 키는 복잡한 절차 없이 단순화된 Bearer 토큰입니다. OAuth 흐름도, 리프레시 토큰도, 만료도(보통) 없습니다. 키를 받고, 키를 사용하면, 키가 당신을 식별합니다.
이 단순함이 핵심입니다. 로그인할 사용자가 없는 서버 간 통신에서 API 키는 복잡성을 크게 줄여줍니다. 하지만 보안성은 낮습니다—유출된 API 키는 보통 만료 기한이 없어 피해가 무기한 지속될 수 있습니다.
OAuth 2.0: 서비스로서의 인가
OAuth는 헤더 형식이 아닙니다—토큰을 획득하기 위한 프레임워크입니다. 특정 질문에 답합니다: 내 비밀번호를 주지 않고 어떻게 서드파티 애플리케이션이 내 데이터에 접근하도록 할 수 있을까요?
인증 코드 흐름 (웹 앱에서 가장 일반적):
- 앱이 사용자를 인증 서버(Google, GitHub 등)로 리디렉션
- 사용자는 그곳에서 로그인하고 권한 부여
- 인증 서버가 코드와 함께 리디렉션
- 앱이 그 코드를 액세스 토큰으로 교환
- 앱이 Authorization 헤더에 해당 토큰 사용
사용자 비밀번호는 절대 애플리케이션에 닿지 않습니다. 특정하고 제한된 권한(스코프)을 가진 토큰을 받습니다. 사용자는 언제든지 접근을 철회할 수 있습니다.
결과는 여전히 Bearer 토큰입니다:
OAuth의 복잡성은 가치 있는 것을 제공합니다: 사용자 동의와 세부 권한을 갖춘 위임된 인가.
인증 실패 시
서버는 원하는 것을 알려줍니다. 401 Unauthorized 응답에는 예상 스킴을 명시하는 WWW-Authenticate 헤더가 포함됩니다:
이는 "Bearer 토큰이 필요한데, 유효하지 않은 것을 보냈습니다"라는 의미입니다. realm은 보호 대상을 설명하고, error는 구체적인 원인을 알려줍니다.
Basic 인증의 경우:
이를 보는 브라우저는 사용자명/비밀번호 입력 창을 띄웁니다. 다소 거슬리지만, 기능은 합니다.
보안 핵심 사항
HTTPS는 필수입니다. 모든 인증 방식은 암호화된 전송을 전제로 합니다. 없다면, 아무리 영리하게 형식을 갖춰도 인증 정보가 노출됩니다.
토큰은 만료되어야 합니다. 단기 액세스 토큰(15분~1시간)은 도난당한 토큰의 유효 기간을 제한합니다. 리프레시 토큰은 위험을 연장하지 않고 세션을 유지합니다.
저장 방식이 중요합니다. 브라우저에서:
- HttpOnly 쿠키는 JavaScript로 읽을 수 없습니다 (XSS 공격에 안전)
- localStorage는 페이지의 모든 스크립트가 읽을 수 있습니다 (XSS에 취약)
- 메모리가 가장 안전하지만 페이지를 새로고침하면 사라집니다
모바일 앱의 경우, 플랫폼의 보안 저장소를 사용하세요: iOS Keychain, Android Keystore.
URL에 인증 정보를 넣지 마세요. 로그, 브라우저 기록, Referer 헤더에 노출됩니다:
모든 것을 검증하세요. 형식이 올바른 JWT도 만료되었거나, 폐기되었거나, 잘못된 키로 서명되었을 수 있습니다. 모든 요청에서 서명, 만료 시간, 발급자, 대상 클레임을 확인하세요.
Authorization 헤더에 관한 자주 묻는 질문
인증(authentication)과 인가(authorization)의 차이는 무엇인가요?
인증은 당신이 누구인지 증명합니다. 인가는 당신이 무엇을 할 수 있는지 결정합니다. (혼란스럽게도 이름 붙여진) Authorization 헤더는 인증—신원 증명을 처리합니다. 그 신원으로 무엇을 할 수 있는지는 인증 정보를 검증한 후 서버가 별도로 결정합니다.
매번 사용자명과 비밀번호를 전송하는 대신 왜 Bearer 토큰을 사용하나요?
비밀번호는 가능한 한 적게 노출되어야 하는 중요한 비밀입니다. 비밀번호를 토큰으로 한 번 교환함으로써 네트워크를 통해 비밀번호가 오가는 빈도를 줄입니다. 토큰은 또한 스코프(제한된 권한), 만료(제한된 수명), 폐기(즉각적인 무효화)가 가능합니다—비밀번호로는 이 중 어떤 것도 잘 작동하지 않습니다.
JWT를 암호화할 수 있나요?
네, 하지만 보통은 암호화가 아닌 서명만 합니다. 서명은 토큰이 변조되지 않았음을 증명하지만, 페이로드는 단지 Base64 인코딩되어 있어 누구든 읽을 수 있습니다. JWT 페이로드에 비밀을 넣지 마세요. 암호화된 토큰이 필요하다면 JWE(JSON Web Encryption)를 살펴보세요, 하지만 대부분의 애플리케이션은 필요하지 않습니다.
Bearer 토큰으로 어떻게 로그아웃하나요?
상태 비저장(stateless) JWT의 경우, 만료 전에 토큰을 완전히 무효화할 수 없습니다—서버는 어떤 토큰이 존재하는지 추적하지 않기 때문입니다. 일반적인 해결책: 짧은 만료 시간 사용(자연스럽게 로그아웃됨), 폐기된 토큰의 차단 목록 유지(상태 추가), 또는 리프레시 토큰 순환 사용(리프레시 토큰 폐기로 새 액세스 토큰 발급 차단).
이 페이지가 도움이 되었나요?