Nodejs/암호화

암호화 #1 - Buffer, hash

Sila 2022. 3. 2. 18:09

1. Intro

 

우리가 어떤 웹 페이지에 정보를 입력할 때, 비밀번호 등의 민감한 정보는

 

아무나 볼 수 없도록 암호화해 저장할 필요가 있다.

 

이런 암호화 방식에는 단방향 암호화, 양방향 암호화가 있는데 보통은 단방향 암호화 방식을 많이 사용한다.

 

단방향 암호화는 복호화할 수 없는 암호화 방식을 의미하는데,

 

이는 한 번 암호화하면 다시 원래 문자열을 찾을 수 없다는 것을 의미한다.

 

그래서 요즘엔 비밀번호를 잊어버려서 찾으려하면 비밀번호를 알려주지 않고

 

새로운 비밀번호를 설정하는 식으로 이를 해결하는 것이다.

 

 

이런 단방향 암호화는 주로 해시 기법을 사용한다. 

 

해시 기법이란, 주어진 문자열을 다른 문자열로 바꾸는 방식이다.

 

오늘은 이 해시 기법을 사용하는 방식에 대해 알아볼텐데, 그 전에 알아야할 배경지식을 몇 가지 정리하도록 하자.

 

2. 버퍼 (buffer)

nodejs에서 버퍼는 문자, 변수를 표현하는 데이터를 사용 전 저장하는 메모리(저장 공간)이다.

 

모든 프로그래밍 언어는 buffer가 존재하기 때문에 이건 추가적인 패키지 설치도 필요없이 실행 가능하다.

 

즉, 이미 Buffer라는 객체가 있으므로 이걸 가져다가 쓰면 된다.

 

2.1 Buffer.method

Buffer 객체 안에 있는 여러 메소드를 활용해 문자열을 변환할 수 있는데, 이 중 중요한 메소드를 몇 가지 살펴보자.

 

i) from(string) : 괄호 안의 문자열을 버퍼로 바꿔준다.

 

/*  buffer.js  */

const name = 'lsj'

const buf = Buffer.from(name)

console.log(name)   //   lsj
console.log(buf)   //   <Buffer 6c 73 6a>

 

콘솔로 확인해보면 문자열이 다른 형태로 변환되었음을 알 수 있다.

 

6c 73 6a 는 각각 대응하는 l, s, j의 아스키 코드 값을 16진수로 표현한 것이다.

 

from은 인자값을 2개 가질 수 있는데, 하나는 방금과 같이 변환할 문자열,

 

나머지 하나는 인코딩 형식이다. 써주지 않는다면 영어의 경우 utf-8 chatset을 사용한다.

 

그 밖의 다른 형식으로는 hex, base64 등이 있다.

 

/*  buffer.js   */

const a = 'abc'

console.log(Buffer.from(a))
console.log(Buffer.from(a,'utf-8'))
console.log(Buffer.from(a, 'hex'))
console.log(Buffer.from(a, 'base64'))

확인해보면 인자값을 주지 않았을 때와 utf-8 을 주었을 때는 서로 같은 값을 출력하지만

 

hex, base64를 사용했을 때는 다른 값을 출력한다.

 

 

ii) toString(buffer) : 버퍼를 다시 string 형태로 바꿔준다. (괄호 안이 비어있을 경우)

 

앞의 변수 buf를 다시 원 문자열로 바꿔보자.

 

/*  server.js  */

const reName = buf.toString()
console.log(reName)   //   lsj

 

reName을 출력해보면 name과 동일한 문자열임을 알 수 있다.

 

3. 해시 (hash)

해시 기법이란 주어진 문자열을 다른 문장으로 바꿔주는 방법을 의미한다.

 

이 역시 buffer와 마찬가지로 변환할 문자열, 사용할 알고리즘, 인코딩 알고리즘을 정해주면

 

나머지는 컴퓨터가 다 알아서 해준다.

 

사용자가 어떤 페이지에서 비밀번호를 입력하면, 해시 기법을 통해 다른 문자열로 바꿔주고, 이를 DB와 비교해

 

일치하면 로그인을 진행하고, 일치하지 않는다면 로그인이 거부될 것이다.

 

해시의 특징은 주어진 문자열로부터 바꿔준 문자열을 만들어내는 것은 윕지만

 

바뀐 문자열로부터 원래 문자열을 역산하는 것은 매우 어렵다는 것이다.

 

 

이런 암호화를 위한 객체도 js는 가지고 있다.

 

방금 전 buffer에서 buffer를 사용했다면, 여기서는 crypto 객체를 가져올 것이다.

 

/*  hash.js  */

const crypto = require('crypto')   //   외부에서 가져온다.
const name = 'lsj'

 

i) createHash(algorithm) : 사용할 해시 알고리즘을 넣는다. 최근에는 sha256, sha512등을 사용한다.

 

ii) update(string) : 변환할 문자열을 넣는다.

 

iii) digest(인코딩) : 인코딩할 알고리즘을 넣는다.

 

/*  hash.js  */

const sha256 = crypto.createHash('sha256').update(name).digest('base16')
console.log(sha256)
// <Buffer 80 3e 72 02 77 8e 98 eb 3f eb c0 e0 c7 62 66 2a 41 6b 21 10 26 87 fd 64 3f 96 bd 57 b6 c0 94 46>

 

변수 name의 값을 sha256 해시 알고리즘과 base16 인코딩 알고리즘을 이용해 변환한다.

 

다른 인코딩 알고리즘을 사용하면 다른 형태로 값을 이끌어낼 수도 있다.

 

/*  hash.js  */

const sha256_2 = crypto.createHash('sha256').update(name).digest('hex')
console.log(sha256_2) // 03e7202778e98eb3febc0e0c762662a416b21102687fd643f96bd57b6c09446

 

여기까지 한 후, 보안을 더 강화하기 위해 해시를 생성할 때 인자값을 하나 추가해줄 수 있다.

 

지금까지 문을 여는데 필요한 열쇠가 하나였다면, 문을 열기 위해 필요한 열쇠를 하나 더 추가해서,

 

두 열쇠를 모두 가진 사람만이 열쇠를 모두 사용해 문을 열수 있게 한다고 생각하면 된다.

 

두 번째 인자값을 보통 'salt'라고 부른다. 나만 사용하는 해시값이라고 보면 된다.

 

/*  hash.js  */

const salt = 'web7722' // salt의 값으로 문자열 선언

const hashWithSalt = crypto.createHmac('sha256', Buffer.from(salt)).update(name).digest('hex')
// 9123f76a04f60f7f42abf537832551ad2a0123388d6238bd62b83b094f527340

 

우선 salt값을 from 메소드를 통해 바꿔준 후, 이를 두 번째 인자값으로 삼아 해시를 생성한다. 

 

문자열의 길이는 같지만 이 값으로 원래 문자열을 찾기는 더욱 어려워졌다.

 

다음 글에서는 오늘 알아본 것을 바탕으로 JWT (JSON Web Token)에 대해 알아보자.

 

'Nodejs > 암호화' 카테고리의 다른 글

암호화#2 - JWT  (0) 2022.03.02