오늘은 Nest.js를 사용해 대표적인 양방향 암호화 방식인 AES를 적용해 볼 것이다.
사용하는 모듈은 crypto
이다.
AES가 뭐지?
일반적으로 개발에서 가장 많이 사용되는 암호화는 단방향 암호화인 hash이다. 비밀번호 저장 등 중요한 사용자의 정보를 보관할 때 쓰이며,
이때 사용자의 정보는 compare를 통한 비교만 가능할 뿐, 원본값을 찾아내는 것은 어렵다. 복호화가 불가능한 것이다.
반면 AES는 양방향 암호화에 해당하며, 보내는 쪽과 받는 쪽 모두 key를 가지고 있으면 원래의 값을 추출해내는 것이 가능하다.
자세한 설명은 여기에서 볼 수 있다.
어디에 쓰길래?
사용자의 정보값을 기준으로 동작해야하는 api가 최근 개발되어야하는데, 사용자의 정보값은 마스킹이 되어야하는 값이다.
그러므로 백엔드에서 프론트로 반환한 후에는 프론트가 다시 백엔드로 특정 사용자의 정보를 요청할 수 없는 상황이 되었다.
그러므로 반환해야 하는 값에 사용자의 string 정보값을 AES로 encrypt하여 반환해주면, 프론트는 그 값을 가지고 백엔드에 요청을 보내는 것이다.
그럼 프론트와 백엔드가 각각 secret key를 가지고 있나?
아니다.
그렇게 한 이유는 두 가지가 있는데,
- 프론트가 값을 가지게 되면 주기적으로 그 값을 갱신하기가 까다로워진다. 값이 브라우저로 넘어가버리면 session 외에는 백엔드와 프론트엔드의 secret key 값을 동기화하기가 까다로워지기 때문이다. 특히 localStorage나 cookie로 저장되는 경우가 그렇다.
- 프론트에 중요한 정보를 맡기는 것은 위험한 일이다. 브라우저에서 정보를 탈취하는 방법은 무궁무진하기 때문이다. JWT의 값 decode 등을 프론트엔드에서 처리하지 않고 백엔드가 처리하게 하는 것 등도 이런 이유에 해당한다.
그럼 어떻게 할 것인가?
양방향이라는 글에 사로잡히기 쉬우나 백엔드에서 둘 다 처리하는 것이 가능하다.
암호화를 하는 함수 / 복호화를 하는 함수가 각각 key를 가지고 있는 형태가 되는 것이다.
즉, 백엔드는 하나의 키를 계속 쥐고 있으면서 프론트에서 전달받은 키를 복호화 함수의 매개변수로 사용만 하면 되는 것이다.
구현
다음과 같이 구현하였다.
const iv = randomBytes(16);
const key = Buffer.from('kimsuhanmugeobukiwadurumicapopoe');
// 암호화
export const generateAESHash = (plainText: string): string => {
const logger: Readonly<Logger> = new Logger(generateAESHash.name);
try {
const cipher = createCipheriv('aes-256-ctr', key, iv);
const encryptedText = Buffer.concat([
cipher.update(plainText),
cipher.final(),
]);
return encryptedText.toString('base64');
} catch (error) {
logger.error(error);
throw new Error('crypto failed while generate hash');
}
};
// 복호화
export const decodeAESHash = (encryptedText: Buffer): string => {
const logger: Readonly<Logger> = new Logger(generateAESHash.name);
try {
const decipher = createDecipheriv('aes-256-ctr', key, iv);
const decryptedText = Buffer.concat([
decipher.update(encryptedText),
decipher.final(),
]);
return decryptedText.toString();
} catch (error) {
logger.error(error);
throw new Error('crypto failed while generate hash');
}
};
다음과 같이 암호화하면 원하는 값을 얻을 수 있다.
iv가 뭐지?
iv는 Initial Vector초기화 벡터의 약자이다. salt와 유사한 역할로, 같은 암호화를 거쳐도 IV를 통해 매번 다른 결과를 도출하는 것이 가능하다.
종종 base64를 암호화라고 착각할 수 있으나, base64는 인코딩이다. 파일 등의 데이터를 일련의 아스키 문자열로 바꾸는 것으로, 우리가 10진수를 2진수로 변환하는 것을 암호화라고 하지 않듯(누구나 읽을 수 있으므로), base64 또한 고유의 진법을 사용해 데이터를 변환하는 것이므로, 암호화가 아니다.
즉, decode를 통해 누구나 읽을 수 있는 조건이 갖춰지는 알고리즘들은 특정 내용 소유자에게만 내용을 보여준다는 원칙에 위반되므로, 암호화가 아니다.
'DEVELOPMENT > NODEJS' 카테고리의 다른 글
Jest config (0) | 2022.11.07 |
---|---|
TypeORM where (in 0.3.x) (2) | 2022.09.25 |
NestJS :: param과 URI의 우선순위 문제 (0) | 2022.04.19 |
Node.js BASE64로 AWS S3에 이미지 업로드하기 (0) | 2022.04.17 |
cookie 데코레이터에서 JWT payload 추출하기 (0) | 2022.04.15 |