인증(Authentication)
- 유저의 identification을 확인하는 절차 (아이디, 비밀번호 확인)
- 우리 서비스를 누가, 어떻게 사용하는지 추적하기 위해 사용한다.
- 인증에는 아이디, 이메일 주소, 비밀번호* 등이 필요하다.
비밀번호 관리
- 비밀번호는 개인정보보호법에 따라 '암호화'해서 관리해주어야 한다. (법규상 강제)
- 비밀번호는 Database에 저장 시 개인정보를 해싱(난독화)하여 복원할 수 없도록 한다.
- 통신 시 개인 정보를 주고받을 때 SSL을 적용하여 암호화한다. (HTTPS 프로토콜)
암호화 방법
단방향 해쉬 함수(one-way hash fuction)
- 해시(hash)함수는 자료구조에서 빠른 자료와 검색, 데이터의 위변조 체크를 위해 쓰이며, 복원이 불가능해 암호학적 용도로 사용한다. (예: MD5, SHA-1, SHA-256)
- 단방향 해시 함수는 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest)를 생성한다. 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어서 단방향성(one-way) 이라고 한다.
- 예를 들어, "test password"를 hash256이라는 해쉬 함수를 사용하면
0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e
값이 나온다.
- 만일 "test password2"를 hash256 해쉬 함수를 사용하면
d34b32af5c7bc7f54153e2fdddf251550e7011e846b465e64207e8ccda4c1aeb
값이 나온다. 실제 비밀번호는 비슷하지만 해쉬 함수 값은 완전히 틀린것을 볼 수 있다. 이러한 효과를 avalance라고 하는데 비밀번호 해쉬 값을 해킹을 어렵게 만드는 하나의 요소이다.
- 결과만 보고 식별이 불가능하기 때문에 완벽해보이지만, 같은 알고리즘으로 해싱하면 같은 결과가 도출되기 때문에 위험할 수 있다. (Rainbow Table attack: 가능한 경우의 수를 모두 해시값으로 만들어 판매하는 서비스)
=> salting, Key Streching 개발됨
Salting & KeyStretching (소금치고 늘리기!)
- 솔팅(Salting)은 실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법이다.
- 입력한 비밀번호와 임의로 생성한 문자열을 합쳐서 해싱, 이 해싱값 결과를 저장한다.
- 비교를 위해 해시값과 솔트값을 같이 저장해야 한다.
- 키 스트레칭(Key Stretching)은 단방향 해쉬값을 계산한 후 그 해시값을 또 해쉬하는 반복 작업으로, 해커가 패스워드 무작위 대입을 통해 해시값을 계산하는데 필요한 시간을 대폭 늘려 원본 값을 유추하기 어렵게 만든다.
bcrypt
- Salting & Key Streching 개념들을 적용해주는 대표적 라이브러리
- 처음부터 비밀번호를 단방향 암호화하기 위해 만들어진 해쉬 함수로 hash 결과값에 솔트값, 해시값 및 반복횟수를 같이 보관하기 때문에 DB 설계가 편리하다는 장점이 있다.
로그인 절차
- 유저 아이디와 비밀번호 생성
POST /auth HTTP/1.1
Host: localhost:5000
Content-Type: application/json
{
"username": "joe",
"password": "pass"
}
- 유저 비밀번호 암호화 -> DB에 저장.
- 유저 로그인 -> 아이디와 비밀번호 입력
- 유저가 입력한 비밀번호 암호화 -> 암호화되서 DB에 저정된 유저 비밀번호와 비교
- 일치하면 로그인 성공
- 로그인에 성공하면
access token
을 클라이언트에게 전달
POST /auth HTTP/1.1
Host: localhost:5000
Content-Type: application/json
{
"username": "joe",
"password": "pass"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDQ0OTE3NjQwLCJuYmYiOjE0NDQ5MTc2NDAsImV4cCI6MTQ0NDkxNzk0MH0.KPmI6WSjRjlpzecPvs3q_T3cJQvAgJvaQAPtk1abC_E"
}
- 서버에서 access token을 복호화해 유저 정보를 얻는다. 유저는 로그인 성공후 다음부터는
access token
을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다.
(HTTP를 알고 주소 쓰기 참조)
{
user_id = 1
}
JSON Web Token(JWT)
- access token을 생성하는 널리 사용되는 기술중 하나로, 유저 정보를 담은 JSON 데이터를 암호화해서 클라이언트와 서버간에 주고 받는다.
JWT 구성
- 헤더(header): 토큰 타입의 해시 알고리즘 정보로 BASE64 방식으로 인코딩 되어 JWT의 가장 첫부분에 기록됨 (암호화 x) 예: {"alg":"HS256", "typ":"JWT"}
- 내용(payload): 내용에는 exp와 같이 만료시간을 나타내는 공개 클레임, 클라이언트와 서버간 협의하에 사용하는 비공개 클레임. 두가지 요소를 조합하여 작성한 뒤 BASE64 인코딩해 두번째 요소로 위치 예: {"user-id": 1, "exp":1539517391}
- 서명(signature): JWT가 원본 그대로라는 것을 확인.
BASE64URL 인코드된 header와 payload 그리고 JWT secret(별도 생성)을 헤더에 지정된 암호 알고리즘으로 암호화하여 전송 (복호화 가능)
- 프론트엔드가 JWT -> 백엔드 API 서버 전송 -> JWT 서명부분 복호화하여 서버 생성 유무 확인
=> header와 payload는 암호화가 아닌 BASE64 인코딩한 것이므로 누구나 원본을 볼 수 있으니 개인정보를 담아선 안된다.
Access token을 사용하는 이유
- 무거운 bcrypt call을 받지 않고, 간단한 해쉬 만으로 퍼포먼스를 낼 수 있다.
- Client-side storage방식
쿠키와 같이 실제 ID와 패스워드가 저장되지 않고, 다른 사이트에서 재사용할 수 없기 때문에 안전하다.
인가(Authorization)
- 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차다.
- 예를 들어, 해당 유저는 고객 정보를 볼 수 있는 있지만 수정할 수는 없다
- Authroization도 JWT를 통해서 구현 될 수 있다.
access token
을 통해 해당 유저 정보를 얻을 수 있음으로 해당 유저가 가지고 있는 권한(permission)도 확인 할 수 있다.
인가 절차
- Authentication 절차를 통해
access token
을 생성한다. access token
에는 유저 정보를 확인할 수 있는 정보가 들어가 있어야 한다 (예: user id).
- 유저가 request를 보낼때
access token
을 첨부해서 보낸다.
- 서버에서는 유저가 보낸
access token
을 복호화 한다.
- 복호화된 데이터를 통해 user id를 얻는다.
- user id를 사용해서 database에서 해당 유저의 권한(permission)을 확인하다.
- 유저가 충분한 권한을 가지고 있으면 해당 요청을 처리한다.
- 유저가 권한을 가지고 있지 않으면 Unauthorized Response(401) 혹은 다른 에러 코드를 보낸다.