암호화 모듈 - crypto

2 분 소요

이번에 MERN 스택을 활용하여 게시판을 만들면서 배운 것들에 대해서 정리하려 합니다.
이번에는 암호화용 모듈인 crypto에 대해서 작성해보겠습니다.

crypto란?

Node.js의 암호화용 모듈 중 하나로, 암호화 알고리즘, 해시 데이터 생성 등 암호화와 관련된 기능들을 묶어놓은 모듈입니다.

암호화란?

어떠한 데이터를 사람들이 바로 확인을 하지 못 하도록 처리해주는 것.
키값 또는 알고리즘 등을 이용하여 이용하여 데이터의 값을 바꿔줍니다.
크게 양방향 암호화, 단방향 암호화로 나누어집니다.

  • 양방향 암호화

    암호화, 복호화가 가능합니다.
    문서나 파일같은 값이 다시 그대로 돌아와야 하는 경우에 사용합니다.

  • 단방향 암호화

    암호화만 가능합니다.
    비밀번호와 같은 값을 다시 복호화 할 필요가 없고, 비교만 해도 되는 경우에 사용합니다.

이 암호화들은 똑같은 값, 똑같은 알고리즘을 이용하여 암호화 할 경우, 결과값이 모두 같게 나옵니다.
그렇기 때문에

사용할 암호화 방식

제가 암호화를 사용하는 이유는 로그인 시 필요한 비밀번호를 저장하기 위해 사용합니다.
이 비밀번호는 비교하여 값이 맞는지만 확인하면 되므로, 단방향 암호화로 예제를 만들도록 하겠습니다.

제가 사용할 알고리즘은 pbkdf2이며, 이 알고리즘을 사용할 때 필요로 한 값은 다음과 같습니다.

  • 암호화 할 데이터
  • salt값
  • 알고리즘 반복 횟수
  • 결과값의 길이(크기)

예시

간단하게 알고리즘에 대해서 설명하면, 데이터에 salt값을 추가한 값을 해싱하고, 이 해싱한 값을 반복 횟수만큼 다시 해싱합니다.

예시를 간단히 들어보겠습니다.

  1. 데이터 생성
    여기에서는 “1234”를 암호화 해 보겠다는 것으로 가정하겠습니다.

  2. 데이터에 salt값 추가 “1234”값에 crypto.randombytes 함수를 이용하여 만든 랜덤 해시값을 뒤에 붙혀줍니다.
  3. 데이터 해싱
    “1234”와 랜덤 해시값을 붙힌 값을 반복 횟수만큼 해싱한다.

왜 이렇게까지 해야하는가?

일반적인 해싱 방법으로는 해커들이 값을 예측하기가 쉬워 더 예측하기 어렵도록 만들기 위해서입니다.

사용법

  • 암호화

      var crypto = require("crypto");   
      var salt = "";    // 랜덤 해시값(salt값)  
      var pw_saved = "";  // 암호화 결과 값    
      // 1     
      crypto.randomBytes(64, (err, buff) => {  
            
          salt = buff.toString("base64");     
          // 2    
          crypto.pbkdf2("비밀번호", salt, 100138, 64, 'sha512', (err, key) => {   
              // 3    
              result = key.toString("base64");   
          });  
      });
    

1번의 randomBytes 함수를 이용하여 길이가 64인(64바이트짜리) 랜덤 해시값을 만들었습니다.
이 해시값은 Byte 형식으로 되어있으며, 이 값을 그대로 사용하기는 불편하여 toString을 이용하여 base64 형식의 String으로 만들어서 저장합니다.

2번의 pbkdf2 함수가 암호화를 실행하는 함수입니다.
암호화할 데이터, salt값, 반복 횟수, 결과값 크기를 순서대로 인수로 넣어주고, 마지막 인수로 콜백 함수를 넣어줍니다.

이렇게 암호화를 끝낸 비밀번호 3번의 key값으로 나오며, 이것도 역시 Byte 형식으로 되어있으니 base64 형식의 String으로 만들어 저장합니다.

값으로 나온 salt값과 암호화된 비밀번호 값은 나중에 다시 사용해야 하므로 따로 저장을 해야합니다.

  • 비밀번호 체크

      var crypto = require("crypto");
      var salt = "";                  // 암호화 시 사용했던 salt 값
      var pw_saved = "";              // 암호화된 비밀번호
      var pw_getted = "비밀번호";      // 로그인 시 전달된 비밀번호 값
    
      // 1
      crypto.pbkdf2(pw_getted, salt, 100138, 64, 'sha512', (err, key) => {
              // 2   
              if(key.toString("base64") === pw_saved) {
                  console.log("로그인 성공");
              } else {
                  console.log("로그인 실패");
              }
          });
    

이번에는 salt값이 이미 만들어져 있기 때문에 salt값 생성을 넘기고, 바로 1번의 pbkdf2부터 시작합니다.
여기에서 중요한 것은 똑같은 환경에서 암호화 한 결과값을 비교 해야하기 때문에 비밀번호를 제외한 모든 값(salt, 반복 횟수 등)이 암호화 시 사용했던 값들을 그대로 사용해야 합니다.
반복횟수 값이 1이라도 다르면 전혀 다른 값이 나오게 되니 주의합시다.

2번과 같이 결과값(key)을 저장되었던 비밀번호(pw_saved)와 형식을 같게 만들어 준 뒤 비교를 하여 두 값을 비교할 수 있습니다.


이렇게 해서 단방향 암호화 알고리즘인 pbkdf2를 사용하여 비밀번호를 암호화 및 복호화를 해보았습니다.

다음에는 로그인의 결과값을 저장할 방법인 쿠키와 세션, 그리고 JWT에 대한 이야기를 해보겠습니다.