쿠키, 세션, 토큰, JWT

2022. 1. 13. 15:52기타 공부들

반응형

쿠키, 세션, 토큰에 대한 이해

참고: https://www.youtube.com/watch?v=tosLBcAX1vk(노마드코더)

참고: https://velopert.com/2350(벨로퍼트)

Auth(인증)을 통해 서비스는 유저를 검증할 수 있는데, 이때 유저 인증을 위해서 JWT혹은 세션을 사용할 수 있다. 이때 등장하는 개념이 쿠키, 세션, 토큰, JWT인데 개념을 잡아보자.

🍪 쿠키

사용자에 관한 정보를 기억하기 위해 서버는 쿠키를 이용해 브라우저에 데이터를 넣을 수 있다. 사이트에 방문하면 브라우저는 서버에 요청을 보내고, 서버는 요청에 응답한다. 이때 서버가 응답해서 보내주는 데이터에는

  1. 방문한 페이지에 관한 정보
  2. 서버가 브라우저에 저장하고자 하는 쿠키
  3. 기타정보

브라우저에 쿠키를 저장한 후에는 해당 웹사이트를 방문할 때마다 브라우저는 해당 쿠키도 요청과 함께 보내게 된다.

쿠키의 특징

  1. 도메인에 따라 제한이 됨. 유튜브가 준 쿠키는 유튜브에만 보내지게 된다.
  2. 쿠키는 유효기간이 있어서 서버가 정한 기간에 따라 유효하다.

쿠키는 인증 뿐만 아니라, 여러가지 정보를 저장할 수 있는데

예를들어 웹사이트 언어 설정을 바꾸면 브라우저에 쿠키를 주고 선택한 언어를 저장해둔다. 다음에 해당 웹사이트에 방문하면 쿠키는 요청과 함께 서버로 보내지고 서버는 해당 쿠키가 기억해둔 언어를 제공한다.

세션

기본적으로 왜 세션과 토큰이 필요할까?

이유는 HTTP(인터넷에서 데이터를 주고받을 수 있는 프로토콜)가 stateless이기 때문이다.

stateless란, 서버로 가는 모든 요청(request)이 이전 request와 독립적으로 다뤄진다는 뜻. 즉, 메모리가 없기 때문에 해당 요청이 끝나면 서버는 너가 누군지 잊어버리게된다.요청이 끝나면 서버는 우리가 누군지 잊어버리기 때문에 요청할 때마다, 우리가 누군지 알려줘야하고 이걸 알려주는 방식 중 하나가 바로 세션(session)이다.

세션은 서버 기반의 인증 방식으로, 서버 측에서 사용자들의 정보를 기억하고 있어야 한다. 사용자들의 정보를 기억하기 위해서는 세션을 유지해야 하는데, 메모리나 디스크 또는 데이터베이스 등을 통해 관리한다. 서버 기반의 인증 시스템은 클라이언트로부터 요청을 받으면, 클라이언트의 상태를 계속해서 유지하고 이 정보를 서비스에 이용하는데, 이러한 서버를 Sateful 서버라고 한다.

따라서 같은 웹사이트의 다른 페이지로 이동하면 브라우저는 세션ID를 갖고있는 쿠키를 (자동으로)서버에 보내게되고 서버는 들어오는 쿠키를 보고,세션 ID와 함께 오는 쿠키를 확인하게 됨. 유저가 들어올 때 마다 서버에서 확인해줘야하기 때문에, 유저가 늘어남에 따라 DB 리소스가 더 필요하게된다.

 

토큰

쿠키는 브라우저에만 있기 때문에, 네이티브 앱에서는 쿠키를 주고받는 방식을 사용할 수 없다.

이 경우에 ‘토큰’을 사용하고 서버에 토큰을 보내게 된다.

토큰은 그냥 이상하게 생긴 string인데, 서버는 세션 DB에서 해당 토큰과 일치하는 유저를 찾는다.

토큰은 인증정보를 다른 어플리케이션으로 전달해 페이스북/구글 같은 소셜 계정들을 이용하여 다른 웹서비스에서도 로그인 할 수 있게된다.대표적인 예제로는, [OAuth](<https://d2.naver.com/helloworld/24942>) 가 있습니다.

 

JWT(JSON Web Token) 토큰 인증방식이란?

참고:[JWT] JSON Web Token 소개 및 구조

JWT는 하나의 토큰 형식이고 JWT로 유저 인증을 처리하면 서버에 세션 DB를 가지고 있을 필요가 없고 서버는 유저 인증을 위해 많은 일을 하지 않아도 된다.

JWT와 세션의 차이점

세션(넷플릭스)장단점

장점

세션을 사용하면 서버는 로그인 된 유저의 모든 정보를 세션DB에 저장함. 해당 정보를 이용하면, 유저 계정을 활용한새로운 기능들을 추가할 수 있게됨.

 

단점

  1. 세션DB를 위해 DB를 사고, 유지해야함. 유저가 늘어나면 DB도 커져야함(비용적인 이슈) 보통 이를 위해 redis DB를 사용함.
  2. 확장성: 세션을 사용하면서 분산된 시스템을 설계하는건 불가능한것은 아니지만 과정이 매우 복잡해집니다.
  3. CORS: 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어있습니다. 따라서 쿠키를 여러 도메인에서 관리하는것은 번거롭습니다.(네이티브 앱에서는 사용불가)

JWT(QR체크인)장단점

장점

1)JWT에서는 DB를 따로 구축할 필요가 없음.

2)확장성: 토큰을 사용하여 다른 서비스에서도 권한을 공유 할 수 있습니다.(구글, 페북 등) 토큰 기반 시스템에서는, 토큰에 선택적인 권한만 부여하여 발급을 할 수 있습니다 (예를들어서 로켓펀치에서 페이스북 계정으로 로그인을 했다면, 프로필 정보를 가져오는 권한은 있어도, 포스트를 작성 할 수 있는 권한은 없죠)

  1. 토큰을 사용한다면, 그 어떤 디바이스에서도, 그 어떤 도메인에서도, 토큰만 유효하다면 요청이 정상적으로 처리

단점

서버는 생성된 토큰을 추적하지 않고 서버가 아는 것은 토큰이 유효한가 여부 밖에 없기때문에 기능을 추가할 수는 없음.

 

JWT 토큰 사용법

패스워드의 해싱과 JWT토큰을 헷갈리지 말자

hashlib - 문자열 해싱

회원가입(sign up)

@app.route('/sign_up/save', methods=['POST'])
def sign_up():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    useremail_receive = request.form['email_give']
    
		# 패스워드 암호화
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    
		doc = {
        "username": username_receive,                               # 아이디
        "password": password_hash,                                  # 비밀번호
        "useremail": useremail_receive                              # 이메일

    }
    db.users.insert_one(doc)
    return jsonify({'result': 'success'})

hashlib.sha256() 로 문자열이 해싱(암호화)된다.

문자열은 바이트 문자열이어야 하므로 .encode('utf-8') 과 같이 유니코드 문자열을 UTF-8 형식의 바이트 문자열로 변환한다.

문자열을 해싱하였다면 digest 또는 hexdigest 함수를 사용하여 해싱된 문자열을 얻을 수 있다.

digest()는 해싱된 바이트 문자열을 리턴하고 hexgigest()는 바이트 문자열을 16진수로 변환한 문자열을 리턴한다.

 

 

로그인**(sign in)**

'password': pw_hash로 확인한다.

@app.route('/sign_in', methods=['POST'])
def sign_in():
    # 로그인
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    # print(username_receive, password_receive)

    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    # 매칭되는 id와 pw값이 있는지 확인
    result = db.users.find_one({'username': username_receive, 'password': pw_hash})
    # 만약 result가 있다면
    if result is not None:
        payload = {
         'id': username_receive,
         'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

        return jsonify({'result': 'success', 'token': token})
    # 찾지 못하면
    else:
        return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})

 

 

결론

쿠키 = 그냥 옮기는 시스템. 매개체 토큰 = 서버가 기억하는 이상하게 생긴 텍스트 ID카드처럼 서버에게 보여줘야 하는 것 JWT = 정보를 갖고있는 토큰. DB없이 검증할 수 있음.

추가한 부분

- 이메일 중복확인

 

이메일 중복확인 코드와 아이디 중복확인 코드를 하나로 줄일 수 있을지 테스트 해볼 것

- 비밀번호 정규표현식

  1. 이전(영문 숫자 조합 8-20자. 특수문자(!@#$%^&*)도 사용 가능)
    function is_password(asValue) {
        var regExp = /^(?=.*\\d)(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
        return regExp.test(asValue);
    }
    ​
  2. 이후(영문,숫자,하나 이상의 특수문자 조합의 8-20자의 비밀번호)
    function is_password(asValue) {
        var regExp = /^(?=.*\\d)(?=.*[!@#$%^&*])(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
        return regExp.test(asValue);
    }
    ​
  1. 참고자료
    1. regex 내용 정리된 영상: https://www.youtube.com/watch?v=t3M6toIflyQ&t=1s
    2. 연습 사이트: https://regexr.com/5mhou
반응형