인증(Authentication)이란?
인증은 회원가입과 로그인을 말한다. 사용자의 활동을 추적하기 위해 필요하다. 인증을 하기 위해서는 개인을 식별할 수 있는 정보(아이디, 이메일, 비번 등)가 필요한데, 가장 중요한 것은 비밀번호다. 비밀번호는 반드시 암호화 해서 저장해야 한다.
비밀번호 암호화에는 크게 두 가지 방법이 있다. 하나는 백엔드 단에서 작업해주는 해싱이고, 통신 시 개인정보를 주고받을 때 SSL을 적용해 암호화 하는 방식도 있다.(HTTPS) 이 방법은 따로 작업해주는 건 아니고 인증서를 통한 통신으로 주고받은 데이터를 알아보기 힘들게 하는 방식이다.
암호화는 어떻게 하는가?
단방향 해시 함수는 복원이 불가능하기 떄문에 암호학적 용도로 사용한다. MD5, SHA-1 방식은 보안 취약해 SHA-256, SHA-512 사용을 권장한다.
다만 해싱은 동일한 input 값이라면 항상 동일한 해시 값을 내놓기 때문에 보안에 취약하다. 레인보우 테이블은 이런 고정된 해시 값을 미리 모아놓은 것이다. 단순하게 해싱만 해준다면 이런 레인보우 테이블로 비밀번호 암호화가 풀릴 염려가 있기 때문에 salting과 key scretching으로 취약점을 보완해준다.
- salting: 임의의 값을 해시값 사이에 끼워넣음
- key stretching: 해싱을 여러 번 반복. salting으로도 충분하지 않아서 만들어짐.
bcrypt library는 솔팅과 키 스크래칭을 편하게 사용할 수 있게 해주는 라이브러리다. bcrypt로 암호화한 결과값(digest)은 아래 항목으로 구성된다.
- 알고리즘: 어떤 방식 사용할지 (sha-256 등)
- 알고리즘 옵션: 몇 번 회전했는지
- 솔트: 솔트한 값
- 해시드 패스워드: 해시값
인가란?
사용자가 서버에 로그인하면 해당 사용자가 맞는지 확인하는 과정이다. http 특성 때문에 인가 과정이 필요하다. https는 요청(request)이 들어오면 응답(response)하는 구조인데, 응답 이후 상태가 저장되지 않기 떄문에(stateless) 내가 나라는 걸 증명할 수단이 필요하다.
따라서 header의 메타 데이터를 보내서 로그인한 유저의 권한을 확인하는데, 이것도 쉽게 알아볼 수 없게 하기 위해 유저 정보를 JSON Web Token(JWT)를 통해서 암호화한 형태로 받는다. 아래 예시를 살펴보자.
header부분.payload부분.signature부분
- 헤더(header): JWT의 헤더는 타입과 알고리즘을 지정하고, BASE64 인코딩 되어 가장 맨 앞에 위치한다. 토큰 타입과 해시 알고리즘 종류 정보가 들어간다.
- 내용(payload): JWT의 페이로드는 공개 클레임과 비공개 클레임을 작성한뒤 BASE64인코딩하여 두번째 요소로 위치한다. 지금 로그인한 사람이 누구인지 정보가 들어가는데, 쉽게 노출되지 않도록 pk키 등 쉽게 식별할 수 없는 정보가 들어가야 한다. (유저 아이디 절대 안 됨)
- 서명(signature) : JWT의 시그니처는 인코드된 헤더와 페이로드 별도의 secret을 헤더에 지정된 알고리즘으로 암호화하여 전송되며, 복호화 가능하다. 프론트엔드에서 JWT를 백엔드 API 서버로 전송하면 서버에서는 전송받은 JWT의 서명 부분을 복호화하여 서버에서 생성한 JWT가 맞는지 확인한다. 계약서 위변조를 막기 위해 서로 사인하는 것과 같다.
header와 payload는 암호화가 아니라 단지 인코딩한 값일 뿐이므로 그 부분에 개인정보를 담으면 안 된다. 서명은 암호화가 되어있으며 복호화도 가능하다.
백엔드에서 토큰 값을 전달해주면 프론트엔드가 토큰 값을 스토리지에 담아놨다가 백엔드가 요청할 때마다 헤더에 담아서 전달한다.