해싱 (Hashing)
가장 많이 쓰이는 암호화 방식 중 하나. 해싱은 암호화만 가능하다. (복호화 불가능)
해싱은 해시 함수(Hash Function)을 사용하여 암호화를 진행한다. 해시 함수는 다음과 같은 특징을 갖고 있다.
- 항상 같은 길이의 문자열을 리턴한다.
- 서로 다른 문자열에 동일한 해시 함수를 사용하면 반드시 다른 결과값이 나온다.
- 동일한 문자열에 동일한 해시 함수를 사용하면 항상 같은 결과값이 나온다.
레인보우 테이블
레인보우 테이블은 해시 함수를 사용하여 만들어낼 수 있는 값들을 대량으로 저장한 테이블(표)이다.
해시 함수는 역함수를 가지지 않는다. 즉 복호화가 불가능하다. 그러므로, 차라리 가능한 모든 경우의 수를 다 써놓고 거기서 찾아내는 방법을 고안해낸 것이다. 그런데 공격자 입장에서 레인보우 테이블을 가지고 있다면 해당 데이터베이스에서 유저의 비밀번호를 알아낼 수도 있다.
솔티드 해시
기존에는 패스워드만 해시 함수에 통과시켰지만 이제 가입 시간 혹은 난수를 비밀번호와 함께 해시값에 포함시킨다. 이때 추가적으로 포함된 값을 솔트(salt)라고 한다. 서로 다른 계정이 같은 비밀번호를 사용하더라도 솔트가 추가되면 완전히 다른 해시값을 반환하기 때문에 평문을 유추하기 힘들어진다.
해싱의 목적
왜 복호화가 불가능한 암호화 방식을 사용하는 것일까? 해싱의 목적은 데이터 그 자체를 사용하는 것이 아니라, 동일한 값의 데이터를 사용하는지 체크하는 것이기 때문이다.
사이트 관리자는 사용자의 비밀번호를 알 필요가 없다. 오히려 문제가 될 수도 있다. 그래서 보통 비밀번호를 데이터베이스에 저장할 때는 복호화가 불가능하도록 해싱하여 저장한다.
그럼 서버 측에서 비밀번호를 모르는데 어떻게 로그인 요청 처리를 하는 것일까? 해싱한 값끼리 비교해서 일치하는지만 체크하면 된다.
이처럼 해싱은 민감한 데이터를 다뤄야 하는 상황에서 데이터 유출의 위험성은 줄이면서 데이터의 유효성을 검증하기 위해 사용하는 단방향 암호화 방식이다.
토큰 인증 방식
토큰을 사용하면 사용자의 인증 정보를 서버가 아닌 클라이언트 측에 저장할 수 있다.
토큰 인증 방식의 등장 배경
세션 기반 인증은 유저의 상태를 서버에서 관리한다. 개발자들은 서버의 부담을 줄이기 위해 유저의 상태를 클라이언트에 저장하는 방법을 찾게 되었고 토큰 기반 인증 방식이 등장하게 됐다. 토큰을 이용하면 유저의 인증 상태를 클라이언트에 저장할 수 있어서, 세션 인증 방식에 비해 서버의 부하나 메모리 부족 문제를 줄일 수 있다.
토큰 인증 과정
1. 사용자가 인증 정보를 담은 로그인 요청을 서버에 보낸다.
2. 서버는 데이터베이스에 저장된 사용자의 인증 정보를 확인한다.
3. 인증에 성공했다면 해당 사용자의 인증 및 권한 정보를 서버의 비밀 키와 함께 토큰으로 암호화한다.
4. 생성된 토큰을 클라이언트에 전달한다.
- HTTP 상에서 인증 토큰을 보내기 위해서는 Authorization 헤더나 쿠키를 사용한다.
5. 클라이언트는 전달받은 토큰을 저장한다.
- Local Storage, Session Storage, Cookie 등에 저장
6. 클라이언트가 서버로 리소스를 요청할 때 토큰을 함께 전달한다.
- 요청을 보낼 때도 마찬가지로 Authorization 헤더나 쿠키를 사용한다.
7. 서버는 전달받은 토큰을 검증한다(서버의 비밀 키를 이용해서). 이를 통해 토큰이 위조되었는지 혹은 토큰이 만료되었는지의 여부를 확인할 수 있다.
8. 유효한 토큰이라면 클라이언트의 요청에 대한 응답 데이터를 전송한다.
토큰 인증 방식의 장점
- 무상태성: 서버가 유저의 인증 상태를 관리하지 않는다. 서버는 비밀 키를 통해 클라이언트에서 보내온 토큰의 유효성만 검증하면 되기 때문에 무상태적인 아키텍처를 구축할 수 있다.
- 확장성: 다수의 서버가 공통된 세션 데이터를 가질 필요가 없다. 그러므로 서버 확장이 쉽다.
- 어디서나 토큰 생성 가능: 토큰의 생성과 검증이 굳이 하나의 서버에서 이뤄지지 않아도 된다. 토큰 생성만을 담당하는 서버를 구축할 수도 있다. 이를 활용하면 여러 서비스 간 공통된 인증 서버를 구현할 수도 있다.
- 권한 부여에 용이: 토큰은 인증 상태, 접근 권한 등 다양한 정보를 담을 수 있기 때문에 사용자 권한 부여에 용이하다. 이를 활용해 admin 권한 부여나 정보 접근에 대한 범위 설정이 가능하다.
JWT (JSON Web Token)
토큰 기반 인증 구현 시 대표적으로 사용되는 기술. JWT는 JSON 객체에 정보를 담고 이를 토큰으로 암호화하여 전송할 수 있는 기술이다. 클라이언트가 서버에 요청을 보낼 때, 인증 정보를 암호화된 JWT 토큰으로 제공하고 서버는 이 토큰을 검증하여 인증 정보를 확인할 수 있다.
JWT의 구성
JWT는 다음 그림과 같이 .
으로 나눠진 세 부분으로 구성되어 있다.
1. Header
Header에는 마치 HTTP의 헤더처럼 해당 토큰 자체를 설명하는 데이터가 담겨 있다. 토큰의 종류, 시그니처를 만들 때 사용할 알고리즘을 JSON 형태로 작성한다.
{
"alg": "HS256",
"typ": "JWT"
}
이 JSON 객체를 base64 방식으로 인코딩하면 JWT의 첫 번째 부분인 Header가 완성된다.
- 참고로 base64 방식은.. 원한다면 얼마든지 디코딩할 수 있는 인코딩 방식이다. 따라서 비밀번호와 같이 노출되면 안 되는 민감한 정보는 담지 말아야 한다.
2. Payload
Payload는 HTTP의 페이로드와 마찬가지로, 전달하려는 내용물을 담은 부분이다. 정보 접근 권한, 개인 정보, 토큰의 발급 시간, 만료 시간 등의 정보를 JSON 형태로 담는다.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
이 JSON 객체를 base64 방식으로 인코딩하면 JWT의 두 번째 부분인 Payload가 완성된다.
3. Signature
Signature는 토큰의 무결성을 확인할 수 있는 부분이다. Header와 Payload가 완성되었다면, Signature는 이를 서버의 비밀키(암호화에 추가할 salt)와 Header에서 지정한 알고리즘을 사용하여 해싱한다. 따라서 누군가 권한을 속이기 위해 토큰의 Payload를 변조하는 시도를 하더라도, 토큰을 발급할 때 사용한 Secret을 정확하게 알고 있지 못한다면 유효한 Signature를 만들어낼 수 없다.
토큰 인증 방식의 한계
무상태성
서버가 인증 상태를 관리하지 않기 때문에, 토큰이 탈취되어도 해당 토큰을 강제로 만료시킬 수 없다.
유효 기간
토큰이 탈취되는 상황에 대비해 토큰의 유효 기간을 짧게 설정하면, 사용자는 로그인을 자주해야하기 때문에 좋지 않은 UX.
그렇다고 유효 기간을 길게 설정하면 토큰이 탈취될 경우 더욱 치명적.
액세스 토큰과 리프레시 토큰
위 문제를 해결하기 위해 액세스 토큰 & 리프레시 토큰의 개념이 탄생했다.
Access Token
액세스 토큰은 서버에 접근하기 위한 토큰이다. 보안을 위해 짧은 유효 기간을 둔다.
Refresh Token
액세스 토큰이 만료되었을 경우 새로운 액세스 토큰을 발급받기 위해 사용하는 토큰. 상대적으로 긴 유효 기간을 둔다.
하지만 이 또한 탈취될 가능성이 없지는 않다.
보안을 위해 서버에서 리프레시 토큰을 관리하는 방법도 쓰이게 됐는데 이는 세션 인증 방식과 크게 다를 바가 없다. 이처럼 세상에 완벽한 보안 방법은 없다. 보안과 UX 사이에서 적절한 균형을 찾고 내가 구현하려는 서비스에 적합한 방식이 무엇인지 알아야 한다.
'부트캠프 > 따로 공부' 카테고리의 다른 글
사이드 프로젝트 with React 20. 아키텍처 다이어그램 (0) | 2023.03.11 |
---|---|
사이드 프로젝트 with React 19. 시스템 설계 (0) | 2023.03.10 |
사이드 프로젝트 with React 18. AWS (0) | 2023.03.09 |
[맥 Mac] 사용 중인 포트 강제 종료(localhost port) (0) | 2023.03.08 |
사이드 프로젝트 with React 17. 중간 정리 (0) | 2023.03.08 |