개발 하나둘셋/CS

[네트워크] 회원가입, 로그인기능 (해시함수 / JWT)

유리코딩 2021. 11. 7. 22:15
반응형

개념정리

회원가입, 로그인기능 (해시함수 / JWT)


 

1. 해시함수(암호화 알고리즘)

  • 알고리즘의 한 종류로서 임의의 데이터를 입력 받아 항상 고정된 길이의 임의의 값으로 변환해주는 함수
  • 해시함수 SHA256은 어떤 길이의 입력값을 넣어도 항상 256바이트의 결과값이 나옴
  • 동일한 입력값은 항상 같은 결과값이 나오고, 입력값은 조금이라도 달라지면 완전히 다른 값이 나오게되며, 결과값을 통해 입력값을 알아내는 것이 불가능

 

[ 회원가입 API ]

  • post로 id, pw, nickname을 받아서, mongoDB에 저장
  • 저장하기 전에, pw를 sha256 방법(=단방향 암호화. 풀어볼 수 없음)으로 암호화해서 저장
@app.route('/api/register', methods=['POST'])
def api_register():
    id_receive = request.form['id_give']
    pw_receive = request.form['pw_give']
    nickname_receive = request.form['nickname_give']

    #저장하기 전 pw를 sha256 방법으로 암호화해서 저장
    pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()

	db.user.insert_one({'id': id_receive, 'pw': pw_hash, 'nick': nickname_receive})

    return jsonify({'result': 'success'})

 

2. JWT(JSON Web Tokene)

JSON 객체를 사용해 정보(토큰)를 안정성 있게 전달하는 웹표준. 사용자가 로그인하면 서버에서 회원임을 인증하는 토큰을 넘겨줌으로써 이후 회원만 접근할 수 있는 서비스 영역에서 신분을 확인하는 데 쓰임

 

[JWT 인증절차]

출처 : https://tansfil.tistory.com/58?category=255594

 

[ 로그인 API ]

  • id, pw를 받아서 맞춰보고, 토큰을 만들어 발급
@app.route('/')
def home():
	#아래 api_login()에서 만든 토큰을 발급받아 SECRET_KEY를 통해 decode(복호화)
    #토큰을 쿠키에 담아 발급
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        user_info = db.user.find_one({"id": payload['id']})
        return render_template('index.html', nickname=user_info["nick"])
    except jwt.ExpiredSignatureError:
        return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
    except jwt.exceptions.DecodeError:
        return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))

@app.route('/api/login', methods=['POST'])
def api_login():
    id_receive = request.form['id_give']
    pw_receive = request.form['pw_give']

    # 회원가입 때와 같은 방법으로 pw를 암호화합니다.
    pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()

    # id, 암호화된pw을 가지고 해당 유저를 찾습니다.
    result = db.user.find_one({'id': id_receive, 'pw': pw_hash})

    # 찾으면 JWT 토큰을 만들어 발급합니다.
    if result is not None:
        # JWT 토큰에는, payload와 시크릿키가 필요합니다.
        # 시크릿키가 있어야 토큰을 디코딩(=풀기) 해서 payload 값을 볼 수 있습니다.
        # 아래에선 id와 exp를 담았습니다. 즉, JWT 토큰을 풀면 유저ID 값을 알 수 있습니다.
        # exp에는 만료시간을 넣어줍니다. 만료시간이 지나면, 시크릿키로 토큰을 풀 때 만료되었다고 에러가 납니다.
        payload = {
            'id': id_receive,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
        }
        #SECRET_KEY는 상단에 선언
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

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

 

[ajax로 쿠키 받기]

쿠키의 개념

  • 로그인을 구현하면, 반드시 쿠키라는 개념을 사용
  • 페이지에 관계없이 브라우저에 임시로 저장되는 정보. 키:밸류 형태(딕셔너리 형태)로 저장
  • 쿠키가 있기 때문에, 한번 로그인하면 네이버에서 다시 로그인할 필요가 없음
  • 브라우저를 닫으면 자동 삭제되게 하거나, 일정 시간이 지나면 삭제되게 할 수 있음
function login() {
    $.ajax({
        type: "POST",
        url: "/api/login",
        data: {id_give: $('#userid').val(), pw_give: $('#userpw').val()},
        success: function (response) {
            if (response['result'] == 'success') {
                // 로그인이 정상적으로 되면, 서버에서 토큰을 받아옵니다.
                // 이 토큰을 mytoken이라는 키 값으로 쿠키에 저장합니다.
                $.cookie('mytoken', response['token']);

                alert('로그인 완료!')
                window.location.href = '/'
            } else {
                // 로그인이 안되면 에러메시지를 띄웁니다.
                alert(response['msg'])
            }
        }
    })
}

 

[추가 개념]

장점

1. 간편하다. JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이 때문에 별도의 저장소를 사용하지 않는 서버에서 유리하다. 이는 서버를 확장하거나 유지,보수하는데 유리하다.

2. 확장성이 뛰나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능합니다. Facebook 로그인, Google 로그인 등 토큰기반으로 하는 서비스에서 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다. 

 

단점

1. 이미 발급된 JWT에 대해서는 돌이킬 수 없다. JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능하기 때문에 악의적인 사용자는 유효기간이 지나기 전에 정보를 이용할 수 있다. 

-> 해결책

기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급하면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있다

 

2. Payload 정보가 제한적이다. Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없다.

 

3. JWT의 길이는 세션/쿠키 방식에 비해 길다. 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생한다.

 

 

 

Header 

Header를 복호화(Decoding)하면, type과 alg정보가 들어있다.

  - type : JWT (고정)

 - alg(방식) : verify signature 정보를 암호화 할 알고리즘 정보 (HS256 등)

 

Payload

Payload를 복호화(Decoding)하면, 서버에 보낼 정보들이 담겨있다. 

  - 누가 누구에게 발급했는지

  - 토큰의 유효기간

  - 어떤 정보를 보내줄 것인지(닉네임 등)

   

 Verify Signature

서버에 비밀 키 값(Secret Key)을 지정해두고, Header, Payload와 함께 알고리즘을 통해 암호화하면 Verify Signature(서명) 값이 나온다.

반응형