Cookie, Session, Auth, Token, jwt, OAuth, 인증, safari, remember_me

반응형

Cookie, Session, Auth, OAuth, 인증, safari

 

전통적 인증 방법. 만료일이 설정되지 않은 쿠키를 세션쿠키라고 하며 보통 이것으로 세션 id 를 저장한다. 클라이언트가 id, password 를 입력하고 서버로 요청을 보내면 서버는 이 정보가 맞는지 확인하고 맞다면 Set-Cookie 헤더에 session id 값을 넣어서 응답해준다. 클라이언트는 이 Set-Cookie 헤더 값을 자동으로 쿠키에 저장한다. 그리고 매번 서버에 요청시 이 session id 값을 Cookie 라는 헤더에 담아서 요청을 한다. 서버 측에서는 이 Cookie 정보에서 session id 값이 session storage 에 있다면 현재 로그인된 사용자로 간주한다. 이런 방식은 범용적으로 사용되긴 힘들다. 웹 브라우저에서나 가능하다. 굳이 사용하고자 한다면 가능은 하지만 어느정도 한계가 존재한다. 이에 따라 여러 대안이 있을 수 있으나... HTTP Basic Auth 도 있고, token, oauth.. 결국 oauth 도 token 방식이나 좀 더 규격화 된 프로세스 절차가 있다.

큰 프레임워크를 위해서는 oauth 2.0 으로 나가는 것이 맞을 것이고,
간단히 빨리 만드는 데 있어서는 json web token 을 sign 하는 방식으로 후딱 적용하는 것도 좋을 것이다. 인증이 필요없을 수도 있고, 있다고 해도 게시판 정도만 있을 수도 있고 한데 oauth 를 적용하긴 좀 무거울 듯.

 

Angular 나 React 로 만든 SPA 의 경우 rest api 호출 형식으로 angular, react
의 스크립트를 받은 호스트와 login 과 같은 인증 api 의 서버 호스트가 다른 이름을 가질 경우 safari 의 기본 설정에서는 로그인이 안되는 증상이 있다. safari 의 환경설정에서 개인 정보 보호 부분에 쿠키 및 웹 사이트 데이터에 '방문한 웹 사이트 허용' 이라는 옵션이 기본으로 되어 있기 때문이다.

'항상 허용' 을 하게 되면 쿠키가 항상 허용되므로 문제가 없다. '항상 차단' 을 하면 네이버도 로그인이 안된다.

이유는 일반적인 세션 인증에서 로그인 되었다는 것을 세션을 통해서 구현하게 되는데 이 세션이라는 것은 서버 측의 작은 저장소일 뿐이고 서버 측의 기술이다. 실질적으로 클라이언트와의 통신은 쿠키라는 것을 통하게 된다. 세션이라는 것은 HTTP 통신과 관련 없는 서버측의 자그마한 인증을 위한 정보 저장 공간을 다루는 기술일 뿐이다.

이 때 보통 사용되는 쿠키는 세션 쿠키라고 불리며 세션 쿠키란 저장 기간이 해당 브라우저가 종료되기 전까지인 쿠키를 의미한다.

보통은 쿠키의 직접적인 사용보다 정보 저장 자체는 HTML5 의 LocalStorage 를 사용하게 되는데 결국 LocalStorage 는 클라이언트 측 정보 저장 기술일 뿐이고 서버와 클라이언트 간의 상호 작용은 Cookie 규약의 헤더를 통해 이루어진다.

따라서 일반적인 브라우저의 경우 쿠키 허용이 기본 옵션이고 이러한 옵션은 크로스 도메인까지 허용을 의미하지만 사파라의 경우 방문한 웹 사이트 허용 이라는 옵션이 기본이고 이것은 자신이 방문한 페이지의 도메인과 일치하는 곳과의 쿠키만을 허용한다는 의미이다. 이러한 이유로 SPA 로 만든 클라이언트 스크립트만을 다운받아 도메인이 다른 API 서버로 인증을 위한 API 를 호출할 경우 쿠키가 적용되지 않아 로그인이 안된다.

따라서 인증의 경우는 OAuth2 를 사용하거나 아니면 일반적인 웹페이지의 경우 JSP 와 같이 뷰를 직접 서버측의 뷰로 보여주는 방식으로 작성하는 것이 보안상으로도 좋은 방식이다.

우버나 에어비앤비 등의 사이트들을 보면 로그인의 경우 auth.uber.com 과 같이 들어가면 로그인 페이지가 서버측에서 파싱한 페이지가 보이도록 되어 있다. 대다수의 제대로 된 사이트 들의 경우 웹에서는 이러한 쿠키를 이용한 인증방식을 사용하며 또한 그 뷰는 서버 측의 뷰를 보여주는 방식으로 구현되어 있다.

 

앱이나 웹이나 등등 범용적으로 사용하기 위해서 또한 API 까지 노출하기 위해서는 OAuth2 를 추가하는 방향으로 간다.

 

참고로 이런 방법도 있다고는 한다.
http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari

 

XHR withCredentials...

https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials

https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials#Example

http://blog.ionic.io/angularjs-authentication/

 

CORS 버전들.. simple, preflight
http://stackoverflow.com/questions/22372377/angularjs-http-post-withcredentials-fails-with-data-in-request-body

https://quickleft.com/blog/cookies-with-my-cors/

 

클라이언트 쪽에서 세션쿠키를 없앨 수는 없나? 내용을 보면 서버에서 로그아웃 하는 것은 클라이언트 쪽에서 할 수 없다고 한다. 브라우저의 세션 쿠키를 강제로 지워버리면 더이상 로그인 상태가 아닌거 아닌가? 라고 생각할 수 있는데 보안 상 세션쿠키 부분을 직접 컨트롤 할 수 없게 되어 있는 듯 하다. (브라우저의 기본 동작 방식이 그런 것이다. 브라우저는 내 PC 측에 있으니 맘 먹으면 우회하는 방법으로는 되겠지)
http://stackoverflow.com/questions/18734267/javascript-remove-a-cookie-that-was-set-by-an-ajax-response
https://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly

 

세션쿠키 또한 일반적인 쿠키이지만 httpOnly 파라미터가 붙은 것은 읽고, 쓰고, 삭제하는 것을 HTTP 요청을 통한 방식을 제외하고는 불가하다. 예를 들어 자바스크립트로 쿠키를 변경하는 것이 불가하다.
A session cookie is just a normal cookie without an expiration date. Those are handled by the browser to be valid until the window is closed or program is quit.

But if the cookie is a httpOnly cookie (a cookie with the httpOnly parameter set), you cannot read, change or delete it from outside of the HTTP.

 

쿠키 다루는 법들..
https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
http://www.w3schools.com/js/js_cookies.asp
http://www.sitepoint.com/how-to-deal-with-cookies-in-javascript/
http://stackoverflow.com/questions/179355/clearing-all-cookies-with-javascript

 

세션, 쿠키, 인증, remember me

장바구니나 팝업 같은 경우 cookie 에 정보를 담아 사용할 수 있다.

기본적으로 세션쿠키의 경우 서버 접속시 세션 아이디 값이 부여되고 그것이 세션쿠키에 저장되어 세션이 유지된다.

remember me 의 경우 session token 을 발행하여 cookie 에 저장해주는데 이 session token 을 매 요청시마다 먼저 검사하여 일치하면 로그인 상태로 해준다. 로그인된 상태라면 검사 안해야겠고, 로그인 안된 상태라면 이 값을 체크하고, 매번 재발행해준다.

 

 

 

 

크게 세가지 방식 정도가 있을거 같아요.

 

1. 세션쿠키 httponly 를 이용한 일반적인 세션 인증. xss csrf 공격에 대한 고려 필요.

2. jwt 을 이용한 간단한 토큰 인증. jwt 의 원리를 이해하시면 유효한 토큰은 서버에서 밖에 만들지 못한다는 것늘 알 수 있어요. 대신 해당 토큰을 바탕으로 어떻게 하겠다는 정책은 정하기 나름이죠. 요청시 authorization bearer 헤더에 실어서 보내구요. 이것도 토큰이 탈취 당하면 해킹당하는거죠.

3. oauth2.0 및 openid connect 를 이용하는 방법.

 

간단한 사이트는 1번 방식을 많이 사용하는거 같구요. 2번 방식은 좀 어설픈 땜빵이고. 3번 방식은 제대로된 서비스를 만드시려면 이걸 사용하시면 될 거 같아요. 그냥 인증은 이게 표준인 거 같아요. authorization server 를 만드실 시간이나 자원이 부족하시면 okta 같은 서비스를 이용하시면 될 거 같아요.

 

세션 방식은 결국 쿠키를 이용하는거에요. 세션은 서버측에 저장소라고 생각하시면 되구요. 브라우저에서 쿠키가 약간 성격이 다른게 있는데 세션쿠키라고 다른 쿠키 보다 보안이 더 강화된 게 있어요. httponly 옵션을 줄 경우 해당 쿠키는 http 헤더에 실어서 보낼때만 사용되고 자바스크립트로는 조작이 불가능해서 xss 같은 위협을 방지할 수 있어요. 아무튼 세션인증이라는 것도 결국 클라이언트 측에서는 전부 쿠키를 사용하는 것입니다.

 

쿠키 만료 시간을 안주면 쿠키가 브라우저를 닫을 때 삭제 되도록 되어 있나보네요. 그걸 세션 쿠키라고 하고. httponly 옵션은 자바스크립트가 조작할 수 없는 쿠키를 말하고 로컬스토리지, 세션스토리지 의 세션 스토리지와는 다른 의미이네요. 그냥 쿠키가 조작 방법에 따라 저렇게 여러 종류가 존재합니다. 과거에는 쿠키가 인증을 위한 유일한 방법이었으니 아마존 같은 업체에서도 이런 기술 부분에 많은 연구를 했었을 거 같아요. 지금은 oauth2 와 openid 로 거의 통일되었다고 보면 되구요.

 

Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB

 

Set-Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

Secure; HttpOnly;

Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

 

이 jwt 를 받은 후 전통적 인증 방식과 비슷하게 매 요청마다 함께 던져주는데 (http 는 tcp 소켓과 달리 비 연결 지향이라...) 일반적으로는 Authorization 헤더에 넣어준다.

 

이 때는 Bearer 한칸 띄우고 JWT 형식으로 보내준다. X-Access-Token 이라는 헤더를 통해서도 보내주기도 하는데 이는 과거 이것이 일반적인 인증 방식이 아니었을 때 X 붙은 번 외의 헤더로 보내주었었다. 지금은 Authorization 에 Bearer 를 붙여서 보내는게 맞는 듯 하다.

 

그리고 전통적 인증 방식과 비슷하게 쿠키에 담아서 보낼 수도 있다. CSRF 와 XSS 공격에 대해 쿠키는 기본적으로 CSRF 에 강력하다고 한다. HTML5 의Web Storage 즉 LocalStorage 의 경우 XSS 에 취약하다고 한다. 쿠키는 기본적으로 과거부터 인증을 위해 사용되어 왔기 때문에 보안에 강하다고 한다. 그래서 쿠키를 추천하지만 모바일의 경우 쿠키가 일반적으로 지원이 되지 않는 경우도 있고해서...

 

대부분의 구현은 local storage 에 저장을 하는데... https 로 보내지 않으면 탈취될 가능성도 염두에 두어야 한다. 토큰은 사실 세션 처럼 서버측 세션 저장소에 저장하지 않아도 된다. secret 만 가지고 있으면 된다. 하지만 레디스 같은 곳에 토큰을 키로 하는 정보들을 저장해 놓고 인증이 되면 그 정보들을 불러와서 사용하는 방식도 사용한다. 꼭 저장된 정보를 가져온다기 보다 레디스에 정보가 있으면 토큰을 발행한 상태라는 것을 통해 더 명확한 인증을 하려는 것. 하지만 그냥 레디스를 사용하지 않는 것이 가벼운 듯 하다. 이렇게 하면 서버 측에서 강제로 토큰을 파기하려면 secret key 를 변경하면서 강제 업데이트를 시켜주는 방법 외엔 없을 듯 하다. 아니면 다른 요청을 통해 자동으로 클라이언트 측 토큰을 파기하도록 하던가. 그렇다면 모바일과 웹 등 몇 개의 기기에서 로그인 시에는 어떨까? 상관없다... payload 에 android 인지 web 인지 등등 정보를 넣어줄 수도 있고... 안 넣어주더라도 아이디와 비밀번호가 맞아서 인증에 성공하면 그 때의 토큰을 발행해서 내려주기 때문에 여러 단말에서도 접속이 무리없이 가능하다. aaa.bbb 를 단방향 암호화 해싱을 통해 ccc 만 나오면 된다.

반응형

댓글

Designed by JB FACTORY