__Authentication

  • HTTP의 stateless하고 connectionless한 특성 때문에, 서버와 클라이언트는 연속적으로 통신을 할 수 없습니다. 때문에 같은 유저가 여러 번 요청을 보내더라도, 서버는 그 유저와의 통신이 이전에도 있었던지 알 길이 없습니다.
  • 만약 쿠키와 세션이 없는 가운데 권한 부여(authorization)을 구현하려 한다면, 새로운 요청이 있을 때마다 매번 인증(authentication) 작업을 해 주어야 할 것입니다. 이에 대한 보완책이 바로 쿠키(cookie)와 세션(session)입니다.

세션과 쿠키는 별개의 개념이 아닙니다! 세션 또한 쿠키를 기반으로 합니다. 서버에서 세션 id가 생성이 되면, 응답 시 쿠키에 세션 id를 실어 클라이언트에 전달합니다.

  • 서버가 사용자 브라우저 위치에 어떤 정보를 저장하고 불러오는 수단입니다.
  • 특정 호스트에서 생성된 쿠키는 이후 모든 요청마다 서버로 전송됩니다.
  • 이름, 값, 만료 날짜, 경로 정보 등으로 구성되어 있습니다.
  • 필요할 때 참조나 재사용이 가능합니다.
  • 4KB 이하 저장 가능

Session

  • 일정 시간 동안 같은 사용자로부터 오는 요구를 하나의 상태로 보고, 그 상태를 일정하게 유지시키는 수단.
  • 여기서 일정 시간이란 방문자가 웹 브라우저를 통해 웹 서버에 접속한 시점으로부터 웹 브라우저를 종료하여 연결을 끝내는 시점을 말합니다.
  • 웹 서버는 각각의 요청에 대하여 고유한 식별자인 session ID를 부여하여 클라이언트를 구별합니다.
  • 웹 서버의 세션 스토리지에 세션 정보가 저장됩니다.
  • 데이터 저장 제한 X
  • 통신에만 발동되는 쿠키, 세션 쿠키 라고도 합니다.

Token

![](/tutorial/2020-02-22-authentication_files/Screen Shot 2020-04-22 at 6.43.29 PM.png)

reference

  • 인증을 위해 사용되는 암호화 된 문자열입니다.
  • 토큰 인증에서, 서버는 JWT(JSON Web Token)을 생성하여 클라이언트에 보냅니다.
  • 클라이언트는 JWT를 로컬 스토리지 또는 쿠키에 저장하고, 세션과 마찬가지로 사용자가 보내는 모든 요청에 포함됩니다.
  • 세션 인증에서 서버가 세션 정보를 관리했던 것과 달리, 서버는 JWT를 저장하지 않습니다! 다만 토큰이 유효한지 여부만을 확인할 뿐입니다.
  • JWT를 추가적으로 저장할 공간이 필요 없기 때문에, 서비스가 커진다고 하여 서버에 추가적인 부하가 발생하지 않습니다. 더 효율적입니다!

Crypto?

  • node.js의 내장 모듈로서 암호화와 관련된 일을 처리합니다.
  • crypto 모듈을 사용하여 해싱과 솔팅을 구현하는 방법을 다뤄 보도록 하겠습니다.

Hashing?

  • 하나의 문자열을 다른 값이나 키로 변환하는 단방향 암호화 기법입니다.
  • 데이터 변환은 해시 함수를 통해서 일어납니다. 해시 함수는 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는데, 알고리즘마다 매핑된 값의 길이가 다릅니다. 예를 들어 SHA256은 64, SHA512는 128입니다.
  • 앞에서 해시 자료구조를 배웠을 때 다뤘던 것처럼, 해시 함수는 같은 값을 넣으면 늘 같은 값을 반환해야 합니다. 때문에 여러 암호를 모은 데이터베이스를 기반으로, 암호를 유추해 낼 위험이 있습니다.
  • 또한 SHA256, MD5등 알고리즘 자체는 이미 널리 공개가 되어 있습니다. 때문에 이미 보안이 뚫린 알고리즘(MD5, SHA1 등)도 있습니다.
  • 해싱의 한계를 극복하기 위해, 우리는 해시 함수를 여러 번 반복하여 적용하거나 솔트(salt)를 활용할 수 있습니다.
  • Crypto로 해싱을 구현해 보겠습니다.

    const crypto = require('crypto');
    
    const hashClass = crypto.createHash('sha512');
    // sha512 알고리즘을 사용하는 해시 class를 만든다.
    const hashedValue = hashClass.update('get coding').digest('base64');
    // 'get coding' 이라는 문자열을 해싱하겠다는 의미. 
    // 해싱한 문자열을 base64로 인코딩하여 다이제스트를 반환한다. 
    // 다이제스트란 해시를 통해 얻어낸 암호화된 값을 뜻한다.

양방향 암호화도 있습니다! 이 또한 crypto로 구현할 수 있으니 관심이 있다면 cipher에 대해 알아봅시다.

Salting?

  • 솔팅(salting)이란 암호화해야 하는 값에 어떤 별도의 값(salt)을 추가해서 결과를 변형하는 것을 뜻합니다.
  • crypto 모듈에서는 pbkd2(Password-Based Key Derivation Function 2)라는 비동기 메서드를 통해 솔트를 적용할 수 있습니다

    const crypto = require('crypto')
    // password, salt, iteration, 바이트 길이, 알고리즘, 콜백을 인자로 받는다.
    crypto.pbkdf2('생선구이', '소금', 123456, 64, 'sha512', (err, derivedKey)=>{ 
    	if(err) throw err; 
    	console.log(derivedKey.toString('hex')) // hex로 인코딩한다.
    })
    
  • 만약 솔트 값이 고정되어 있다면, 솔트 값이 유출되기 쉽겠죠? 따라서 암호화를 할 때마다 랜덤한 솔트 문자열을 생성한다면 좋을 것입니다. 이와 관련하여 crypto에서 사용할 수 있는 메서드가 바로 randomBytes 입니다.

        // 랜덤한 솔트를 64바이트 길이로 생성한다. 버퍼 형식으로 반환된다.
        crypto.randomBytes(64, (err, buf)=>{ 
        // 랜덤하게 생성된 버퍼를 base64를 통해 인코딩한다.
        	const randomlyGeneratedSalt = buf.toString('base64') 
        	crypto.pbkdf2('꽁치구이', randomlyGeneratedSalt, 123456, 64, 'sha512', (err, derivedKey)=>{
        	 if(err) throw err; 
        		console.log(derivedKey.toString('hex')) })
        })
  • 랜덤으로 생성된 솔트는 해싱된 값(derivedKey)와 반드시 함께 저장해야 한다는 점을 명심해 주세요.

솔티드 해시 외에도 HMAC이라는 방법을 사용하여 보안을 강화할 수도 있습니다. 크립토의 createHMA 메서드와 함께 알아보세요.