[Session] 인증 / 인가

Danbi Cho·2020년 5월 17일
0

Session

목록 보기
7/9

인증(Authentication) & 인가(Authorization)

  • 인증과 인가는 API에서 자주 구현되는 기능중 하나.
  • Private한 API, Public한 API 둘 다기본적인 인증과 인가를 요구한다.

인증

  • Authentication은 유저의 identification을 확인하는 절차다.
  • 유저의 아이디와 비번을 확인하는 절차를 말한다.
  • 인증을 하기 위해서 먼저 유저의 아이디와 비번을 생성할 수 있는 기능도 필요하다.

인증은 왜 필요할까?

  • 우리 서비스를 누가 쓰는지, 어떻게 사용하는지에 대해 추적이 가능하도록 하기 위해 필요하다.

로그인 절차

  1. 유저 아이디와 비밀번호 생성
  2. 유저 비밀번호를 암호화 하여 DB에 저장
  3. 유저 로그인 -> 아이디와 비밀번호 입력
  4. 유저가 입력한 비밀번호 암호화 한 후 암호화 되서 DB에 저장된 유저 비밀번호과 비교
  5. 일치하면 로그인 성공
  6. 로그인 성공하면 access token을 클라이언트에 전송
  7. 유저는 로그인 성공 후 그 다음 부터 access token을 첨부하여 request를 서버에 전송한다.
    -> 매번 로그인 해도 되지 않도록 하기 위해

비밀번호 어떻게 관리해야하는가?

  • 법규상의 강제 : 유저의 비밀번호는 절대 비밀번호 그대로 DB에 저장 하지 않는다.
  • 데이터베이스에 저장시 개인정보를 해싱하여 복원할 수 없도록 한다.
  • 통신시 개인정보를 주고받을때 SSL을 적용하여 암호화(HTTPS)한다.
  • 비밀번호 암호에는 단방향 해쉬 함수(one-way hash function)가 일반적으로 쓰인다.

암호화는 어떻게 할까요?

단방향 해쉬란?

  • 본래 해쉬 함수는 자료구조에서 빠른 질의 검색.
  • 데이터의 위변조 체크를 위해서 쓰이지만 복원이 불가능한 단방향 해쉬하수는 암호학적 용도로 사용한다.
  • 단방향 해시 함수는 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest)를 생성한다.
    원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어서 단방향성(one-way) 이라고 한다.
In [21]: import hashlib

    In [22]: m = hashlib.sha256()

    In [23]: m.update(b"test password")

    In [24]: m.hexdigest()
    Out[24]: '0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e'

    In [25]: m = hashlib.sha256()

    In [26]: m.update(b"test password2")

    In [27]: m.hexdigest()
    Out[27]: 'd34b32af5c7bc7f54153e2fdddf251550e7011e846b465e64207e8ccda4c1aeb'

📌 단방향 해쉬 함수의 취약점들을 보안하기 위해 일반적으로 2가지 보완점들이 사용된다.

salting & keyStretching?

  • 단순 해쉬값이 해킹에 쉽게 노출되기 때문에 salting이 생겨났다.

Salting

실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법.

Key Stretching

  • 단방향 해쉬값을 계산 한 후 그 해쉬값을 또 또 해쉬 하고, 또 이를 반복하는 것을 말한다.

bcrypt

  • salting & key stretching 대표적 라이브러리

  • bcrypt는 처음부터 비밀번호를 단방향 암호화 하기 위해 만들이전 해쉬함수 이다.

  • 단방향 해쉬 함수도 몇가지 취약점이 있다.

  • Rainbow table attack - 미리 해쉬값들을 계산해 놓은 테이블을 Rainbow table이라고 한다.

  • 해시 함수는 원래 패스워드를 저장하기 위해서 설계된 것이 아니라 짧은 시간에 데이터를 검색하기 위해 설계된 것. 그렇기 때문에 해시 함수는 본래 처리 속도가 최대한 빠르도록 설계되었다. 이러한 속성 때문에 공격자는 매우 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있다(MD5를 사용한 경우 일반적인 장비를 이용하여 1초당 56억 개의 다이제스트를 대입할 수 있다). 이런 방식으로 패스워드를 추측하면 패스워드가 충분히 길거나 복잡하지 않은 경우에는 그리 긴 시간이 걸리지 않는다 (대부분 사용자의 패스워드는 길거나 복잡하지 않을 뿐 아니라, 동일한 패스워드를 사용하는 경우도 많다).

In [40]: import bcrypt

In [41]: bcrypt.hashpw(b"secrete password", bcrypt.gensalt())
Out[41]: b'$2b$12$.XIJKgAepSrI5ghrJUaJa.ogLHJHLyY8ikIC.7gDoUMkaMfzNhGo6'

In [42]: bcrypt.hashpw(b"secrete password", bcrypt.gensalt()).hex()
Out[42]: '243262243132242e6b426f39757a69666e344f563852694a43666b5165445469397448446c4d366635613542396847366d5132446d62744b70357353'

인가

  • Authorization은 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차 이다. (사용자가 서버에 로그인 하면 사용자가 맞는지 확인하는 과정)
  • http의 특징 :
    바로 request / responese 요청과 응답
    stateless한 성질(저장하지 않는 성질)
  • access token을 통해 해당 유저 정보를 얻을 수 있음으로 해당 유저가 가지고 있는 권한(permission)도 확인 할 수 있다.

📌 위 특징 때문에 서버는 사용자가 로그인 했을 경우, 로그인 했다는 것을 어떻게 알 수 있을까요?

  • 메타 헤더에 저장하여 보냄

JWT (JSON Web Token)

  • 유저가 로그인에 성공한 후에는 access token이라고 하는 암호화된 유저 정보를 첨부해서 request를 보내게 된다.

유저 로그인

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"
}

/* access_token 복호화한 것
{
    user_id = 1 
} */
  • 그러면 서버에서는 access token을 복호화 해서 해당 유저 정보를 얻게 된다.

헤더.내용.서명 (JWT구조)

  • 해더: 토큰의 타입과 해시알고리즘 정보가 들어간다
    내용에는 exp와 같이 만료시간을 나타내는 공개 클레임
    클라이언트와 서버간 협의하에 사용하는 비공개 클레임

  • 서명: JWT가 원본 그대로라는 것을 확인할 때 사용하는 부분

Authorization 절차

  1. Authentication 절차를 통해 access token을 생성한다.
    access token에는 유저 정보를 확인할 수 있는 정보가 들어가 있어야 한다
    (예를 들어 user id).
  2. 유저가 request를 보낼때 access token을 첨부해서 보낸다.
  3. 서버에서는 유저가 보낸 access token을 복호화 한다.
  4. 복호화된 데이터를 통해 user id를 얻는다.
  5. user id를 사용해서 database에서 해당 유저의 권한(permission)을 확인하다.
  6. 유저가 충분한 권한을 가지고 있으면 해당 요청을 처리한다.
  7. 유저가 권한을 가지고 있지 않으면 Unauthorized Response(401) 혹은 다른 에러 코드를 보낸다.
profile
룰루랄라! 개발자 되고 싶어요🙈

0개의 댓글