CryptoJS: Javascript 기반의 암/복호화가 가능한 여러 알고리즘을 간편하게 제공하는 라이브러리
암호화 방식은 크게 양방향, 단방향 암호화로 나눌 수 있다.
이 두 방식의 차이점은 암호문을 복호화해 원문을 알아낼 수 있는지 여부이다.
crypto-js가 제공하는 암호화 방식에는 ⑴ 대칭키, ⑵ 비대칭키, ⑶ 해싱(hashing)이 있다.
양방향
암호화된 암호문을 복호화할 수 있다.
1. 대칭키(symmetric encryption) : 암/복호화 시 같은 키값을 이용한다.
ex) DES, 3-DES, AES(128bit, 256bit), SEED, ARIA
현재 가장 보편적인 암호화 방식은 현 미국 표준방식인 aes다(DES는 너무 오래돼서 취약점이 발견됨).
128~256 비트 키를 적용할 수 있어 보안성이 뛰어나고, 공개 알고리즘이라 누구나 사용할 수 있다.
단, 송신 측에서 수신 측에 암호키를 전달하는 '키 배송 과정'에서 키를 탈취하면 아무리 뛰어난 암호화 알고리즘을 사용했더라도 평문이 털리게 된다.
2. 비대칭키(asymmetric encryption) : 암/복호화 시 다른 키값을 이용한다. A로 암호화한 암호문을 B로만 복호화할 수 있는 식이다.
ex) RSA, ECC, DSS, Rabin, ElGamal
문제점: 암/복호화가 대칭형 암호에 비해 현저리 느리다.
=> 비대칭형 암호를 이용해 대칭형 암호의 키를 배송하고, 실제 암호문은 대칭형 암호를 사용하는 식으로 상호보완적으로 이용하는 것이 일반적이다.
단방향
암호화된 암호문은 절대 복호화가 불가능하다.
3. hashing: 암호화는 가능하지만 복호화는 불가능하다. 비밀번호 등에 이용한다.
hash | year | coll.res. | size(bits) | design | broken? |
MD4 SHA-0(SHA) MD5 SHA-1 |
1990 1993 1993 1995 |
64 80 64 80 |
128 160 128 160 |
32-bit ARX DM | 1995 1997 2004 2005 |
SHA-256 (SHA-2) SHA-384 (SHA-2) SHA-512 (SHA-2) |
2002 | 128 192 256 |
256 384 512 |
32-bit ARX DM 64-bit ARX DM 64-bit ARX DM |
|
SHA-224 (SHA-2) SHA-512/224 SHA-512/256 |
2008 2012 2012 |
112 112 128 |
224 224 256 |
32-bit ARX DM 64-bit ARX DM 64-bit ARX DM |
|
SHA3-224 SHA3-256 SHA3-384 SHA3-512 SHAKE128 SHAKE256 |
2013 | 112 128 192 256 ≤128 ≤256 |
224 256 384 512 any any |
64-bit Keccak sponge |
그 외: pbkdf2, bcrypt, scrypt, hmac 등
md4, md5, sha-0, sha-1은 이미 보안성을 잃은 알고리즘으로, 단시간 내에 충돌값을 찾아낼 수 있다(비둘기 집의 원리).
그렇기에 현재는 sha-256 이상, 가능하면 sha-3을 쓰는것이 좋다. sha-2도 sha-1을 변형한 함수이기 때문에 공격당할 가능성이 있기 때문에 안전하다고 보기는 어렵기는 하다.
특히, sha-2는 속도가 워낙 빨라 초당 대략 6억번 수행이 가능하다. 이 때문에 무차별대입공격(Brute Force)과 각 평문에 대한 해시값이 알려진 Rainbow Table에 의해 어느정도 공략이 가능하다. 완벽한 공략까지는 약 137억년의 시간이 걸린다고 하니 복잡한 암호를 사용하면 위험도가 낮겠지만, 보안이 완벽하다고 보긴 어렵다.
sha-3은 sha-2와 디자인이 완전히 다르기에 비교적 호평을 받고 있다.
그러나 sha-3은 그 당시 정치적인 이유로 128bit, 256bit 등을 맞추는 등 과도하게 설계되어서 필요 이상의 비용이 들기 때문에, shake128과 256을 쓰길 추천하고 있다.
** CryptoJS에서는 아직 SHA3 까지만 제공하고 있는 상태이며, SHAKE128과 256은 아직 구현 중에 있는 듯하다.
** CryptoJS.SHA3의 기본값은 512bits 며, 224, 256, 384, 512 중 하나의 해시 길이를 설정해 출력할 수 있도록 구현되어 있다.
해시 함수는 양방향 암호화와 달리 복호화가 가능한 키가 배송 중 탈취당할 확률은 없다.
하지만 입력된 값이 같으면 언제나 같은 결과를 나타내기 때문에,
무차별 대입공격을 기반으로 원문과 매치되는 해시값의 Rainbow Table을 만들어 공격이 성공하거나
비둘기 집의 원리처럼 수많은 경우의 수를 일정 bit에 몰아넣다보면 언젠가는 충돌이 발견될 가능성이 존재한다.
또한 아직 지구상의 계산값(현속도로는 다 공략하는데 137억광년이 걸린다고...)으로는 SHA-256을 사용해도 크게 상관이 없다며 사용을 권장하지만, 기술의 발전으로 계산속도가 더 빨라지면 공략될 가능성이 있다.
암호학적 해시 함수의 단점을 보완하는 방법
1. 키 스트레칭(key stretching)
해시 암호화를 여러번 반복해서 무차별 대입공격을 방지할 수 있다.
이 때 반복 횟수는 암호화 시 키를 생성하는 비용을 증가시켜 공격의 난이도를 높인다.
2. 솔팅(salting)
실제 암호의 앞이나 뒤에 솔트된 데이터와 원본 패스워드를 넣어 해시값을 만든다. rainbow table 공격을 방지할 수 있다.
이를 사용한 강력한 해시 암호화 알고리즘들
1. pbkdf2 - crypto-js 에서도 제공
import CryptoJS from "crypto-js";
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const key128Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, {keySize: 128 / 32});
const key256Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, {keySize: 256 / 32});
const key512Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, {keySize: 512 / 32});
const key512Bits1000Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, {
keySize: 512 / 32,
iterations: 1000
});
해시 함수의 컨테이너 역할을 한다.
salt 적용 후 키 스트레칭 횟수의 지정이 가능하다. 가볍고 구현하기 쉬워 가장 많이 사용되는 함수 중 하나다.
NIST(미국표준기술)에서 승인된 알고리즘이며, 미국 정부 시스템에서도 사용중이다.
2. bcrypt - bcryptjs로 제공
import bcrypt from 'bcryptjs';
const bcryptHash = bcrypt.hashSync(pwd, bcrypt.genSaltSync(10));
bcrypt.compareSync(inputText, bcryptHash) // true
패스워드 저장을 목적으로 만들어졌다. 아직까지 가장 강력한 해시 메커니즘으로 알려져있다.
사용법도 쉽고 hash값과 원문이 매칭되는지 확인하는 함수를 제공해 사용성이 좋다.
3. scrypt - scrypt-js로 제공
import scrypt from 'scrypt-js';
const password = new buffer.SlowBuffer("anyPassword".normalize('NFKC'));
const salt = new buffer.SlowBuffer("someSalt".normalize('NFKC'));
const N = 1024, r = 8, p = 1, dkLen = 32;
// 암호화
const keyPromise = scrypt.scrypt(password, salt, N, r, p, dkLen, updateInterface);
// 매칭 여부 확인
const key = scrypt.syncScrypt(password, salt, N, r, p, dkLen);
import scrypt from 'scrypt-js';
const N = 1024, r = 8, p = 1, dkLen = 32;
const scryptPwd = scrypt(
Buffer.from(inputText),
Buffer.from(JSON.parse(salt.toString())),
N, r, p, dkLen
);
더 강력하지만 많은 메모리와 CPU를 요구하며, OpenSSL 1.1이상 제공 시스템에서만 작동한다.
여러 언어 라이브러리로 제공되고 있다.
promise를 사용해 scrypt pbkdf를 비동기적으로 계산한다고 한다.
그런데 사용해보니 password와 salt 에 ArrayLike<number>가 전달되어야 했다. 이 부분은 추후에 좀 더 알아보려고 한다.
** 여러 해시함수를 제공하는 CryptoJS를 사용하는 것도 좋지만, bscrypt나 scrypt를 사용하는 것도 괜찮을 듯 하다.
** 아직 scrypt는 실행해보지 못했다. buffer에 대해 좀 더 공부하고 다시 시도해보려고 한다.
HMAC 예제: https://defineall.tistory.com/701
참고글
https://k0102575.github.io/articles/2020-03/hash
https://jusungpark.tistory.com/34
https://crypto.stackexchange.com/questions/68307/what-is-the-difference-between-sha-3-and-sha-256
https://nukw0n-dev.tistory.com/12
cryptojs docs : https://cryptojs.gitbook.io/docs/#pbkdf2
bcryptjs npm : https://www.npmjs.com/package/bcryptjs/v/2.4.3
scryptjs npm : https://www.npmjs.com/package/scrypt-js
scrypt 예제: https://javascript.hotexamples.com/examples/scrypt/js/-/javascript-js-class-examples.html
'Javascript' 카테고리의 다른 글
[Ubuntu] nvm으로 node version update & node version 유지 (0) | 2024.03.05 |
---|