[포스코x코딩온] 웹 풀스택 과정 7주차 회고 | 암호화

암호화
단방향 암호화
암호화만 가능한 방식이다. 복호화는 불가능하기 때문에, 한번 암호화된 정보는 바꿀 수 없다.
- 비밀번호 저장에 주로 사용한다.
- 암호화 해시함수를 이용하여, Hash 암호화 방식을 사용한다.
해시함수 (Hash Function)
- 임의의 길이의 데이터를 고정된 길이의 데이터로 반환시켜준다. (출력값은 언제나 동일한 길이)
- 동일한 데이터에 대해서 항상 동일한 해시 값이 생성된다.
- 해시(Hash): 해시 함수에 의해 얻어지는 값
- 해시함수(해시알고리즘): SHA-256, SHA-512, ...
암호 생성
- crypto를 불러온다. (node.js 내장된 패키지로 별도 설치 필요없음)
const crypto = require('crypto');
- 아래의 해시생성 명령어를 입력한다.
.createHash('알고리즘').update('암호화할 값').digest('인코딩방식')
: 선택한 인코딩 방식으로, 암호화할 값을 선택한 알고리즘에 따라 암호화한다.
const createHashedPassword = (password) => {
retrun crypto.createHash('sha512').update(password).digest('base64');
};
※인코딩 방식 'base64'
: 64개의 가능한 문자로 데이터를 인코딩하는 방식이다.
(그 중 62개는 영문 대소문자와 10진수 숫자로 이루어짐. 나머지 2개는 인코딩과 디코딩 과정에서 추가적인 문자로 사용됨)
암호화 보완
해시함수로 생성한 암호의 경우, 레인보우 테이블 (해시함수를 사용해 만들 수 있는 값들을 대량으로 저장해놓은 표)에 의해 보안 문제점이 있다.
- salt 값 추가 또는 해시함수의 반복으로 위 문제점을 보완한다.
1) salt 값 추가: 입력한 값에 salt라는 특정 값을 붙여 변형시킨다.
crypto.randomBytes(size): 지정한 크기의 random data를 생성한다.
.toString(): 생성된 random data는 버퍼형식이기 때문에, 읽을 수 있는 String으로 변환한다.
const salt = crypto.randomBytes(16).toString('base64');
2) 해시함수의 반복: 해시 함수를 여러번 반복하여 본래의 값을 예측하기 어렵게 한다.
const iterations = 10000;
pdkdf2 함수를 이용한 암호화
pdkdf2Sync(password, salt, iterations, keylen, digest)
crypto에 내장된 pbkdf(password-based key derivation function)는 비밀번호 기반의 키 도출 함수이다.
암호화할 비밀번호, 솔트, 반복횟수, 키의 길이, 알고리즘을 지정하여 생성할 수 있다.
버퍼를 반환하기 때문에, .toString()을 이용하여 인코딩 방식 지정이 필요하다.
const salt = crypto.randomBytes(16).toString('base64'); // 솔트 생성
const iterations = 10000; // 반복횟수
const keylen = 64; // 생성할 키의 길이
const digest = 'sha512'; // 해시 알고리즘
const createPbdkf = (password) => {
return crypto.pbdkf2Sync(password, salt, iterations, keylen, digest).toString('base64');
}
검증
복호화가 불가능하기 때문에, 입력받은 비밀번호를 동일한 알고리즘으로 암호화한 뒤, 결과를 비교하여 검증한다.
const verifyPassword = (password) => {
const compare = crypto.pbdkf2Sync(password, salt, iterations, keylen, digest).toString('base64');
if (compare === dbPassword) return true;
return false;
}
Bcrypt
비밀번호를 암호화하는 알고리즘 중 하나로, 강력한 보안이 필요할 때 적합하다.
내장된 패키지가 아니므로, bcrypt 설치 후 불러와야 한다. (npm install bcrypt)
암호화와 검증을 위한 함수가 각각 bcrypt에 내장되어 있어, 비교적 간단하게 코드를 작성할 수 있다.
- 암호화 .hashSync()
const bcrypt = require('bcrypt');
const salt = 10; // 일반적으로 10, 11으로 설정함
const bcryptPassword = (password) => {
return bcrypt.hashSync(password, salt);
}
- 검증 .compareSync()
const comparePassword = (password, dbPassword) => {
return bcrypt.compareSync(password, dbPassword);
}
양방향 암호화
암호화와 복호화를 모두 하는 방식으로, 주로 문서를 암호화해서 보낼 때 사용한다.
대칭키 암호화 방식
암복호화에 사용하는 키가 동일한 방식이다.
- 키 관리가 중요하다!
- 해당 키를 아는 사람만이 문서를 복호화하여 볼 수 있다.
- 대표적인 알고리즘: DES, 3DES, AES, ARIA
대칭키 암호화
.createCipheriv(algorithm, key, iv): 암호화 객체를 생성한다. (iv: 데이터블록을 암호화할 때, 보안성을 높이기 위해 사용)
.update(암호화할 데이터, '입력 인코딩', '출력 인코딩'): 암호화할 데이터를 처리한다.
ex) .update(word, 'utf-8', 'base64'): word라는 데이터를 utf-8 형식으로 입력받아, base64 형식으로 출력한다.
- 전체 과정:
1) 알고리즘, 키, iv를 지정
const algorithm = 'aes-256-cbc'; // 256비트 키를 사용, 128비트의 블록.
const key = crypto.randomBytes(32); // 1바이트는 8비트이므로, 32바이트 크기의 키 생성
const iv = crypto.randomBytes(16);
2) 암호화 객체 생성 → 데이터 암호화 처리 → 최종결과 생성
const cipherEncrypt = (word) => {
const cipher = crypto.createCipheriv(algorithm, key, iv); // 암호화할 객체 생성
let encryptedData = cipher.update(word, 'utf-8', 'base64'); // 데이터 암호화 처리
encryptedData += cipher.final('base64'); // 최종결과 생성
return encryptedData;
}
대칭키 복호화
.createDecipheriv(algorithm, key, iv): 복호화 객체를 생성한다. (암호화와 동일한 값 입력)
- 복호화 과정은 암호화와 동일하다. (입력, 출력 인코딩 방식은 뒤바뀜)
const decipher = (encryptedData) => {
const decipher = crypto.createDecipheriv(algorithm, key, iv);
const decryptedData = decipher.update(encryptedData, 'base64', 'utf-8');
decryptedData += decipher.final('utf-8');
return decryptedData;
}
공개키 암호화 방식
암복호화에 사용하는 키가 서로 다른 방식이다.
- 공개키(모든 사람이 접근 가능한 공개된 키)와 개인키(각 사용자만이 갖는 키, 공개X)로 이루어진다.
- 공개키로 데이터를 암호화하고, 암호를 받아 개인키로 복호화한다.
- 대칭키에서 키를 전송해야 하는 점을 보완할 수 있다.
- 속도가 느리다.