제로널리지 시스템에서 모든 것은 사용자가 입력한 마스터 패스워드 한 줄에서 출발합니다. 그런데 그 패스워드를 그대로 암호화 키로 쓰면 안 됩니다. 사람이 기억하는 패스워드는 엔트로피가 낮고 낮은 엔트로피의 키는 아무리 좋은 암호 알고리즘을 써도 오프라인 무차별 대입 앞에서 무너집니다. 그래서 패스워드와 실제 키 사이에는 반드시 키 파생 함수(KDF)가 들어갑니다. Ciphek을 만들며 이 첫 단추를 어떻게 잠갔는지 정리합니다.
패스워드를 늘리는 함수가 필요한 이유
패스워드 기반 키의 안전성은 공격자가 후보 패스워드 하나를 검증하는 데 드는 비용에 달려 있습니다. 단순 해시는 이 비용이 사실상 공짜라 초당 수십억 번을 시도할 수 있습니다. KDF는 이 검증을 일부러 비싸게 만들어 정당한 사용자에게는 로그인당 1초 남짓의 지연이지만 공격자에게는 후보마다 그 비용을 곱하게 만듭니다. 핵심은 "느리게"가 아니라 "비싸게"이고 무엇을 비싸게 만드느냐가 알고리즘의 세대를 가릅니다.
Ciphek은 여기서 Argon2id를 선택했습니다. PBKDF2는 널리 지원되지만 연산량만 늘릴 뿐 메모리를 거의 쓰지 않습니다. 그래서 GPU나 ASIC처럼 병렬 연산 유닛이 많은 하드웨어로 대량 크래킹하기 쉽습니다. Argon2id는 메모리 하드니스가 있어 후보를 검증할 때마다 상당한 메모리를 강제로 점유하게 만듭니다. 메모리는 GPU에서도 싸지 않기 때문에, 같은 예산으로 만들 수 있는 병렬 크래킹 유닛의 수가 크게 줄어듭니다. OWASP도 패스워드 저장과 키 파생에서 Argon2id를 우선 권장합니다.
파라미터를 잡는 감각
Argon2id에는 메모리 크기, 반복 횟수, 병렬도라는 세 손잡이가 있습니다. Ciphek은 메모리 64MiB, 반복 3회, 병렬도 1, 출력 32바이트로 잡았습니다. 메모리 64MiB는 OWASP가 인터랙티브 로그인에 제시하는 최소 수준으로, 브라우저에서 무리 없이 돌면서도 크래킹 비용을 의미 있게 올리는 균형점입니다. 병렬도를 1로 둔 것은 브라우저 WASM이 단일 스레드로 실행되기 때문이라, 병렬도를 올려도 실질 이득이 없고 오히려 예측 가능성만 떨어집니다.
여기서 중요한 것은 이 파라미터가 마법의 숫자가 아니라 배포 환경과 사용자 경험 사이의 협상 결과라는 점입니다. 메모리를 더 키우면 안전하지만 저사양 모바일에서 로그인이 버벅이고 반복을 더 늘리면 체감 지연이 길어집니다. 미디어 클라우드는 데스크톱과 모바일을 모두 지원해야 했기에, 가장 약한 기기에서도 견딜 수 있는 상한을 먼저 정하고 그 안에서 메모리를 최대한 확보하는 순서로 결정했습니다.
솔트와 실행 위치
파생에는 사용자별 무작위 솔트가 필요합니다. Ciphek은 32바이트 솔트를 클라이언트에서 생성해 서버에 저장합니다. 솔트는 비밀이 아니라 레인보우 테이블을 무력화하고 같은 패스워드를 쓰는 두 사용자가 같은 키를 갖지 않게 하는 장치입니다. 로그인 시 서버가 솔트를 돌려주면 브라우저가 패스워드와 합쳐 다시 같은 마스터키를 파생합니다. 서버는 솔트만 알 뿐 마스터키는 결코 보지 못합니다.
실행 위치도 설계의 일부입니다. Argon2id는 데스크톱에서도 1초에서 3초 정도 스레드를 붙잡기 때문에 메인 스레드에서 돌리면 로그인하는 동안 화면이 얼어붙습니다. 그래서 파생은 반드시 Web Worker 안에서만 수행하고 UI 스레드는 진행 상태만 표시합니다. 무거운 암호 연산을 워커로 격리하는 이 패턴은 이후 파일 키 언래핑이나 청크 복호화에도 그대로 재사용됩니다.
파생 다음에 일어나는 일
Argon2id가 뱉어낸 32바이트는 그 자체로 파일을 암호화하는 데 쓰이지 않습니다. 이 마스터키는 오직 다음 단계인 HKDF의 입력이 되어 용도별 서브키로 다시 갈라집니다. 마스터키를 직접 여러 곳에 쓰면 한 용도에서 생긴 약점이 다른 용도로 번지기 때문에, 파생과 분리를 두 단계로 나누는 것이 원칙입니다. 마스터키 자체도 사용이 끝나면 메모리에서 지우고 필요할 때마다 패스워드에서 다시 파생하는 것을 기본으로 삼았습니다.
이 첫 단계의 목표는 단 하나, 사람이 기억하는 약한 비밀을 기계가 쓰는 강한 키로 안전하게 바꾸되 그 과정을 오직 사용자 브라우저 안에 가두는 것입니다. 이 전제가 무너지면 뒤에 쌓은 모든 암호 계층이 의미를 잃습니다. 그래서 제로널리지 미디어 클라우드에서 KDF 선택과 실행 위치는 가장 먼저, 가장 보수적으로 결정했습니다.
파생된 마스터키가 어떻게 용도별 서브키로 갈라지는지는 HKDF 도메인 분리 서브키에서 이어집니다. 전체 아키텍처는 제로널리지 암호화 미디어 클라우드 사례에서 볼 수 있습니다.
자주 묻는 질문
PBKDF2를 쓰면 안 되나요?
쓸 수는 있지만 미디어 클라우드처럼 오프라인 크래킹을 진지하게 가정해야 하는 제품에는 부족합니다. PBKDF2는 메모리 하드니스가 없어 GPU 병렬 크래킹에 취약하고 같은 방어 수준을 내려면 반복 횟수를 비현실적으로 올려야 합니다. Argon2id는 메모리를 점유하게 만들어 병렬 하드웨어의 이점을 줄이므로, 같은 사용자 경험에서 더 높은 방어를 제공합니다.
메모리 64MiB, 반복 3회면 충분한가요?
이 값은 OWASP 2024 기준의 인터랙티브 로그인 최소치이며 브라우저와 모바일에서 견딜 수 있는 상한 안에서 잡은 균형점입니다. 절대적인 정답은 없고 기기 성능이 올라가면 파라미터를 상향하는 것이 맞습니다. 중요한 것은 숫자 자체보다 가장 약한 지원 기기에서 먼저 상한을 정하고 그 안에서 최대치를 확보하는 절차입니다.
왜 반드시 Web Worker에서 돌리나요?
Argon2id는 의도적으로 무거워 데스크톱에서도 몇 초간 CPU를 붙잡습니다. 메인 스레드에서 실행하면 그동안 UI가 응답하지 않아 사용자는 앱이 멈춘 것으로 느낍니다. Web Worker로 격리하면 파생이 도는 동안에도 화면이 살아 있고 진행 표시를 보여줄 수 있어 보안과 사용성을 동시에 지킬 수 있습니다.