🕵️ 친구와 암호 대화
한국어

서버가 내용을 모르게 만드는 설계 (제로 지식 구조)

서비스 운영자가 "저희는 여러분의 글을 보지 않습니다"라고 말하는 것과, 보고 싶어도 볼 수 없게 만들어 두는 것은 전혀 다릅니다. 앞의 것은 약속이고 뒤의 것은 구조입니다. 이 글에서는 서버가 내용을 알 수 없도록 설계한다는 것이 무슨 뜻인지, 친구와 암호 대화에서는 서버에 무엇을 두고 무엇을 두지 않았는지를 정리합니다. 여기서 말하는 제로 지식 구조는 엄밀한 수학적 용어라기보다 서버가 내용에 대해 아는 것이 없도록 하는 설계 방향을 가리키는 말로 이해해 주시면 됩니다.

서버가 침해된다는 가정에서 출발합니다

어떤 서버든 해킹당하거나 내부자가 마음먹으면 데이터가 새어 나갈 수 있습니다. 운영자가 아무리 선량해도 서버에 있는 것은 언젠가 유출될 수 있다고 가정하는 편이 현실적입니다. 그러면 질문이 바뀝니다. "서버를 어떻게 지킬까"에서 "서버가 뚫려도 곤란하지 않게 하려면 무엇을 서버에 두지 말아야 할까"로 말입니다.

가장 확실한 답은 가진 적 없는 것은 잃어버릴 수도 없다는 것입니다. 글의 내용, 해독 키, 사용자의 비밀번호를 서버가 아예 받지 않는다면 서버가 털려도 그 부분은 새지 않습니다. 이 서비스는 이 원칙을 따릅니다.

서버가 실제로 저장하는 것

막연한 설명보다 구체적인 목록이 낫습니다. 서버 코드가 번역기 하나에 대해 보관하는 정보는 다음과 같습니다.

반대로 서버가 받지도 저장하지도 않는 것은 번역기 주소의 # 조각에 있는 번역기 키, 사용자의 비밀번호, 사용자가 쓴 글, 그리고 만들어진 비밀어입니다. 비밀어는 메신저로 오가는 것이지 이 서버를 거치지 않습니다. 이 서비스의 서버 코드 머리말에도 서버는 주소 키, 비밀번호, 평문을 받지 않는다고 적혀 있습니다.

키를 서버에 보내지 않는 장치들

핵심은 해독 키가 서버 밖에서 만들어지고 서버 밖에만 있다는 점입니다. 번역기 키는 URL의 # 조각에 담겨 서버로 전송되지 않고, 글은 브라우저 안에서 암호화됩니다. 비밀번호가 있는 번역기에서도 비밀번호 자체는 전송되지 않습니다. 서버에 가는 것은 비밀번호에서 여러 단계로 계산한 인증 토큰이고, 서버는 이를 다시 해시한 값만 보관합니다. 이 과정이 PBKDF2와 키 유도 글에 나옵니다.

또한 서버가 내주는 server_part는 해독 키 조립의 재료 중 하나일 뿐입니다. 서버는 server_part를 알지만 번역기 키와 비밀번호가 없으므로 해독 키를 만들 수 없습니다. 반대로 번역기 주소만 가진 사람은 server_part가 없어서 키를 만들 수 없습니다. 어느 한쪽이 혼자서는 해독할 수 없도록 재료를 나눠 둔 셈입니다.

서버가 그래도 알 수 있는 것

내용을 모른다고 해서 아무것도 모르는 것은 아닙니다. 정직하게 적어 두겠습니다. 서버는 어떤 번역기 id가 언제 만들어지고 언제 마지막으로 열렸는지, 비밀번호가 걸려 있는지, 오입력이 있었는지를 압니다. 번역기 정보를 요청하는 접속이 있다면 그 접속의 IP 주소와 시각도 웹 서버 수준에서 보일 수 있습니다. 이 서비스는 속도 제한을 위해 IP를 날짜별로 해시한 값으로만 메모리에 두고 장기 보관하지 않지만, 접속 사실 자체는 지울 수 없습니다.

누가 누구와 대화하는지, 대화가 얼마나 오가는지 같은 메타데이터는 이 서비스가 아니라 비밀어를 실어 나르는 메신저에 남습니다. 그 메신저는 비밀어가 있다는 사실과 길이, 시각을 알 수 있습니다. 내용을 가린다고 해서 대화 사실이 가려지는 것은 아닙니다.

침해 사고를 가정해 보는 사고 실험

구조가 말뿐이 아닌지 확인하려면 서버의 저장 내용이 전부 공개되었다고 상상해 보면 됩니다. 공격자는 번역기 id 목록과 솔트, 해시 값, server_part를 얻습니다. 그런데 이 값들만으로는 글을 풀 수 없습니다. 해독 키는 번역기 키에서 시작하는데 번역기 키가 목록 어디에도 없기 때문입니다. 비밀어도 서버에 없으니 풀어 볼 암호문 자체가 없습니다. 메신저에 오간 비밀어를 따로 입수했다고 해도, 번역기 키가 없으면 마찬가지입니다.

비밀번호를 건 번역기라면 한 겹이 더 있습니다. 공격자가 번역기 키를 어떻게든 얻었다고 해도 비밀번호와 PBKDF2 계산이 필요합니다. 반대로 번역기 키 없이 비밀번호만 대입해 보는 것은 의미가 없습니다. 각 재료가 서로 다른 곳에 흩어져 있어서 한 곳이 뚫린다고 전부 열리지 않는 구조입니다. 물론 이것은 키와 비밀번호가 새지 않았을 때의 이야기이며, 이용자의 주소 관리가 허술하면 이 이점은 줄어듭니다.

이 구조가 주는 장점과 대가

장점은 분명합니다. 서버가 침해되어도, 또는 운영자나 외부 요청이 있어도 서버가 내줄 수 있는 글이 없습니다. 이용자는 가입 없이 주소만으로 쓸 수 있고, 서비스는 보관할 개인 정보를 최소로 둘 수 있습니다.

하지만 대가도 있습니다. 가장 큰 것은 복구가 불가능하다는 점입니다. 번역기 주소를 잃어버리면 서버에도 키가 없으므로 운영자도 도와 드릴 방법이 없습니다. 비밀번호를 잊었을 때도 마찬가지입니다. 비밀번호 찾기 기능은 이런 구조에서는 만들 수 없습니다. 편리함과 비밀 보장 사이에서 후자를 택한 결과입니다.

또 서버가 내려주는 웹 화면의 코드를 이용자가 매번 믿어야 한다는 한계가 남습니다. 서버는 내용은 몰라도 화면 코드는 내려주기 때문입니다. 그래서 이 서비스는 외부 스크립트를 두지 않고, 콘텐츠 보안 정책으로 스크립트와 연결 대상을 이 사이트로 제한하며, 출처 정보를 보내지 않는 설정을 붙여 위험을 줄이려 합니다. 이는 완화일 뿐 완전한 해결은 아닙니다.

정리하며

이 설계를 한 문장으로 말하면 서버가 가진 것으로는 글을 풀 수 없게 하자는 것입니다. 서버가 가진 것과 이용자만 가진 것을 나누고, 두 쪽이 만나는 곳을 브라우저 안으로 한정했습니다. 이용자 입장에서는 주소와 비밀번호를 잘 관리해야 하는 책임이 생기지만, 그 대신 서버를 믿어야 하는 부분이 줄어듭니다.

서버가 내용을 모르게 만드는 설계는 서버를 믿지 않아도 되도록 키를 서버 밖에 두는 설계입니다. 이것이 모든 위협을 막아 주지는 않습니다. 막아 주는 것과 못 막는 것의 경계는 이 서비스가 막아 주는 것과 막아 주지 못하는 것에서 자세히 다룹니다. 구조를 직접 경험해 보시려면 번역기 열기로 이동해 보세요.

잘못된 내용이나 보완할 점을 발견하셨다면 소개·문의의 이메일로 알려 주세요. 확인 후 글을 고치고 수정일을 갱신합니다.

함께 읽으면 좋은 글

다음 글: 이 서비스가 막아 주는 것과 막아 주지 못하는 것 → 번역기 열기