사용자 식별
회원가입, 로그인을 말한다. 우리 서비스를 누가, 어떻게 사용하는지 추적 가능하도록 하기 위해 필요하다.
인증 로직을 관리할 때 비밀번호 저장은 개인정보 보호법에 따라 암호화를 해야 한다. 서버에 인증서를 넣어놓으면 주소를 호출할 때 인증서를 읽고, https 통신 상태가 된다. 외부에서 통신을 가로채도 어떤 단어인지 쉽게 알 수 없게 된다.
비밀번호, 바이오정보, 주민등록번호 등과 같은 주요개인정보가 암호화되지 않고 개인정보처리 시스템에 저장되거나 네트워크를 통해 전송될 경우, 노출 및 위변조 등의 위험이 있으므로 암호화 등의 안전한 보호 조치가 제공되어야 한다.
암호화는 개인정보가 비인가자에게 유노출되더라도 그 내용 확인을 어렵게 하는 보안기술이다.
시스템이 인터넷에서 격리된 네트워크에 위치하는 경우나 예외적인 개인정보 항목을 다루는 경우를 제외하고는
국가에서 권고하는 상용 암호화 알고리즘을 이용해 개인정보를 암호화하도록 법적으로 요구
하고 있다.
정리해보면
전송 계층 보안(영어: Transport Layer Security, TLS, 과거 명칭: 보안 소켓 레이어Secure Sockets Layer, SSL) 컴퓨터 네트워크에 통신 보안을 제공하기 위해 설계된 암호 규약이다. 이 규약은 인터넷 같이 TCP/IP 네트워크를 사용하는 통신에 적용되며, 통신 과정에서 전송계층 종단간 보안과 데이터 무결성을 확보해준다.
네이버 메인 페이지에 접속해보면 HTTPS로 시작하는 것을 알 수 있다.
hash 함수는 본래 자료 구조에서 빠른 자료 검색, 데이터의 위변조 체크를 위해 쓰지만, 복원이 불가능한 단방향 hash 함수는 암호학적 용도로도 사용한다. hashing하면 난수화된 문자열을 받고,
같은 언어로 같은 내용을 hashing하면(=원본이 같으면) 항상 같은 결과값이 나온다.
이렇게 비교 검증에 쓰이는 해시값을 특히 Digest
라고도 부른다.
hashing하기 위한 MD5, SAH-1, SHA-256 등 여러 보안 알고리즘이 있다. 앞의 두 개의 경우 보안이 취약하다고 한다. hash 값들을 다 돌리고 매칭해서 결과값을 유추하는 rainbow table attack 등의 해킹 방식이 있기 때문에, 추가적으로 암호화가 필요하다.
TMI: 특정 길이의 문자열들에 대해서 모든 해시값을 계산해서 판매하는 사이트도 있다고 한다 🤔 안 착하게 돈 버는 방법은 참 가지가지다...
원본 문자열에 random한 문자열을 합쳐서 저장하는 방식이다. 입력한 비밀번호와 salt값(임의로 생성한 문자열)을 합쳐서 -> 해싱하고 -> 해시값을 저장하는 방식이다. 이 때 해시값과 salt값을 함께 저장해야 한다.
hashing, salting을 10 번정도 반복해 원본 값을 유추하기 힘들게 만드는 것이다. 반복해서 hashing값이 더 복잡해지게 만들면 그만큼 난수화가 되고, 원본을 찾는 것이 굉장히 어려워진다.
salting & key stretching을 통해 암호화해주는 해싱 라이브러리. 다양한 언어를 지원한다. hash 결과값에 salt, hash, 반복 횟수를 같이 보관하기 때문에 DB 설계를 복잡하게 할 필요가 없다.
bcrypt 를 통해 해싱된 결과값(Digest)의 구조는 다음과 같다. (이미지 출처:코드플로우)
사용자 확인
사용자가 서버에 로그인하면 해당 사용자가 맞는지 보는 과정이다. http 통신은
위와 같은 성격을 갖고 있기 때문에 정보를 지속적으로 주고받지 않으면 확인할 수 없다. 그래서 서버는 사용자가 로그인했을 때, headers에 메타 데이터를 보내 로그인했다는 것을 확인한다.
요 메타 정보를 JSON Web Token, 즉, JWT라고 한다.
배운 개념으로 보기 전에 공식 문서를 살펴보자.
JWT는 당사자간 안전하게 정보를 JSON 객체로 전송하기 위한 오픈 스탠다드이다. json web token이 쓰이는 것은 보통 두 가지 경우다
- 인가: 가장 많이 쓰인다. 유저가 로그인을 하면, 각각의 후속 요청은 JWT를 포함해 토큰과 함께 유저의 동선, 서비스, 자원에 대한 접근이 허용된다. Single Sign On이 JWT를 요즘 많이 쓰는 기능이다.
- 정보 교환: 당사자 간 정보를 안전하게 교환하는 데 좋은 방법이다. JWT는 public / private key pairs를 통해 사인될 수 있기 때문에, 보내는 사람이 누군지 알 수 있다. 또 signature는 header와 payload를 사용해서 계산되기 때문에, 내용물이 보이지 않는 것을 보장할 수 있다.
사용자가 맞다는 것을 확인하기 위한 인증서라고도 볼 수 있다. 프론트에 JWT를 만들어서 전달해주면, 프론트엔드에서 JWT를 가지고 있으면서 로그아웃 하기 전까지는 매번 상황이 생길 때마다 JWT를 전달한다.
JWT는 header / payload / signature로 이루어져 있다.
토큰의 타입(JWT)과 HMAC SHA256, RSA와 같이 사용된 해시 알고리즘(signing algorithm)이 들어간다. 그리고 Base64url로 암호화되어 JWT의 첫부분을 구성한다. (encoded)
예시 - {"alg":"HS256", "typ":"JWT"}
클레임(claim)을 담고 있다. 요청이라고 하고 싶은데 번역을 어떤 단어로 해야될지 잘 모르겠다 🤔
claim은 entity(객체, 일반적으로는 유저)와 추가 데이터에 대한 statement이다.
registered claim: 미리 정의된 클레임으로 의무적인 것은 아니지만 유용하기에 권장된다. iss(issuer), exp(expiration time), sub(subject) 등이 있다.
public claim: 원할 때 JWT를 사용해 정의할 수 있는 클레임.
private claim: 정보를 공유하기 위해 만들어진 커스텀 클레임이다. registered나 public이 아니면 다 private이다. 세션 설명에서는 클라이언트와 서버 간 협의 하에 사용하는 클레임이라고 설명했다.
# payload 예시 1
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
# payload 예시 2
{
"user-id":1,
"exp":1539517391
}
위 요소를 조합 및 작성한 뒤 BASE64 방식으로 인코딩해 두번째 요소에 위치한다.
JMT가 원본 그대로라는 것(message가 바뀌지 않았다는 것을)을 확인할 때 사용하는 부분. 계약서 위변조를 막기 위해 서로 사인하는 것과 비슷하다.
서명은 BASE64URL로 암호화된 header와 payload, 그리고 JWT secret(별도 생성)을 헤더에 지정된 암호 알고리즘으로 암호화해 전송한다. 만약 HMAC SHA256 알고리즘을 사용하길 원하면, 서명은 다음과 같이 만들어질 것이다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
서명은 복호화가 가능해야 한다. 풀어야 내 것이 아닌지 맞는지를 보고 데이터를 조회할 수 있기 때문이다.
주의할 점은 header와 payload는 BASE64로 인코딩한 것이므로(=암호화 한 것이 아니므로)누구나 원본을 볼 수 있으니, 개인 정보를 담으면 안 된다.
위의 모두를 합치면, 결과는 세 개의 Base64-URL string이 .으로 구분된 형태다. 아래 예시를 보면 JWT가 암호화된 header, payload를 가졌고 secret과 함께 사인됐다는 것을 알 수 있다.
링크 사이트에 들어가면 생성된 JWT를 복호화할 수도 있다.
어렵다 🤯
설명 엄청 잘 해놓으셨네요. 감사합니다!