계정정보를 요청에 담아 보내는 방식. HTTP 요청에 인증할 수단에 비밀번호를 넣는다.
해커가 HTTP 요청을 가로채서 계정정보를 알 수 있다. 최악의 인증방식
쿠키는 클라이언트 로컬에 저장되어있기 때문에 다른 사용자에 의해서 임의로 변경이 가능하므로 보안성이 낮습니다.
세션 저장소를 보통 redis를 많이 사용한다고 함.
http 요청 중에 쿠키가 노출되더라도 쿠키 자체(세션 id)는 유의미한 값을 가지고 있지 않으므로 안전할 수 있음. 그러나 해커가 쿠키를 가로챈다면 그 훔친 쿠키를 이용해 http 요청을 보내면 서버는 사용자로 인식해 정보를 그냥 보내주게됨
해결책 1 : https를 사용해 요청 자체를 탈취해도 안의 정보를 읽기 힘들게 한다
해결책 2 : 세션에 유효시간을 설정한다.
세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 됨. 하지만 jwt는 한 번 발급되면 유효기간이 완료될 때까지 계속 사용 가능. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 정보를 가져갈 수 있음
해결책 : 기존의 access token의 유효기간을 짧게하고 refresh token이라는 새로운 토큰을 발급하면 됨.
db에 pw는 해쉬함수로 해싱을 한 pw를 저장.
이때 salting,key 스트레칭,adaptive key derivation function(bcrypt,,) 을 이용하여 더 안전하게 해싱한 pw 저장
3~4. 사용자가 확인 되면 Access Token, Refresh Token을 발급합니다. 이때 일반적으로 회원DB에 Refresh Token을 저장해둡니다.
5. 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보냅니다.
6~7. Access Token을 검증하여 이에 맞는 데이터를 보냅니다.
8. 시간이 지나 Access Token이 만료됐다고 보겠습니다.
9. 사용자는 이전과 동일하게 Access Token을 헤더에 실어 요청을 보냅니다.
10~11. 서버는 Access Token이 만료됨을 확인하고 권한없음을 신호로 보냅니다.
** Access Token 만료가 될 때마다 계속 과정 9~11을 거칠 필요는 없습니다.
사용자(프론트엔드)에서 Access Token의 Payload를 통해 유효기간을 알 수 있습니다. 따라서 프론트엔드 단에서 API 요청 전에 토큰이 만료됐다면 바로 재발급 요청을 할 수도 있습니다.
access token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 http 요청 횟수가 많아짐. 서버의 자원 낭비
refresh token도 db에 저장 해야 되나..? -> redis에 저장하기로
XSS 공격
해커가 클라이언트 브라우저에 javascript를 삽입해 실행하는 공격.
공격자가 < input > 태그를 통해 javascript를 서버로 전송해 스크립트를 실행하거나 url에 javascript를 적어 클라이언트에서 스크립트 실행이 가능하다면 공격자가 사이트에 스크립트를 삽입해 xss 공격을 할 수 있다. 이때 공격자는 Javascript를 통해 사이트의 글로벌 변숫값을 가져오거나 그 값을 이용해 사이트인 척 API 콜을 요청할 수도 있다. 다시 말하면 공격자의 코드가 내 사이트의 로직인 척 행동할 수 있다는 거다.
CSRF 공격
공격자가 다른 사이트에서 우리 사이트의 API 콜을 요청해 실행하는 공격이다. API 콜을 요청할 수 있는 클라이언트 도메인이 누구인지 서버에서 통제하고 있지 않다면 CSRF가 가능한데, 이때 공격자가 클라이언트에 저장된 유저 인증정보를 서버에 보낼 수 있다면, 제대로 로그인한 것처럼 유저의 정보를 변경하거나 유저만 가능한 액션들을 수행할 수 있다.
클라이언틍에서는 보통 인증정보(세션 id, access token, refresh token)를 localstorage나 쿠키에 저장한다. 페이지를 리프레시하거나 창을 닫고 다시 접속할 때도 로그인 정보가 이어지도록 둘 다 브라우저에 방식.
쿠키는 브라우저를 종료해도 계속해서 정보가 남아있음.
localstorage는 강제적으로 지우지 않는 이상 영구적임. 데이터 만료 시점이 없다.
쿠키와 localstorage는 위 두가지 공격에 취약할 수 있다.
추가적으로 sesstion storage가 있는데 여기에 저장된 데이터는 페이지 세션이 종료되면(웹 브라우저를 닫을 경우) 지워진다.
브라우저 저장소에 저장하는 방식. javascript 내 글로벌 변수로 읽기/쓰기 접근이 가능
이 곳에 인증 정보(세션 id, refresh token, access token)을 저장하면 XSS 취약점을 통해 local storage 안에 담긴 값을 불러오거나, 불러온 값을 이용해 api 콜을 위조할 수 있다.
브라우저에 쿠키로 저장되는데, 클라이언트가 HTTP 요청을 보낼 때마다 자동으로 쿠키가 서버에 전송된다. Javascript 내 글로벌 변수로 읽기 / 쓰기 접근이 가능하다.
해커가 XSS 취약점을 통해 cookie에 담긴 값들을 불러오거나, 불러온 값을 통해 API 콜을 보내면 쿠키에 담긴 값들이 함께 전송되어 로그인한 척 공격을 수행할 수 있다.
쿠키에 세션 id나 accessToken을 저장해 인증에 이용하는 구조에 CSRF 취약점이 있다면 해커는 CSRF 공격으로 API 콜 요청시 자동으로 인증 정보가 쿠키에 담겨 서버로 보내진다. 공격자는 유저 권한으로 정보를 가져오거나 액션을 수행할 수 있다.
쿠키에 refresh token만 저장하고 access token은 다른 곳에 저장하면 CSRF 공격시 쿠키가 서버로 보내져도 access token이 없기 때문에 유저 권한으로 정보를 가져오거나 액션을 수행할 수 없다. 즉, CSRF 공격 방어 가능
브라우저에 쿠키로 저장되는 건 같지만 javascript 내에서 접근이 불가능하다. secure를 적용하면 https 접속에서만 동작한다.
httpOnly 쿠키 방식으로 저장된 정보는 XSS 공격을 해도 자바스크립트 내에서 httpOnly 쿠키에 접근이 불가능하기 때문에 XSS 방어 가능.
하지만, httpOnly 쿠키에 담긴 값에 접근은 할 수 없지만 XSS 취약점을 노려 API 콜을 해커가 요청하면 httpOnly 쿠키 값도 함께 보내저 유저인 척 행동 가능.
httpOnly 쿠키에 access token이나 세션 id 인증 정보를 저장한다면 CSRF 공격으로 API 콜을 해커가 요청하면 쿠키가 서버로 자동으로 보내지기 때문에 공격자는 유저 액션 수행할 수 있다. 즉, access token이나 세션 id는 httpOnly 쿠키에 저장하면 안됨.
httpOnly 쿠키에 refresh token만 저장한다면 CSRF 공격시 쿠키가 서버로 보내져도 유저 권한으로 일을 수행할 수 없다. 즉, CSRF 공격 방어 가능
결론 : secure httpOnly 쿠키에 refresh token만 저장하고(CSRF 공격 방어), 클라이언트와 서버가 XSS 방어 처리를 해줘야 한다. 그리고 access token은 json payload에 담고 웹 어플리케이션 내 로컬 변수로 이용.
백엔드는 http 응답 set-cookie 헤더에 refresh token 값을 넣고, access token을 json으로 응답한다.
백엔드는 http 응답 set-cookie 헤더에 refresh token 값을 넣고, access token을 json으로 응답한다.
웹이 mount 될 때마다(브라우저 창이 꺼지거나 페이지가 리프레시 되는 등 페이지가 리로드 될 때) refresh token을 이용해 새로운 access token을 받아와 웹 어플리케이션 내 지역 변수에 저장하고 사용한다.
클라이언트쪽에서는 access token을 보내는 API 요청할 때 Authorization 헤더에 access token을 넣어 보내준다. (refresh token은 쿠키 헤더에)