내가 만든 도구가 데이터를 안 보낸다는 걸, 내가 검증하면 증명이 아니다
브라우저에서만 도는 법률 문서 도구의 핵심 약속은 사건 파일이 밖으로 안 나간다는 것이다. 그런데 그 검증을 만든 사람이 자기 컴퓨터에서 돌리면 아무것도 증명하지 못한다. 이 독립성 문제를 하루 동안 풀었다.
3줄 요약
- 브라우저 안에서만 도는 법률 문서 도구가 사건 파일을 밖으로 안 보낸다는 약속을, 만든 사람이 자기 컴퓨터에서 검증하면 증명이 되지 않는다는 지적을 받고 검증 구조를 다시 설계했다.
- 이게 중요한 이유는 프라이버시 약속의 신뢰는 검증을 누가 어디서 돌렸는지에 달려 있기 때문이다. 검증 코드가 아무리 정교해도 실행 주체가 검증 대상과 같으면 값이 없다.
- 빌더에게 남는 교훈은 하나다. 신뢰는 증명하는 쪽이 조작할 수 없는 사실에서만 나온다. 애매하면 실패로 판정하게 설계하라.
자기가 만든 검증을 자기가 돌리면 증명이 아니다
프라이버시 약속의 증명은 실행 주체와 실행 환경이 결정한다. 문제의 시작은 날카로운 지적 하나였다. 이 도구는 사건 파일을 서버로 안 보내고 브라우저 안에서만 처리한다는 게 핵심 약속인데, 그걸 검증하는 테스트를 만든 사람이 자기 컴퓨터에서 돌리고 있었다. 검증 스크립트가 "이 실행은 독립적입니다"라는 표시를 스스로 붙이고 있었는데, 그 표시는 만든 사람이 자기 컴퓨터에서도 똑같이 붙일 수 있다. 즉 독립성의 증거가 아니라 자기 선언일 뿐이었다.
이건 코드 버그가 아니라 인식론의 문제다. 내가 "안 보낸다"고 말하고, 내가 만든 테스트를 내가 돌려서 "안 보냈다"는 결과를 얻으면, 바깥에서 보는 사람 입장에서 새로 알게 되는 사실이 없다. 신뢰는 그런 식으로 만들어지지 않는다.
신뢰는 증명하는 쪽이 조작할 수 없는 사실에서만 나온다
해법은 독립성을 스스로 선언하는 대신 조작 불가능한 사실에서 끌어내는 것이었다. 검증을 만든 사람 컴퓨터가 아니라, 외부 자동화 환경이 매번 새로 만드는 일회용 실행기에서 돌리게 했다. 그 환경은 실행할 때마다 저장소 신원, 실행 식별자, 실행 주체, 실행 환경 종류 같은 값을 자동으로 주입한다. 이 값들은 만든 사람이 나중에 손댈 수 없다. 검증 결과는 이 주입된 사실로부터 "이 실행이 정말 독립 환경에서 돌았는가"를 파생시킨다.
핵심 설계 원칙은 애매하면 실패로 판정하는 것이었다. 실행 환경이 외부 자동화가 아니거나, 주입된 사실이 빠져 있거나, 로컬에서 돌린 흔적이 있으면 전부 "자기 실행"으로 판정한다. 자기 선언만으로는 독립성을 주장할 수 없게 막았다. 실제로 같은 검증을 내 컴퓨터에서 흉내 내 돌려보니 판정이 정확히 "독립 아님"으로 나왔다. 이게 옳다. 로컬은 원래 독립일 수 없으니까.
배포와 검증을 분리하는 결정
그다음 갈림길은 사람의 몫이었다. 이 독립 검증을 실제로 돌리려면 코드를 외부에 올려야 하는데, 그 올리는 행위가 곧 아직 검증이 끝나지 않은 제품을 공개 배포하는 것과 묶여 있었다. 되돌리기 어렵고 바깥으로 향하는 행동이라 자동으로 밀어붙이지 않고 결정을 넘겼다.
결정은 명확했다. 배포는 막고 검증만 계속한다. 그래서 코드를 올려도 자동 배포가 걸리지 않게 방아쇠를 떼어내고, 오직 수동 실행으로만 검증이 돌게 분리했다. 그 뒤 외부 자동화 환경에서 검증이 실제로 세 번 돌았다. 처음 두 번은 폰트 문제와 서명 기능 미지원으로 걸렸고, 각각 원인을 고쳤다. 세 번째에 완전히 통과했다. 그 결과물을 다시 내려받아 대조하니 독립성 판정이 사실로부터 파생되었고, 외부로 나간 통신 기록은 비어 있었으며, 저장소에 남은 사건 데이터도 없었다.
오늘 하루를 관통한 판단
정직하게 남는 한계도 기록했다. 이건 글자로 된 문서 경로에 대한 독립 증거일 뿐이고, 사람이 하는 법무 검증과 폴더 권한 문제는 아직 남아 있어서 출시는 여전히 멈춰 있다.
그래도 오늘 배운 건 분명하다. 어떤 약속의 증명은 그 약속을 하는 쪽이 만질 수 없는 곳에서 나와야 한다. 프라이버시든 정확성이든, "내가 내 걸로 확인했다"는 아무리 정교해도 출발선이지 결승선이 아니다. 조작 불가능한 사실에 판정을 걸고, 애매한 경우를 전부 실패로 떨어뜨리는 설계가 신뢰의 뼈대다.