가장 많이 쓰이는 암호화 방식
복호화가 가능한 다른 암호화 방식들과 달리, 해싱은 암호화만 가능
Hash Function 을 사용해서 암호화 진행
특징: 항상 같은 문자열 리턴
=> 서로 다른 문자열에 함수 사용 시, 반드시 다른 결과 값
=> 서로 같은 문자열에 함수 사용 시, 반드시 같은 결과 값
해싱 함수를 거치기 전에 값을 기록해놓은 표 => 유출 시 위험하다.
이 때 활용할 수 있는 것 => 솔트(Salt)
Salt 라는 임의의 값을 더해서, 데이터가 유출되더라도 Hashing 이전의 값을 알아내기 어렵게 암호화하는 방법
기존 값 ==Hashing==> 암호화된 값(A)
기존 값 + Salt ==Hashing==> A와 전혀 다른 암호화된 값(B)
즉, 암호화된 값(A)를 알게 되더라도
기존 값에 Salt를 더해서 암호화된 값이랑 다르다.
해싱 값이 유출되더라도, 보안을 유지할 수 있다.
왜 복호화가 불가능한 암호화 방식을 사용할까?
해싱의 목적은 동일한 값의 데이터를 사용하고 있는지 여부만 확인하는 것이 목적이다.
웹 사이트 관리자는 비밀번호를 알 필요가 없고, 알아서도 안 된다.
보통 비밀번호를 DB에 저장할 때, 복호화가 불가능하도록 해싱해서 저장한다.
해싱은 복호화가 불가능하니까,
웹 사이트 관리자도 정확한 비밀번호를 알 수 없게 된다.
그럼 어떻게 비밀번호의 일치를 확인해서 로그인하는가?
비밀번호 -> 암호화(해싱)한 값을 저장해서,
암호화(해싱)한 값끼리만 일치하면 로그인이 되게끔 하면 된다.
해싱은 민감한 데이터를 다뤄야 하는 상황에서
데이터 유출 위험을 줄이고,
데이터 유효성을 검증하기 위해 사용되는
단방향 암호화 방식이다.
토큰 인증 방식을 사용하면 사용자의 인증 정보를 클라이언트 측에 저장할 수 있습니다.
(사용자 인증 정보를 서버에 저장하는게 아니다)
최근 웹 애플리케이션에서 많이 사용되는 인증 방식 중 하나이다.
등장 배경
세션 기반 인증이 가지고 있던 한계를 극복하고자 고안되었다.
어떤 한계?
세션 기반 인증은 '서버'에서 유저의 상태를 관리한다.
개발자들은 서버의 부하를 줄이고 싶었다.
사용자 인증 정보를 클라이언트에 저장하는 방법을 고민했다.
그래서 토큰 인증 방식이 등장했다.
토큰은 유저의 인증 상태를 클라이언트에 저장한다.
세션에 비해, 서버의 부하나 메모리 부족 문제를 줄일 수 있다.
웹 보안에서 토큰은
인증과 권한 정보를 담고 있는 암호화된 문자열이다.
토큰 인증 방식의 흐름
무상태성(토큰 강제 종료 못함)
인증 상태를 관리하는 주체가 서버가 아니므로,
토큰이 탈취되어도 해당 토큰을 강제로 만료시킬 수 없습니다.
따라서 토큰이 만료될 때까지 사용자로 가장해 계속해서 요청을 보낼 수 있습니다.
유효 기간
토큰이 탈취되는 상황을 생각해보자.
유효 기간을 짧게 설정하면, 사용자는 토큰이 만료될 때마다 다시 로그인을 진행해야 한다.
이는좋지 않은 사용자 경험을 제공합니다.
유효 기간을 길게 설정하면, 토큰이 탈취될 위험이 있다.
토큰이 탈취될 경우 더 치명적이다.
토큰의 크기
데이터의 증가에 따른 비용 문제
토큰에 여러 정보를 담을 수 있는 만큼,
많은 데이터를 담으면 그만큼 암호화하는 과정도 길어지고 토큰의 크기도 커진다.
이는 네트워크 비용 문제를 초래한다.
토큰의 한계를 극복하기 위해 고안되었다.
이 2가지 토큰을 함께 사용하는 것이다.
액세스 토큰은 말 그대로 서버에 접근하기 위한 토큰이다.
따라서 보안을 위해 보통 24시간 정도의 짧은 유효기간이 설정되어 있다.
액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용되는 토큰이다.
따라서 리프레시 토큰은 액세스 토큰보다 긴 유효기간을 설정합니다.
이렇게 두 가지의 각기 다른 토큰을 사용하는 경우,
액세스 토큰이 만료되더라도 리프레시 토큰의 유효기간이 남아있다면,
사용자는 다시 로그인을 할 필요 없이 지속해서 인증 상태를 유지할 수 있습니다.
하지만 리프레시 토큰의 도입도 모든 문제를 해결해주진 않는다.
리프레시 토큰은 긴 유효 기간을 가지고 있다.
이 토큰마저 탈취된다면 토큰의 긴 유효 기간 동안 악의적인 유저가 계속해서 액세스 토큰을 생성하고 사용자의 정보를 해킹할 수도 있기 때문이다.
이를 대비하기 위해 리프레시 토큰을 세션처럼 서버에 저장하고 이에 대한 상태를 관리하기도 한다.
모든 것에는 언제나 양면성이 존재한다.
결국 이 세상에 완벽한 보안 방법은 없다.
세션, 토큰 등 다양한 보안 방식 및 여러 구현 방법들은 절대 뚫리지 않는 궁극의 보안을 위해 만들어진 것이 아니다.
이러한 여러 방식들은 단순히 보안뿐만 아니라 보안과 사용자 경험 사이의 적절한 균형을 찾기 위해 만들어졌다.
중요한 것은 내가 구현하려는 서비스에 어떤 인증 방식이 가장 적절한지 판단하는 것이다.
JSON 객체에 정보를 담고 이를 토큰으로 암호화하여 전송할 수 있는 기술이다.
토큰 기발 인증 구현 시 대표적으로 사용한다.
aaaaaaaa.bbbbbbbbb.ccccccccccc
(Header).(Payload).(Signature)
JWT는 .으로 나뉘어진 세 부분이 존재하는데 이를 각각
Header, Payload, Signature 라고 한다.
HTTP의 헤더처럼
해당 토큰 자체를 설명하는 데이터가 담겨있다.
typ: 토큰의 종류
alg: 시그니처를 만들 때 사용할 알고리즘
이 JSON 객체를 base64 인코딩하면, JWT의 첫 번째 부분인 Header가 완성된다.
*base64 인코딩: 3바이트 데이터를 4문자로 표현하는 인코딩 방식. 전자우편 첨부파일 전송에 많이 사용된다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload 는 컴퓨팅 용어로 전송되는 데이터를 의미한다.
HTTP의 Payload와 마찬가지로 전송하려는 내용을 담고 있다.
정보 접근 권한, 개인정보, 토큰의 정보를 JSON 형태로 담는다.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
시그니처는 토큰의 무결성을 확인할 수 있는 부분이다.
Header, Payload가 완성되었다면,
Signature는 이를 서버의 비밀 키(암호화에 추가할 Salt)와 Header에서 지정한 알고리즘을 사용해서 해싱한다.
예를 들어, 만약 HMAC SHA256 알고리즘을 사용한다면 Signature는 아래와 같은 방식으로 생성됩니다.
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
누군가 권한을 속이기 위해 토큰의 Payload를 변조하는 등의 시도를 하더라도,
토큰을 발급할 때 사용한 Secret을 정확하게 알고 있지 못한다면,
유효한 Signature를 만들어낼 수 없다.
따라서 서버는 Signature를 검증하는 단계에서 올바르지 않은 토큰임을 알아낼 수 있다.