본문 바로가기

TIL ( CODESTATES)

AUTHENTICATION - 쿠키기반/세션기반/토큰기반인증

HTTP 통신의 속성과 인증

 

모바일, 웹 서비스 등에서 가장 널리 쓰이는 통신 방식은 HTTP이다. 

HTTP통신에서 Client와 Serve는 현재 요청에 대해서만 서로를 식별할 수 있고 그 요청에 대한 응답 이후에는 연결을 유지시키지 않는  Connectionless한 속성을 가진다. 이 때문에 HTTP는 서로 다른 요청에 대해서는 아무런 정보도 가지지 않는 Stateless한 속성 (무상태성)또한 가지고 있다.

이 때문에 웹페이지에 한번 로그인한 이후에도 재차 인증 과정을 거쳐야하는 문제가 발생했고 이를 보완하기 위해 쿠키를 사용하게 되었다.

 

 

 

쿠키 기반 인증 방식에서 서버는 클라이언트에 인정 정보를 담은 쿠키를 전송하고, 클라이언트는 전달받은 쿠키를 요청과 같이 전송하여 stateless한 인터넷 연결을 stateful하게 유지할 수 있다.

 

하지만 기본적으로 쿠키를 오랜 시간 동안 유지될 수 있고, 자바스크립트를 이용해 쿠키에 접근할 수 있기 때문에 인정 정보가 유출되기 쉽다. 이 때문에 쿠키 기반 인증 방식은 더이상 많이 사용되지 않지만 쇼핑몰의 장바구니 등 여러 옵션을 장기 보관하는 용도로 사용되고 있다.

#쿠키기반인증 #클라이언트에저장 #자바스크립트로접근가능 #장바구니

 

 

쿠키의 단점들을 보완하고자 나온 것이 세션 기반 인증 방식이다. 클라이언트와 서버 간 연결이 활성화된 상태를 세션이 활성화되어 있다고 한다. 쿠키는 클라이언트에 정보를 저장하지만, 세션은 데이터를 서버에 저장하고 쿠키에는 암호화된 세션ID만 저장한다. 

 

쿠키를 통해서 유효한 세션 ID가 서버에 전달되고, 세션 객체에 해당 세션 ID유저의 인증정보가 존재한다면 서버는 해당 요청이 유저정보에 접근 가능하다고 판단하게 된다. 쿠키에 세션 ID 정보가 없거나 해당 세션 객체에 유저 인증정보가 존재하지 않는경우, 서버는 해당 요청이 유저정보에 접근하게 두어서는 안 된다.

 

또한 서버는 로그아웃 요청을 반드시 필요로 한다.

세션 ID가 담긴 쿠키는 인터넷 탭을 닫으면 삭제되지만 유효기간이 따로 정해져 있지 않다. 그렇기 때문에 세션 ID 쿠키가 유출된 상태에서는 해당 쿠키를 이용한 요청이 유출된 쿠키를 이용한 공격인지, 정말로 사용자가 요청을 보낸건지 서버는 확인할 방법이 없으며, 서버는 쿠키의 유출 여부조차 알수 없다.

즉 서버에서 세션을 파괴하는 과정이 반드시 필요하다.

 

세션은 서버 메모리에 데이터를 저장하기 때문에 서버의 성능이 안 좋아질 수 있다. 또한 여전히 쿠키를 사용하기 때문에 세션Id 가 담긴 쿠키가 탈취될 경우 정보가 유출되는 위험이 있다.

#세션기반인증 #서버에저장 #쿠키에세션아이디저장 #세션파괴

 

 

세션 기반 인증은 서버 혹은 db에 유저 정보를 담는 방식이라 서버의 부담이 컸다. 서버의 부담을 해소하고자 토큰 기반 인증 방식이 도입되었다. 토큰은 유저 정보를 암호화한 방식으로 담을 수 있기 때문에 클라이언트에 저장해도 문제가 없다.

가장 범용적으로 쓰이는 토큰이 JWT이다.

Jason Web Token의 약자로 Json 포맷으로 사용자에 대한 속성을 저장하는 웹 토큰을 의미한다.

#토큰기반인증 #클라이언트에저장 #jwt

 

 

 


 

쿠키 기반 인증 Cookie-Based Authentication

Server는 쿠키를 이용하여 Client에 데이터를 저장할 수 있고 특정 조건을 만족하는 경우 불러와서 사용할 수 있다.

그 조건들은 쿠키 옵션으로 정의해 둘 수 있다.

옵션을 지정한 후 Server에서 Client로 쿠키를 처음 전송하게 되면 요청의 헤더에 Set-Cookie라는 프로퍼티에 쿠키를 담아 전송한다.

이후 Client 혹은 Server에서 쿠키를 전송할 때 Client는 헤더에 Cookie라는 프로퍼티에 쿠키를 담아 전송할 수 있다.

 

주로 사용되는 쿠키 옵션

  • Domain : 쿠키옵션에서 도메인은 포트, 서브도메인, 세부 경로를 포함하지 않는다.
  • Path : 세부 경로는 서버가 라우팅할 때 사용하는 경로이다. 설정된 path를 만족하면 추가적인 path가 있더라도 쿠키를 전송한다.
  • MaxAge or Expires : MaxAge는 몇 초 동안 쿠키가 유효한지, Expires는 언제까지 유효한지 date를 지정한다.
  • Secure : 프로토콜에 따른 쿠키 전송 여부를 설정하는 것으로, true이면 HTTPS프로토콜을 이용한 통신일 경우에만 쿠키를 전송한다.
  • HttpOnly : 자바스크립트에서 브라우저의 쿠키 접근 여부를 설정하는 것으로, true인 경우 자바스크립트에서는 쿠키에 접근이 불가능하다.
  • SameSite : Cross-Origin 요청을 받은 경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부를 결정한다.
                      요청을 보낸 Origin과 서버의 도메인이 같은 경우에만 쿠키를 전송할 것인지 묻는 설정으로 3가지 옵션이 있다.
    • Lax : Cross-Origin 요청일 경우 GET 메소드에 대해서만 쿠키를 전송한다.
    • Strict : Cross-Origin 요청이 아닌 same-site인 경우에만 쿠키를 전송한다.
    • None: 항상 쿠키를 전송한다. 다만 쿠키 옵션 중 Secure 옵션이 필요하다.

 

 

 

쿠키 기반 인증방식으로 로그인/로그아웃 로직 구현하기 Pseudocode

조건 : 로그인은 데이터베이스에 저장된 유저정보로만 로그인이 가능하다.

user모델이 정의되어 있고 migration, seeders가 있는 상태에서 API정보에 따라 쿠키를 이용한 로그인 로직을 구현한다.

  

  • 로그인에 성공할 경우 쿠키에 유저 정보를 저장한다. 
  • 로그아웃을 할 경우 쿠키를 삭제한다.

Server

server/login.js

 

1. req.body에 존재하는 유저 정보를 바탕으로 데이터베이스에 해당 유저의 데이터를 받아온다.

2. req로 받은 유저 정보가 일치하지 않는 경우 에러메세지를 반환한다.

3. req로 받은 유저 정보가 일치하는 경우 인증 정보가 담긴 쿠키를 전송한다.

 

 

API

Base URL : https://localhost:4000

  • HTTP Method: 'POST'
  • URL: {baseURL}/users/login
  • Body: application/json
//필수 매개변수
{
  "userId": string,
  "password": string
}

 

 

 

- 유저정보가 일치하는 경우 아래의 응답을 반환한다.

 

Response : 

  • Header : Set-Cookie: id=Number,Domain:id=NUMBER; Domain=STRING; Path=STRING; Secure; HttpOnly; SameSite='none';
  • Type: application/json

 

{
  data: null,	//별도의 데이터를 전송하지 않는다.
  message: string	//요청이 성공하는 경우 'ok'를 담고 있다.
}

Set-Cookie 대신 cookie-parser를 사용해서 인증정보가 담긴 쿠키를 전송했다.

(Express에서는 cookie-parser라는 middleware를 통해 cookie API를 제공한다.)

 

res.cookie(name, value, [options])

유저의 정보를 userInfo라고 할 때, id의 값을 userInfo.id로 하고, Header에 있는 나머지 옵션들을 넣어준 후 쿠키를 전송하도록 한다.

 

 

 

- 유저 정보가 일치하지 않는 경우  상태코드 400과 함께 아래의 응답을 반환한다.

{
  data: null,
  message: 'not authorized'
}

 

 

 

 

 

 


server/logout.js

 

1. 요청 쿠키에 인증정보가 존재하지 않는 경우 에러메세지를 반환한다.

2. 요청 쿠키에 인증정보가 존재하는 경우 쿠키를 제거하고 응답메세지를 반환한다.

 

 

API

 

Base URL: https://localhost:4000

 

  • HTTP Method: 'POST'
  • URL: {baseURL}/users/logout
  • Body: application/json

- 요청 쿠키에 인증정보가 존재하는 경우 쿠키를 삭제하고 아래와 같은 응답을 반환한다.

Response:

  • Type: application/json
{
  data: null,
  message: string	//요청이 성공하는 경우 'ok'를 담고 있다.
}

res.clearCookie(name, [options])

 

 

 

 

- 요청 쿠키에 인증정보가 존재하지 않는 경우 상태코드 400과 함께 아래와 같은 응답을 반환한다.

{
  data: null,
  message: 'not authorized'
}

 

 

 

 

 

 


server/userinfo.js

1. 쿠키에 인증 정보가 존재하지 않으면 에러 메세지를 반환한다.

2. 쿠키에 인증 정보가 존재하면 유저 정보를 리턴하고 응답메세지를 반환한다.

 

 

API

 

Base URL: https://localhost:4000

  • HTTP Method: 'GET'
  • URL: {baseURL}/users/userinfo

- 쿠키에 인증 정보가 존재하는 경우 아래와 같은 응답을 반환한다.

Response:

  • Type: application/json
{
  data: {
    id: `number`
    userId: 'string',
    email: 'string',
    createdAt: 'string',
    updatedAt: 'string',
  },
  message: 'string'	//요청이 성공하는 경우 'ok'를 담고 있다.
}

즉, req.cookies.is가 존재하는 경우 데이터베이스에서 id가 req.cookies.id와 같은 것을 찾아서 userinfo를 json 형식으로 전송한다.

 

 

- 쿠키에 인증 정보가 존재하지 않으면 상태코드 400과 함께 아래와 같은 응답을 반환한다.

{
  data: null,
  message: 'not authorized'
}

 

 

 

Client

로그아웃을 할 경우 로그인을 하는 페이지 Login.js가 렌더되고 로그인을 할 경우 Mypage.js로 이동한다.

 

components/Login.js

loginRequestHandler() 부분만 구현하면 된다.

 

 

1. 로그인 요청을 보낸다.

2. 로그인에 성공하면 props로 전달받은 함수를 호출해 로그인 상태를 변경하고 사용자 정보를 요청한다.

4. props로 전달받은 함수를 호출해 사용자 정보를 변경한다.

5. 에러핸들링을 한다.

 

 

*axios를 활용해서 비동기 통신을 한다.

axios.post( 'url', {data 객체}, [config])

- data객체에 state에 있는 username, password를 userId, password의 값으로 담는다.

- server의 https 서버에서 cors 설정을 해 주었으므로 config에 withCredentials 옵션을 true로 해 주어 자격증명을 한다.

 

요청이 성공하면 props로 전달받은 loginHandler()를 호출해서 로그인 상태를 업데이트하고 get 요청으로 사용자 정보를 요청한다.

axios.get( 'url', [config])

 

props로 전달받은 setUserInfo 함수를 호출해 사용자 정보를 변경한다.

마지막에 에러핸드링을 한다.

 

 


server/Mypage.js

 

handleLogout()을 구현한다.

 

1. 서버에 로그아웃 요청을 보낸다.

2. 요청에 성공하면 props로 전달받은 함수를 호출해 로그인 상태를 업데이트한다.

 

 

post 메서드로 서버에 로그아웃 요청을 보낸다.

요청에 성공하면 props로 전달 받은 logoutHandler()를 호출해서 로그인 상태를 업데이트하고 마지막에 에러핸들링을 한다.

 

 

 

 

 

 


세션 기반 인증  Session-Based Authentication

 

 

 

 

 

 

세션 인증방식으로 로그인/로그아웃 로직 구현하기 Pseudocode

 

  • HTTPS 서버에 (server/index.js)에 세션 객체를 만든다. (즉, 데이터를 서버에 저장한다.)
  • 로그인에 성공하면 세션 객체에 userId를 저장한다. 
  • 로그아웃을 하면 세션을 삭제한다.

 

 

쿠키와 다른 점을 중점적으로 기술한다.

HTTPS 서버를 만들 때 session middleware를 사용할 수 있다.

그리고 req.session을 통해 접근할 수 있다.

 

 

 


app.session(options) : 옵션들을 활용해 세션 미들웨어를 만들 수 있다. cookie 옵션을 사용하면 session ID 쿠키 객체를 만들 수 있다.

 

server

server/login.js

 

유저 정보가 존재할 경우 세션객체에 userId를 저장한다.

req.session.save(callback)

req.session.userId에 userInfo.userId를 할당한 후 json 형식으로 반환한다.

 

 

 

 


server/logout.js

 

로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단할 수 있다. 따라서 req.session.userId가 참이면 세션을 삭제하고 응답 메세지를 보낸다.

 

req.session.destroy()