26. TIL (인증&인가)

dream.log·2021년 7월 26일
0

TIL

목록 보기
24/42
post-thumbnail

1. 인증은 무엇이고 왜? 하는가?

: 아이디와 비밀번호(회원가입), 로그인의 과정을 통해 회원을 식별하는 것

  • 인증(Authentication) : 회원가입, 로그인 등의 행위
    • 우리 서비스를 누가 쓰는지? 어떻게 사용하는지? 추적이 가능하도록 하기 위해 필요!
    • 인증에 필요한 것은 아이디, 이메일주소, 비밀번호가 있다!
    • 비밀번호는 중요한 정보이기 때문에 따로 관리를 해야한다! 개인정보 암호화는 법적 사항이므로 꼭 지켜야한다.

2. 그렇다면! 비밀번호는 어떻게 관리해야 하는가?

  • 데이터베이스에 저장 시 개인정보를 해싱하여 복원할 수 없도록 함.

    • hashing : 난독화
  • 통신 시 개인정보를 주고받을 때 SSL을 적용하여 암호화(HTTPS)
    SSL : 사이트 주소바 왼쪽의 자물쇠로 잠겨있는 것으로 확인할 수 있음.

    ✅ SSL?
    : 보안 소켓 계층(Secure Sockets Layer, SSL)
    웹사이트와 브라우저(혹은, 두 서버) 사이에 전송된 데이터를 암호화하여 인터넷 연결을 보안을 유지하는 표준 기술.

3. 암호화는 어떻게 해야하는가?

  • 단방향 해쉬(one-way-hash-function)란?
    : 컴퓨터 자료구조중 하나로 빨리 검색하고 찾기 위해서 만들어낸 함수이다.
    input - output이 한개씩 있다. 하나의 값이 들어가면 한개가 나온다.

    • 단방향 해시 함수는 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest) 를 생성한다. 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없기에 단방향성을 띄고 있다.

    • 자료구조에서 빠른 자료의 검색, 데이터의 위변조 체크를 위해 쓰지만 복원이 불가능한 단방향 해쉬함수는 암호학적 용도로 사용한다.
      ex) MD5,SHA-1,SHA-256

but!!
결과만 봐서는 당장 식별이 불가능해보이나, 다시 해싱하면 같은 결과가 나오는 단점이 있다.

  • Rainbow Table = 허점을 통해 가능한 경우의 수를 해시값으로 만든 것.
    이러한 허점을 보완하고자 salting과 key stretching을 사용함. 비밀번호와 임의로 생성한 문자열을 합쳐서 사용한다.

4. Salting & Key Stretching

: 해쉬값이 해킹에 쉽게 노출되어 salting이라는 개념이 생겨남
입력한 비밀번호와 임의로 생성한 문자열을 합쳐서 해싱해서 해시값을 저장하는 방법

  • Salting : 실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법
  • Key Stretching : 단방향 해쉬값을 계산한 후 그 해쉬값을 또 해쉬 - 또 해쉬. 반복하는 것을 말한다.
    • 해시값과 소금값(salt)을 같이 저장해야한다.
    • 해커가 패스워드 무작위 대입을 통해 해시값을 계산하는데 필요한 시간을 늘리기 위해 salting 및 hashing을 여러번 반복해 원본값을 유추하기 어렵게 만드는 것이 Key Stretching이다!

5. bcrypt

  • salting & key stretching의 대표적 라이브러리. 실제로 적용을하기 편하게 도와준다!
    • 다양한 언어를 지원하며 사용이 간편함
    • bcrypt는 hash 결과값에 소금값과 해시값 및 반복횟수를 같이 보관하기에 해싱 적용해주는 데 있어 DB 설계를 복잡하게 할 필요가 없다. 비밀번호 해싱이 간단해진다!
      (설계를 복잡하게 할 필요가 없어짐)
      결과 값(Digest)의 구조 : 해쉬 패스워드가 읽혀도 솔팅이 읽혀지기엔 시간을 벌 수 있다

1) bcrypt의 암호화 방법
: str 데이터가 아닌 Bytes 데이터를 암호화한다!

💌 bcrypt 암호화 방식 코드로 알아보기 💌

password = '2345'

#패스워드를 utf-8로 bcrypt하고 salting 한다.
hashed_password= bcrypt.hashpw(password.encode('utf-8',bcrypt.gensalt()))
print(hashed_password) #암호화된 값 출력
type(hashed_password) #'byte'

# 비밀번호 확인하기.
new_password = '2345'
bcrypt.checkpw(new_password.encode('utf-8'),hashed_password)
# checkpw를 통해 인코딩된 새로운 비밀번호와 hashed_password가 일치하는지 검증한다

bcrypt.hashpw (사전 정의한 비밀번호. encode('utf-8',bcrypt.gensalt()))
: 정의해 둔 비밀번호를 utf-8 형태로 encode화시킨다. gensalt => salting 작업을 해준다!

bcrypt.checkpw(비교할 비밀번호 1,비교할 비밀번호 2)
: encoding한 값과 원래 값이 같은가?
login하기 위해 입력한 값과 암호화된 비밀번호가 같은가? 비교하기 위해 사용해준다.

6. 인가

  • 사용자가 서버에 로그인하면 해당 사용자가 맞는지 확인하는 과정
  • 권한이 있는 유저인지 확인하기 위해 토큰을 사용하여 확인한다.
    • http 특징 : request - response, stateless한 성질 (단기기억 - 로그인 정보가 남지 않음)
  • JWT : Json Web Token.
    서버는 사용자가 로그인 했을 때 meta-data를 headers에 보내서 확인한다.
  • Token : 사용자 정보를 가지고 있는 정보뭉치 같은 것
    토큰은 로그인이 성공됐을 때 생성된다. 로그인이 선행되어야하는 다른 기능을 요청할 때 토큰이 다시 사용된다.

7. JWT (Json Web Token)

: 유저가 로그인에 성공한 후, access token 이라는 암호화된 유저 정보를 첨부해 request를 보낸다. access token을 생성하는 널리 사용되는 기술이 JWT!

  • JWT = header , payload, signature 로 구성됨!
    : JWT는 . 을 구분자로 [ header.payload.signature ] 로 구성되어 있다.

1) header에는 어떤 정보가 들어가는가?

  • 토큰의 타입(jwt)과 해시 알고리즘이 들어간다.
  • header의 내용은 BASE64 방식으로 인코딩해 jwt 가장 첫부분에 기록된다.
  • encode란? 알아볼 수 없게 단순한 바이트타입으로 변환하는 것. (디코딩하면 정보가 원래대로 돌아와 암호화랑은 다르다)

2) Payload (내용)

  • 실제로 담고싶은 내용을 담음
  • exp와 같이 만료시간을 나타내는 공개클레임 (정보를 계속 가지고 있을 순 없으니!)
  • 클라이언트와 서버간 협의하에 사용하는 비공개 클레임. 두가지 요소를 조합하여 작성한 후 base64 인코딩해 두번째 요소로 위치한다.
  • 실제 개인정보를 넣는 것이 아님! 암호화된 정보를 넣어야한다

3) Signature: 서명

  • jwt가 원본 그대로라는 것을 확인할 때 사용하는 부분이다.

  • Signiture는 BASE64URL 인코드 된 header와 payloda 그리고 jwt secret 별도 생성을 통해 헤더에 지정된 암호 알고리즘을 암호화하여 전송한다. (복호화 가능)

  • 프론트엔드가 jwt를 백엔드 api서버로 전송하면 서버에서는 전송받은 jwt의 서명부분을 복호화여 서버에서 생성한 jwt가 맞는지 확인한다.

  • 계약서의 위변조를 막기 위해 서로 사인하는 것과 같은 개념.

  • header와 payload에는 개인정보를 적으면 보이기 때문에 절대 안됨!

💌 JWT 알아보기 💌

jwt의 결과물은 pyjwt ver이 1.7이면 bytes. 2.0이상이면 str로 출력된다.

#토큰 만들기

SECRET = '랜덤한 조합의 키가 등장!'
access_token = jwt.encode({'id':1}, SECRET, algorithm = 'HS526')
# 인증을 위한 매개체를 만들기 위해 jwt.encode를 실행한다. id값 1번의, 랜덤 비밀번호를, 알고리즘을 활용해서.
print(access_token) #암호화 된 값 등장

#토큰 발행 결과 확인하기

header = jwt.decode(access_token, SECRET, algorithm = 'HS256')
# 헤더에 있는 access_token의 값을 decode한다. SECRET 비밀번호와 알고리즘을 활용해서
print(header) # => {'id' :1} 출력

✅ 토큰을 만들기 위해 특정 아이디를 encode한다.

✅ 로그인이 성공하면, 이 토큰을 프론트엔드 엔지니어에게 전달하고, 로그인 성공 시 발행 토큰을 전달해준다. 인증을 통과한 사용자만 접근하려면 토큰을 다시 받아 우리의 토큰이 맞는지 확인한다!
: token = jwt.encode (data, secret, algorithm)

✅ 인증하는 코드는 엔드포인터에 데코레이터를 구현해야하며, user app에 utils.py를 만들어 작성해준다.


⭐️ 유저 로그인 - access token - 복호화 과정⭐️

#유저의 로그인 시도
POST /auth HTTP/1.1
Host: localhost:5000
Content-Type: application/json

{
    "username": "joe",
    "password": "pass"
}

# 정상 로그인. access_token 발급
HTTP/1.1 200 OK
Content-Type: application/json

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDQ0OTE3NjQwLCJuYmYiOjE0NDQ5MTc2NDAsImV4cCI6MTQ0NDkxNzk0MH0.KPmI6WSjRjlpzecPvs3q_T3cJQvAgJvaQAPtk1abC_E"
}

#복호화 결과값
{
    user_id = 1 
}

- QUIZ TIME -

✅ 양방향 해쉬 : 원본을 확인해야 하는 정보에 사용함
ex) 주민번호 : 복호화하여 집어넣고 식별과정 시 꺼내서 복호화해서 확인

✅ 토큰을 타인이 뺏어서 쓰는 것을 방지하기 위해서는?
: ip 정보를 같이 저장하거나, 추가 검증과정을 하나 더 거치거나, 하는 등의 방식이 있다. 이러한 방식들도 공부해보면 좋다!

✅ 데이터베이스에는 암호화된 bcrypt 형태로 저장된다.
✅ salt값은 일정하지 않고 랜덤하다. (길이,값)
✅ password의 charfield값은 넉넉하게 주어야한다.
key-stretching 이후 변환된 값의 길이가 최대 길이를 넘어가도 오류가 발생함

profile
한 걸음, 한 걸음 포기하지 않고 발전하는 Backend-developer 👩🏻‍💻 노션 페이지를 통한 취업 준비 기록과 회고를 진행하고 있습니다. 계획과 기록의 힘을 믿고, 실천하고자 합니다.

0개의 댓글