세션과 쿠키를 이용한 인증
인증 순서
- 사용자가 로그인을 함.
- 서버에서는 계정 정보를 읽어 사용자를 확인한 후, 사용자에게 고유한 ID값을 부여하여 세션 저장소에 저장한 후, 이와 연결되는 Session ID를 발급함.
- 서버는 HTTP 응답 헤더에 발급된 Session ID를 실어 보냄. 이후 매 요청마다 HTTP 요청 헤더에 Session ID가 담킨 쿠키를 실어 보냄.
- 서버에서는 쿠키를 받아 세션 저장소에서 대조를 한 후 대응되는 정보를 가져옴.
- 인증이 완료되고 서버는 사용자에 맞는 데이터를 보냄.
장점
- 사용자의 정보는 세션 저장소에 저장되고, 쿠키는 그 저장소를 통과할 수 있는 출입증 역할을 함. 따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체에는 유의미한 값을 갖고있지 않아서 쿠키에 사용자 정보를 담아 인증을 거치는 것 보다 안전함.
- 각 사용자는 고유의 Session ID를 발급 받기 때문에 일일이 회원 정보를 확인할 필요가 없어 서버 자원에 접근하기 용이함.
단점
- 쿠키에 사용자 정보를 담아 인증을 거치는 것 보다 안전하지만, 해커가 쿠키를 탈취한 후 그 쿠키를 이용해 HTTP 요청을 보내면 서버는 사용자로 오인해 정보를 전달하게 됨. 이를 세션 하이재킹 공격이라고 함. 해결책으로는 HTTPS 프로토콜 사용과 세션에 만료 시간을 넣어주는 것.
- 서버에서 세션 저장소를 사용하기 때문에 추가적인 저장공간을 필요로 함.
Access Token을 이용한 인증
JWT
JWT는 JSON Web Token의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 말하며, Access Token으로 사용 됨. JWT를 생성하기 위해서는 Header, Payload, Verify Signature 객체를 필요로 함.
Header는 토큰의 타입을 나타내는 typ과 암호화할 방식을 정하는 alg로 구성되어 있음.
{
'alg': 'HS256',
'typ': 'JWT'
}
Payload
Payload는 토큰에 담을 정보를 포함. 여기서 하나의 정보 조각을 클레임으로 부름. 클레임의 종류로는 Registered, Public, Private로 3가지가 존재. 보통 만료 일시, 발급 일시, 발급자, 권한정보 등을 포함.
{
'sub': '1234567890',
'name': 'John Doe',
'admin': true,
'iat': 1516239022
}
Verify Signature
Verify Signature는 Payload가 위변조되지 않았다는 사실을 증명하는 문자열. Base64 방식으로 인코딩한 Header, Payload 그리고 SECRET KEY를 더한 후 서명.
HMACSHA256 {
base64UrlEncode(header) + '.' +
base64UrlEncode(payload),
your-256-bit-secret
}
완성된 토큰
Header, Payload는 인코딩될 뿐, 따로 암호화되지 않음. 따라서 Header, Payload는 누구나 디코딩하여 확인할 수 있기에 정보가 쉽게 노출될 수 있음. 하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없음.
만약에 해커가 사용자의 토큰을 훔쳐 Payload의 데이터를 변경하여 토큰을 서버로 보낸다면, 서버에서 Verify Signature을 검사 하게 됨. 여기서 Verify Signature는 해커의 정보가 아닌 사용자의 정보를 기반으로 암호화 되었기 때문에 해커가 변경한 정보로 보낸 토큰은 유효하지 않은 토큰으로 간주. 이를 통해 사용자의 SECRET KEY를 알지 못하면 토큰을 조작할 수 없다는 것을 알 수 있음.
인증 순서
- 사용자가 로그인 함.
- 서버에서는 계정 정보를 읽어 사용자를 확인 후, 사용자의 고유한 ID값을 부여하고 Payload에 정보를 넣음.
- JWT 토큰의 유효기간을 설정함.
- SECRET KEY를 통해 암호화된 Access Token을 HTTP 응답 헤더에 실어 보냄.
- 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 HTTP 요청 헤더에 실어 보냄.
- 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작 여부, 유효 기간을 확인함.
- 검증이 완료된다면, Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져옴.
장점
- 간편함. 세션과 쿠키를 이용한 인증은 별도의 세션 저장소의 관리가 필요함. 그러나 JWT는 발급 후 검증만 거치면 되기 때문에 추가 저장소가 필요함.
- 확장성이 뛰어남. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능함.
단점
- JWT는 한 번 발급되면 유효기간이 완료될 때까지는 계속 사용이 가능하며 중간에 삭제가 불가능. 따라서 해커에 의해 정보가 털린다면 대처할 방법이 없음.
해결책으로는 Refresh Token을 추가적으로 발급하여 해결하는 방식으로 아래에서 설명.
- Payload 정보가 디코딩하면 누구나 접근할 수 있기에 중요한 정보들을 보관할 수 없음.
- JWT의 길이가 길기 때문에, 인증 요청이 많아지면 서버의 자원낭비가 발생.
Access Token + Refresh Token을 이용한 인증
Access Token을 이용한 인증 방식의 문제는 해커에게 탈취당할 경우 보안에 취약하다는 점. 토큰의 유효기간을 짧게 하면 사용자는 로그인을 자주 해야해서 번거롭고, 길게 하면 보안이 취약해지기 때문에 이를 해결하고자 나온 것이 Refresh Token.
Refresh Token은 Access Token과 같은 형태인 JWT. Refresh Token은 Access Token보다 긴 유효기간을 가지고, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됨. 예를 들어 Refresh Token의 유효 기간이 2주, Access Token의 유효 기간이 1시간이라고 한다면, 2주 동안 Access Token이 만료 되는 1시간 주기마다 Access Token을 새롭게 발급받을 수 있음.
인증 순서
- 사용자가 로그인을 함
- 서버에서는 회원 DB에서 값을 비교함.
- 로그인이 완료되면 Access Token, Refresh Token을 발급하여 HTTP 응답 헤더에 실어 보냄. 이때 일반적으로 회원 DB에 Refresh Token을 저장해둠.
- 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 HTTP 요청 헤더에 실어 요청을 보냄.
- Access Token을 검증하여 이에 맞는 데이터를 보냄.
- 시간이 지나 Access Token이 만료됐다고 보겠음.
- 사용자는 이전과 동일하게 Access Token을 HTTP 요청 헤더에 실어 보냄.
- 서버는 Access Token이 만료됨을 확인하고 권한 없음을 신호로 보냄.
- 사용자는 Refresh Token과 Access Token을 HTTP 요청 헤더에 실어 보냄.
- 서버는 받은 Access Token이 조작되지 않았는지 확인한 후, HTTP 요청 헤더의 Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교함. Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해줌.
- 서버는 새로운 Access Token을 HTTP 응답 헤더에 실어 다시 API 요청을 진행함.
장점
- Access Token의 유효 기간이 짧기 때문에, 기존의 Access Token만을 이용한 인증보다 안전함.
단점
- 구현이 복잡함.
- Access Token이 만료될 때마다 새롭게 발급하는 과정에서 서버의 자원 낭비가 생김.
OAuth 2.0을 이용한 인증
OAuth
OAuth는 외부서비스의 인증 및 권한부여를 관리하는 범용적인 프로토콜.
OAuth 2.0
현재 범용적으로 사용되고 있는 것은 OAuth 2.0. 2007년에 OAuth 1.0의 초안이 발표되었는데, 네트워크 시장이 커감에 따라 한계가 나타나기 시작. 그리고 2012년에 OAuth 2.0를 발표하면서 현재까지 사용되고 있음. 바뀐 점은 크게 3가지.
- 모바일에서도 사용이 용이함.
- 반드시 HTTPS를 사용하므로 보안이 강화됨.
- Access Token의 만료 기간이 생김.
OAuth 2.0의 인증 방식은 4가지가 있지만, 가장 범용적으로 쓰이는 Authorization Code Grant에 대해 알아봄.
인증 순서
- Resource Owner : 일반 사용자
- Client : 우리가 만든 웹 어플리케이션
- Authorization Server : 권한 관리 및 Access Token, Refresh Token을 발급해주는 서버
- Resource Server : OAuth 2.0을 관리하는 서버의 자원을 관리하는 곳
- Resource Owner가 Client에게 인증 요청을 함.
- Client는 Authorization Request를 통해 Resource Owner에게 인증할 수단(Facebook, Google 로그인 url)을 보냄.
- Resource Owner는 해당 Request를 통해 인증을 진행하고 인증을 완료했다는 신호로 Authorization Grant를 url에 실어 Client에게 보냄.
- Client는 해당 권한 증서를 Authorization Server에 보냄.
- Authorization Server는 권한 증서를 확인 후, 유저가 맞다면 Client에게 Access Token, Refresh Token, 그리고 유저의 정보를 발급해줌.
- Client는 해당 Access Token을 DB에 저장하거나 Resource Owner에게 넘김.
- Resource Owner가 Resource Server에 자원이 필요하면, Client는 Access Token을 담아 Resource Server에 요청.
- Resource Server는 Access Token이 유효한 지 확인 후, Client에게 자원을 보냄.
- 만일 Access Token이 만료됐거나 위조되었다면, Client는 Authorization Server에 Refresh Token을 보내 Access Token을 재발급 받음.
- 그 후 다시 Resource Server에 자원을 요청함.
- 만일 Refresh token도 만료되었을 경우, Resource Owner는 새로운 Authorization Grant를 Client에게 넘겨야함.
SNS 로그인
인증 순서
- 사용자가 서버에게 로그인을 요청함.
- 서버는 사용자에게 특정 쿼리들을 붙인 페이스북 로그인 URL을 사용자에게 보냄.
- 사용자는 해당 URL로 접근하여 로그인을 진행한 후 권한 증서를 담아 서버에게 보냄.
- 서버는 해당 권한 증서를 Facebook의 Authorization Server로 요청함.
- 서버는 권한 증서를 확인 후, Access Token, Refresh Token, 유저의 정보를 돌려줌.
- 받은 고유 ID를 Key값으로 해서 DB에 유저가 있다면 로그인, 없다면 회원가입을 진행함.
- 로그인이 완료되었다면 세션과 쿠키 , 토큰 기반 인증 방식을 통해 사용자의 인증을 처리함.
참고 사항
- 우리가 만들 서버에서 OAuth를 이용하기 위해서는 사전에 OAuth에 등록하는 과정이 필요함. 개발자 사이트에서 웹 어플리케이션을 등록 후 APP_ID와 CLIENT_ID 등을 보내야 OAuth 에서는 어느 서비스인지를 알 수 있음.
- 페이스북 로그인을 인증을 이용하는 경우, 대부분은 Resource Server(페이스북 자체 API)를 사용하지 않음. 따라서 Access Token, Refresh Token은 실제로 쓰이지 않음. 우리의 서버에서 Access token을 검증할 수도 없을 뿐더러 인증의 수단으로 활용하기엔 부족한 점이 많음. 따라서 보통 Authorization Server로 부터 얻는 고유 ID값을 활용해서 DB에 회원관리를 진행함.
참고
https://velog.io/@gusdnr814/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B8%EC%A6%9D-4%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95