프로젝트의 기능 중 하나인 투표를 담당하면서 db에서 추적되지 않는 익명
투표를 위해 암호화에 대해 공부하며 기록을 남기게 되었다.
전체적인 프로세스는 다음과 같다.
1️⃣ 투표를 익명으로 생성 시 db에서 추적이 불가능하게 하기 위해 먼저 사용자 id를 해시하고
2️⃣ 이 값의 일부로 고유한 AES키를 생성해 서버에 전달한다.
3️⃣ 이를 해당 투표의 고유 id로 정의하고
4️⃣ 사용자가 투표 답변, 수정, 삭제 시 동일한 방법으로 서버에 고유한 키를 보내면
5️⃣ 서버에 저장되어 있던 암호화된 투표 고유 id와 비교해 자신이 생성한 투표인지 아닌지를 판단할 수 있게 하였다.
따라서 사용자의 원래 id를 서버에 전달하지 않고 db에는 사용자를 식별할 수 있는 정보가 없으므로 익명성이 보장된다.
코드를 살펴보자면
import forge from "node-forge";
function getSignature(publicKey: string, content: string): string {
const md = forge.md.sha256.create();
md.update(publicKey);
const key = md.digest().getBytes(16); // AES-128 사용
먼저 사용자 ID를 해시하여 AES 키를 생성하고
const iv = "0000000000000000";
동일키에 동일 암호문이 나와야 하기 때문에 IV (초기화 벡터)를 고정시킨다.
const cipher = forge.cipher.createCipher("AES-CBC", key);
cipher.start({ iv: iv });
cipher.update(forge.util.createBuffer(content));
cipher.finish();
const encrypted = cipher.output.getBytes();
forge 라이브러리를 사용하여 AES-CBC 암호화 객체를 생성하고 위에서 만든 AES 키를 설정한다. 이 후 암호화된 데이터를 바이트 배열로 변환하여 저장한다.
return forge.util.encode64(encrypted);
최종으로 IV와 암호화된 데이터를 Base64로 인코딩하여 반환하는 함수를 만들었다.
이후 테스트를 진행하였는데 url로 이 키를 전달할 시 기호(?*^% 등)가 빈 문자열로 들어가는 이슈로 인해 투표를 생성한 사람과 동일함에도 매칭이 안되는 문제가 생겼다.
url-safe 문자열로 변환하기 위해 encodURIComponent로 한번 감싸서 보내 해결할 수 있었다.
return encodeURIComponent(forge.util.encode64(encrypted));
➕ 추가로 알게 된 것 ➕
AES는 대칭 키 암호화 방식으로 고정된 키와 초기화 벡터(IV)를 사용해 데이터를 암호화한다.
여기서 대칭키 암호 알고리즘과 비대칭키 암호 알고리즘을 비교하며 좀 더 알아보자면 다음과 같은 특징이 있다.
✔️ 대칭키 암호 알고리즘
✔️ 비대칭키 암호 알고리즘
- 암호화/복호화 시의 키가 서로 다른 키를 의미한다.