인증 / 인가
- 인증(Authentication)
- 사용자의 신원을 확인하는 과정
- Ex) ID, PW를 이용한 로그인 / 생체 인식(지문, 얼굴) / OTP / 인증서 등
- 인가(Authorization)
- 인증 이후의 프로세스로 인증된 유저가 어떠한 자원에 접근할 수 있는 지 확인하는 프로세스
- Ex) 관리자 페이지 - 관리자 권한을 가진 유저만 접근 가능
웹 애플리케이션 인증의 특수성
- 일반적으로 서버 - 클라이언트 구조
- Http 프로토콜을 이용한 통신
→ 비연결성(Connectionless), 무상태(Stateless)의 특징을 가짐
비연결성(Connectionless)
- 서버와 클라이언트가 연결되어 있지 않음
- 서버와 클라이언트가 계속 연결되어 있다면, 서버의 비용이 기하급수적으로 증가
- 리소스의 절약을 위해 서버는 하나의 요청에 하나의 응답을 보내고 연결 종료
무상태(Stateless)
- 서버가 클라이언트의 이전 상태를 저장하지 않음
→ 따라서, 클라이언트가 직전에 무슨 요청을 보냈었는지 알 수 없음 - 기존의 상태를 저장하는 것은 서버의 비용과 부담을 증가시키기 때문에, 기존의 상태가 없다고 가정하는 프로토콜을 구현
- 이를 해결 하기 위해 클라이언트가 필요한 상태 정보를 매번 서버로 전송
→ 쿠키, 세션, 토큰을 활용
웹 애플리케이션의 인증 방식
- Http의 Connectionless와 Stateless라는 특성을 생각해보면, 사용자가 로그인을 통해 인증을 거쳐도 이후 요청에 대해서는 인증된 상태를 유지 불가
- 서버가 사용자가 로그인을 했다는 정보를 저장하지 않기 때문에, 최초 로그인 이후 매 요청마다 반복적으로 ID, PW를 입력하여 인증을 받고 페이지에 접근이 가능한지 인가를 받아야 함
- 하지만, 우리는 웹 어플리케이션을 이용할 때 로그인을 한 번 하면 그 이후에 반복해서 로그인 하지 않아도 회원이 접속 가능한 페이지에 접근이 가능
쿠키(Cookie)
- 클라이언트에 저장되는 작은 데이터 파일
- 사용자 로그인 시도
- 서버에서 DB에 있는 정보와 비교하여 확인
- 서버는 Cookie를 생성하여 클라이언트에게 전송
- 이후 브라우저는 서버에서 받은 쿠키를 저장하고 동일한 서버로 재요청할 때 쿠키를 함께 전송
→ 클라이언트가 요청할 때마다 서버는 로그인 정보가 담긴 쿠키를 받아 확인하고 요청에 대한 응답
쿠키의 장점
- 클라이언트 측에 저장이 되는 데이터이기 때문에 서버의 부담이 감소
쿠키의 단점
- 사용자의 주요 정보를 매번 요청에 담기 때문에 보안상 문제가 발생
- 클라이언트에서 쿠키 정보를 쉽게 변경, 삭제, 탈취 가능
- 단, 쿠키의 보안 속성을 통해 일부 위험 완화 가능
- HttpOnly : 쿠키가 자바스크립트에서 접근되지 않도록 하여 XSS 공격을 방지
- Secure : HTTPS에서만 쿠키를 전송하도록 설정하여 안전한 통신을 보장
- SameSite : 쿠키의 크로스사이트 요청을 제한해 CSRF 공격을 방지
- 단, 쿠키의 보안 속성을 통해 일부 위험 완화 가능
- 쿠키 사이즈가 커질수록 네트워크 부하가 발생
쿠키의 구성 요소
- Name(이름)
- 쿠키를 구별하는 데 사용되는 키 (식별자, Unique)
- Value(값)
- 쿠키의 값
- Domain(도메인)
- 쿠키가 저장된 도메인
- Path(경로)
- 쿠키가 사용되는 경로
- Expires(만료기한)
- 쿠키의 만료기한
세션(Session)
- 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용
- 서버에서 클라이언트별 유일한 세션 ID를 부여하고, 세션 정보를 서버에 저장
- Session ID : 사용자의 주요 정보가 아닌, 단지 사용자를 식별할 수 있는 값을 생성
→ 보안 강화
- Session ID : 사용자의 주요 정보가 아닌, 단지 사용자를 식별할 수 있는 값을 생성
- 사용자 로그인 시도
- 서버에서 DB에 있는 정보와 비교하여 확인
- 회원 정보에 대한 세션 생성
- Session ID를 클라이언트에게 쿠키로 전송
- 클라이언트에서 어떠한 요청을 보낼 때 세션 쿠키를 함께 요청
- 서버의 세션 저장소에는 클라이언트별 세션 쿠키 값이 저장되어 있어 요청으로 온 세션 ID를 기반으로 어떤 클라이언트인지 식별 가능
세션의 장점
- 사용자의 로그인 정보를 주고 받지 않기 때문에 민감한 정보의 노출 위험 감소
- 사용자마다 고유한 세션 ID가 발급되기 때문에 요청이 들어올 때마다 회원DB를 찾지 않음
- 인증된 사용자 정보를 서버 메모리나 세션 저장소에서 직접 조회하기 때문에 DB 부하 감소
세션의 단점
- 사용자를 식별할 수 있는 값인 세션 ID를 생성하고 서버에 저장하는 작업 발생
→ 서버 메모리나 데이터베이스, Redis 같은 저장소에 저장되기 때문에 서버의 리소스를 차지 - 서버 세션 저장소를 사용하므로 요청이 많아지면 서버의 부담이 증가
→ 많은 사용자가 동시에 접속하면 세션 저장소의 용량과 성능이 이슈- 이를 해결하기 위해 세션을 분산 서버에서 관리하거나 외부 캐시 저장소(예: Redis)를 사용하는 방식을 도입 가능
JWT(JSON Web Token)
- 인증에 필요한 정보들을 암호화시킨 토큰
- JWT 토큰(Access Token)을 HTTP 헤더에 담아 인증된 사용자임을 확인하는 데 사용
→ JWT 자체는 사용자 식별뿐만 아니라 권한 정보, 유효 기간 등의 인증 정보를 포함 - 클라이언트 측에 저장하여 서버의 부담 감소
JWT 구조
- Header
- 토큰 타입, 해쉬 알고리즘(HS256 or RSA 등)으로 구성
{
"alg": "HS256",
"typ": "JWT"
}
- Payload
- 클라이언트 고유 ID 값이나 유효 기간 등이 포함되는 영역
- claim이 포함되는 영역으로 3가지로 나뉨
→ 토큰에 담을 정보를 가짐
- 등록된 클레임(Registered Claim) : iss(발행자), exp(만료 시간), sub(주제), aud(대상자)와 같은 표준 클레임
- 공개 클레임(Public Claim) : username, role 등 개발자가 정의하는 클레임
- 비공개 클레임(Private Claim) : 특정 서비스나 사용자 간의 정보로, 개발자가 자유롭게 정의할 수 있는 클레임
{
"sub": "1234567890",
"username": "user1",
"admin": true
}
- Signature
- 인코딩된 Header와 Payload를 더한 뒤 secret key로 해싱해서 생성
→ 이 때, 해싱 방법은 Header에서 지정한 해쉬 알고리즘을 사용 - Header와 Payload는 단순히 인코딩된 값이어서 누구든 복호화가 가능하지만, Signature는 서버 측에서 관리하는 secret key가 있어야 복호화가 가능
- Signature 부분으로 토큰의 위변조 여부를 확인 가능
- 인코딩된 Header와 Payload를 더한 뒤 secret key로 해싱해서 생성
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
JWT의 동작 과정
- 클라이언트가 ID, PW 로그인 요청
- 서버에서 DB에 있는 정보와 비교하여 확인
- 로그인 성공 시 서버는 로그인 정보를 Payload에 담고, Secret Key를 사용해서 Access Token(JWT)을 발급
- 서버가 JWT를 클라이언트에 전달
→ 전달방법은 개발자가 결정
ex) 응답 Header에 Authorization: BEARER <JWT> 형태로 전달 - 클라이언트는 전달받은 토큰(JWT)을 저장 ( 쿠키, 로컬 스토리지, 세션 스토리지 등)
→ 쿠키에 저장하는 경우에는 HttpOnly와 Secure 속성을 설정해 보안 강화 가능 - 클라이언트가 서버에 요청할 때마다 토큰(JWT)을 요청 Header의 Authorization에 포함시켜 함께 전달
- 서버는 클라이언트가 전달한 토큰의 Signature를 secret Key로 복호화한 후, 위변조 여부 및 유효기간을 검증
- 검증에 성공하면 JWT에서 사용자 정보를 확인하고 요청에 응답
JWT 장점
- 동시 접속자가 많을 때 서버 부하 감소
- 클라이언트, 서버가 다른 도메인을 사용할 때 사용 가능
ex) 카카오 OAuth2 로그인 시 JWT 토큰 사용
→ Cross-Origin 상황에서도 사용할 수 있어, OAuth2 같은 외부 인증에서도 널리 사용 - 인증 정보를 서버에 별도로 저장하지 않음
→ 서버의 Stateless 특성 유지
JWT 단점
- 구현 복잡도 증가
- JWT에 담는 내용이 커질수록 네트워크 비용이 증가
→ JWT는 매 요청마다 전송되기 때문에 Payload가 크면 네트워크 부하가 증가 - 이미 생성된 JWT를 일부만 만료 불가
→ JWT는 발급된 이후에는 토큰 자체 수정이 불가하여 특정 사용자만 로그아웃 시키는 등의 세부적인 제어가 어려움 - Secret Key 유출 시 JWT 조작이 가능
- Payload 자체는 암호화되지 않기 때문에 사용자의 중요한 정보 저장 불가
→ Payload는 Base64 URL로 인코딩된 것이지 암호화된 것은 아니므로, 중요한 정보는 JWT에 직접 담으면 안 됨
보충 내용
- JWT의 갱신(Refresh Token)
- Access Token이 만료되었을 때 새로운 토큰을 발급받기 위해 Refresh Token을 함께 사용하는 전략
- Refresh Token은 주로 서버에 저장되며 이를 통해 Access Token을 갱신
- JWT의 만료 시간 설정
- JWT의 유효기간 설정 매우 중요
- 너무 길게 설정하면 보안 문제가 생기고, 너무 짧게 설정하면 사용자가 자주 재인증
참고 자료
쿠키, 세션, JWT에 대해 알아보자 : https://velog.io/@kimdy0915/%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90
'Language > Spring' 카테고리의 다른 글
[JWT] JwtUtil 사용 예제 및 설명 (0) | 2024.08.27 |
---|---|
[JWT] JWT 등장 배경, Access Token, Refresh Token (0) | 2024.08.27 |
[Spring Boot] 일정 주기마다 업데이트 프로젝트 적용해보기 (0) | 2024.08.19 |
[Spring Boot] 정해진 시간마다 동작하는 Scheduler (0) | 2024.08.19 |
[Spring Boot] Redis를 이용한 이메일 인증 리팩토링 (0) | 2024.08.13 |