[API]인증 & 인가(bcrypt & jwt)

박민하·2022년 6월 13일
0

DJANGO

목록 보기
8/22
post-thumbnail

✅ 인증(Authentication)

  Authentication은 유저의 identification을 확인하는 절차다. 인증을 하기 위해서는 유저의 ID, Email, Password 등이 필요할 텐데, email 같은 경우에는 이미 외부적으로 공개된 정보이므로 password가 가장 중요한 정보가 된다.

✔ 인증은 왜 필요할까?

  • 우리 서비스를 누가 쓰며 어떻게 사용하고 있는지 추적이 가능하도록 하기 위해

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

1. 유저의 비밀번호는 절대 비밀번호 그대로 DB에 저장 하지 않는다.

  • DB가 해킹을 당하면 유저의 비밀번호도 그대로 노출 된다.
  • 외부 해킹이 아니더라도 내부 개발자나 인력이 유저들의 비밀번호를 볼 수 있다.

2. 유저의 비밀번호는 꼭 암호화 해서 저장 해야 한다.

  • 그럼으로 DB가 해킹을 당해도 비밀번호가 그대로 노출되지 않으며 내부 인력도 비밀번호를 알 수가 없다.
  • DB에 저장 시 개인정보를 해싱하여 복원할 수 없도록 한다. 즉, 암호화해서 저장해서 쉽게 유추할 수 없게 한다.

3. 단방향 해시 함수(one-way hash function)

  • 비밀번호 암호에는 단방향 해시 함수(one-way hash function)가 일반적으로 쓰인다.
  • 단방향 해시 함수는 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest)를 생성한다. 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만, 암호화된 메시지로는 원본 메시지를 구할 수 없어서 단방향성(one-way) 이라고 한다.
  • 예를 들어, "test password"를 hash256이라는 해시 함수를 사용하면 0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e 값이 나온다.
  • 만일 "test password2"를 hash256 해시 함수를 사용하면 d34b32af5c7bc7f54153e2fdddf251550e7011e846b465e64207e8ccda4c1aeb 값이 나온다. 실제 비밀번호는 비슷하지만 해시 함수 값은 완전히 틀린것을 볼 수 있다. 이러한 효과를 avalance라고 하는데 비밀번호 해시 값을 해킹을 어렵게 만드는 하나의 요소이다.

✔ SSL

  통신 시 클라이언트와 서버가 개인정보를 주고받을 때는 SSL을 적용하여 암호화를 한다(HTTPS). 클라이언트가 서버에 데이터를 전송 하는 과정에도 암호화가 적용되며, 주는사람과 받는사람만 암호를 해독할 수 있다.

  결과만 보면 당장 식별이 불가능하므로 완벽해 보이지만, 알고리즘으로 해싱하면 항상 같은 값이 도출된다. 이런 허점을 이용해 가능한 경우의 수를 모아둔 Rainbow Table이라고 해시값을 유추해주는 사이트도 존재한다.

✔ Bcrypt

  단방향 해시 함수의 취약점들을 보안하기 위해 일반적으로 2가지 보완점들이 사용된다. SaltingKey Stretching을 구현한 해시 함수중 가장 널리 사용되는 것이 bcrypt 라이브러리다. bcrypt는 처음부터 비밀번호를 단방향 암호화 하기 위해 만들어진 해시함수 이다. 다양한 언어를 지원하고 있으며, 사용이 간편하여 쉽게 적용이 가능하다. bcrypt는 해시 결과값에 salt값과 해시값 및 반복횟수를 모두 같이 보관하기 때문에 직접 분석을 할 필요가 없어, 비밀번호 해싱을 적용하는데 있어 DB설계를 복잡하게 할 필요가 없다.

1. Salting

  실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법이다. 예를 들어, 1234를 입력하고 저장하는 순간에 랜덤 값을 붙어서 해싱하게 된다. 이렇게 하면 항상 다른 해시값이 나오게 된다.

  단방향 해시여서 복원이 안되는데(복호화 불가) 비밀번호는 어떻게 알까? 저장된 salt값과 함께 해싱해서 두 값을 비교해서 알아낸다. 물론 salt값을 붙여서 해싱한 것도 유추하는 사이트가 있다. 완벽한 보안은 없고 단지 시간을 벌어주는 것임을 항상 염두해 두자.

2. Key Stretching

  단방향 해시값을 계산 한 후 그 해시값을 또 해시 하고, 또 이를 반복하는 것을 말한다.
최근에는 일반적인 장비로 1초에 50억 개 이상의 다이제스트를 비교할 수 있지만, 키 스트레칭을 적용하여 동일한 장비에서 1초에 5번 정도만 비교할 수 있게 한다. GPU(Graphics Processing Unit)를 사용하더라도 수백에서 수천 번 정도만 비교할 수 있다. 50억 번과는 비교할 수도 없을 정도로 적은 횟수다. 앞으로 컴퓨터 성능이 더 향상되면 몇 번의 반복을 추가하여 보완할 수 있다.

✅ 인가(Authorization)

✔ 인가란?

  해당 유저가 request에 해당하는 권한이 있는지 확인하는 절차이다.

✔ JWT(JSON Web Tokens)

  HTTP request와 response가 있고, stateless한 성질을 가진다는 특징이 있다. 이 특징을 생각하면서 어떤 사이트에 로그인을 한다고 생각해보자. 로그인 이후 구매목록에 들어가면 다시 로그인하라는 메세지가 뜨게 될 것이다.

  서버는 일반적으로 사용자가 이미 로그인을 했을 경우에 다시 요구를 하면 안된다. 이런 문제를 해결하기 위해 headers에 메타데이터를 보내서 확인한다. 이 메타정보를 JSON Web Token(JWT), 즉 토큰을 주고받는다고 한다.

  유저가 로그인에 성공한 후에는 access token이라고 하는 암호화된 유저 정보를 첨부해서 request를 보내게 된다. 토큰은 이전 요청을 기억하는 긴 문자열(str)이다. 브라우저에서 토큰을 가지고 있다가 인가가 필요한 API 호출에서 토큰이랑 같이 요청을 보내면 로그인 기록을 기억하고 다시 로그인 재요청을 하는 일은 없게 된다.

  JWT는 단방향이 아니라서 열쇠로 잠그고 열 수 있어서 복호화 가능하다. 키만 있으면 열 수 있다.

  JWT의 구조는 헤더-내용-서명으로 구성된다.

1. 헤더(header)

  헤더(header)에는 토큰의 타입과 해시 알고리즘 정보가 들어간다. 헤더의 내용은 BASE64방식으로 인코딩해서 JWT의 가장 첫 부분에 기록된다.

예시) {"alg":"HS256","typ":"JWT"}

  인코딩은 암호화가 아니므로 개인정보를 담으면 안된다. 애초에 개인정보를 담고 있지 않으니 암호화할 필요가 없다.

2. 내용(payload)

  내용(payload)은 만료시간을 나타내는 exp와 같이 미리 정의된 집합인 registered claim 이고, 공개용 정보 전달을 목적으로 하는 public claim이며, 클라이언트와 서버간 협의하에 사용하는 private claim이다. 위의 세 가지 요소를 조합하여 작성한 뒤 BASE64 인코딩하여 두번째 요소로 위치한다. 여기에는 개인정보는 들어가지 않는다.

예시)  {"user-id":1, "exp":153917391}

  어느 DB에서도 적용되기 때문에 이 ID로 개인을 식별할 수 없다. 다른 DB에 같은 ID 값이 있더라도 같은 의미의 ID는 아니기 때문이다. 하지만 이런 ID같은 것으로도 개인을 식별 할 수 있다. 다음 서명(signature)으로 넘어가 확인해보자.

2. 서명(signature)

  서명(signature)은 JWT가 원본 그대로라는 것을 확인할 때 사용하는 부분이다. 이는 이 토큰이 우리 서버가 발급한 토큰이라는걸 알 수 있게 해준다.

  시크릿키(=서버를 인증해주는 키)를 기준으로 토큰을 만들어준다. 시그니처는 BASE64URL 인코딩된 header와 payload, JWT secret(별도 생성)을 헤더에 지정된 암호 알고리즘으로 암호화하여 전송한다(복호화 가능). 프론트가 JWT를 백엔드 API 서버로 전송하면 서버에서는 전송받은 JWT의 서병부분을 복호화하여 서버에서 생성한 JWT가 맞는지 확인한다. 마치 계약서의 위변조를 막기위해 서로 사인하는 것과 같다고 보면 된다.

  주의할 점은 header과 payload는 BASE64 인코딩한 것이므로 암호화는 되지 않는다(개인정보가 없기 떄문). 그래서 누구나 원본을 볼 수 있으니 개인정보를 담아서는 안된다.

profile
backend developer 🐌

0개의 댓글