"엿보고 싶어도 못 봅니다": 제가 직접 만든 종단간 암호화 파일 전송 툴 phntm.sh 이야기
2026. 3. 27.
"엿보고 싶어도 못 봅니다": 제가 직접 만든 종단간 암호화 파일 전송 툴 phntm.sh 이야기
솔직히 말해, 개인정보처리방침을 매번 설명하는 것도 지쳤고, 그 복잡한 문구를 제가 온전히 이해하고 있다는 확신도 없었습니다. 파일을 보낼 때마다 '우리는 당신의 파일을 보지 않습니다'라는 문구를 굳이 믿어야 했죠. 내 문서로 AI 모델을 학습시키진 않을까? 혹시 내 파일을 몰래 읽고 있진 않을까? 이런 의심은 저뿐만 아니라 누구에게나 당연한 불안감이었습니다. 그리고 그 어떤 서비스도 '진짜'로 그러지 않는다는 걸 제가 검증할 방법은 없었습니다. 여러분도 마찬가지였을 겁니다.
그래서 저는 그냥 제가 직접 만들었습니다. 이름하여 phntm.sh. 물론 아직 완벽하진 않지만, 이게 무엇이고, 무엇이 아니며, 어디가 개선될 여지가 있는지 솔직하게 이야기해볼까 합니다.
핵심 아이디어: 서버는 아무것도 모른다 (Zero-Knowledge)
'제로-놀리지(Zero-Knowledge)'라는 개념은 서버가 사용자 파일을 '읽지 않을 것이다'가 아니라, '읽을 수 없다'는 것을 의미합니다. 이 차이는 정말 중요합니다. '안 한다'는 건 언제든 변할 수 있는 의지에 기반하지만, '할 수 없다'는 건 아키텍처와 기술적 제약에 기반한 약속이니까요. 제가 실무에서 보안 관련 프로젝트를 진행할 때마다 강조하는 부분이지만, '의도적으로 안 한다'는 말은 사실 믿을 수 없습니다. '기술적으로 할 수 없다'가 진짜 신뢰의 시작이죠.
작동 방식은 이렇습니다. 파일을 phntm에 드롭하는 순간, 사용자의 브라우저에서 256비트 AES 키가 생성됩니다. 그리고 단 한 바이트라도 내 기기를 떠나기 전에, 파일은 클라이언트 측에서 AES-256-GCM 방식으로 암호화됩니다. 서버로 전송되는 것은 오직 이 암호문(ciphertext)뿐입니다. 중요한 복호화 키는 URL 프래그먼트, 즉 URL의 # 뒤에 붙는 부분에 포함되죠.
여기서 핵심은 브라우저가 HTTP 요청에 이 프래그먼트 부분을 절대 포함하지 않는다는 점입니다. 이는 RFC 3986 표준에 명시된 규칙입니다. 여러분이 phntm 링크를 공유하면, 받는 사람의 브라우저가 암호문을 다운로드하고, URL 프래그먼트에서 키를 가져와 로컬에서 복호화합니다. 제 서버는 이 키를 절대로, 정말 단 한 번도 보지 못합니다.
결국, 제 서버에는 그저 무의미한 '노이즈'만 저장될 뿐입니다. 키 없이는 암호문은 수학적으로 전혀 쓸모가 없으니까요.
왜 오픈소스여야만 했는가
'우리를 믿으세요'는 아키텍처가 될 수 없습니다. 제가 아키텍처 설계를 할 때마다 늘 고민하는 지점인데, '믿음'은 시스템의 구성 요소가 될 수 없습니다. 오직 기술적 검증만이 신뢰를 줍니다.
그래서 저는 웹 앱과 CLI 도구 모두를 오픈소스화했습니다. 제 말을 무조건 믿지 마세요. 직접 눈으로 확인하고 검증할 수 있도록 말이죠. 암호화 레이어 코드를 직접 읽어보고, 복호화 키가 정말 어디로도 전송되지 않는지 확인해보세요. 보안 관련 주장을 할 때는 이것만이 가장 정직한 방법이라고 생각합니다.
솔직히 말해서, 아직 거친 부분들이 있습니다
이건 제품 출시가 아니라 '공개 개발(build in public)' 과정이기에, 저는 솔직하게 이야기하려 합니다.
-
메모리 사용 문제: 현재 암호화 과정에서 전체 파일을 메모리에 버퍼링합니다. 파일 크기가 커지면 문제가 될 수 있는 부분이죠. 물론 인지하고 있고, 개선 목록에 있습니다.
-
CLI 플래그 파싱: CLI의 플래그 파싱 기능은 아직 기본적인 수준입니다. 외부 라이브러리 대신 직접 구현했는데, 좋은 학습 경험이었지만 견고함 측면에서는 아쉬운 점이 있습니다.
-
Vercel Analytics 이슈: 페이지에 Vercel Analytics를 사용하고 있었습니다. 한 분이 이 문제를 지적해주셨는데, 정말 정확한 지적이었습니다. 브라우저가 HTTP 요청에 프래그먼트를 포함하지 않는다는 RFC 규약은 유효합니다. 하지만 Vercel Analytics는 클라이언트 측에서
location.href를 읽어들이는데, 여기에 해시(프래그먼트)가 포함되고 이를 엔드포인트로 POST 요청하는 문제가 있었습니다. 해시가 바로 복호화 키인데 말이죠!이 문제는 이벤트가 발생하기 전에 프래그먼트를 제거하는
beforeSend훅을 사용해 해결했습니다. 여기서 큰 교훈을 얻었습니다. 보안 관련 주장을 할 때는 '별 생각 없이 추가했던' 써드파티 스크립트까지 포함해서 모든 외부 스크립트를 철저히 감사해야 한다는 것을요. 제가 실무에서 써드파티 라이브러리를 도입할 때 항상 강조하는 부분이기도 한데, '편의성'이라는 이름 뒤에 숨은 보안 리스크는 생각보다 훨씬 큽니다. 특히 민감한 데이터를 다룰 때는 더욱 그렇죠.
지금 당장 phntm.sh이 빛을 발하는 곳
phntm.sh은 어떤 도구도 설치되어 있지 않은 사람에게 파일을 보낼 때 특히 유용합니다. 링크를 받아서 클릭하면, 브라우저에서 자동으로 복호화되고 바로 다운로드됩니다. 계정을 만들 필요도, 앱을 설치할 필요도, 심지어 회원가입조차 필요 없습니다. 이 부분은 정말 사용자 경험(UX) 측면에서 큰 강점이라고 생각합니다.
게다가 타이머가 만료되면 파일은 자동으로 파괴됩니다. 그 이후로는 침해될 정보 자체가 존재하지 않죠.
이걸 만들면서 배운 것들
CLI 도구는 제 첫 실제 Go 프로젝트였습니다. 튜토리얼을 따라 하는 수준이 아니라, 제가 실제로 사용할 무언가를 만든 것이었죠.
Go의 I/O 모델이 '아하!' 하고 깨달음으로 다가온 계기는 io.Reader를 래핑(wrapping)해서 진행률 바를 만든 경험이었습니다. HTTP 클라이언트가 io.Copy를 통해 데이터를 복사하고, 바이트가 흘러가는 만큼 진행률 바가 스스로 업데이트되는 모습을 보면서 컴포저빌리티(Composability)에 대한 제 사고방식이 완전히 바뀌었습니다. 작은 부분이지만, Go의 인터페이스 기반 설계 철학을 깊이 이해하는 데 결정적인 역할을 했습니다.
CLI 전체는 표준 라이브러리(stdlib-only)만 사용했습니다. 외부 의존성이 전혀 없다는 거죠. 이는 의도적인 선택이었고, 동시에 학습에 있어서도 매우 좋은 제약이 되어주었습니다. 제가 다른 언어로 CLI 도구를 만들 때는 무심코 외부 라이브러리에 의존했던 부분이 많았는데, 이번 프로젝트를 통해 표준 라이브러리의 깊이와 유연성을 다시 한번 체감할 수 있었습니다.
프로젝트는 여기에 있습니다
- 웹 서비스: https://phntm.sh
- 웹 앱 GitHub: https://github.com/aliirz/phntm.sh
- CLI GitHub: https://github.com/aliirz/phntm-cli
여러분의 피드백은 언제든 환영입니다. 특히 암호화 레이어에 대한 전문적인 의견은 언제나 귀 기울여 듣겠습니다. 함께 더 나은 프라이버시 보호 도구를 만들어갈 수 있기를 바랍니다. 읽어주셔서 감사합니다!
원문: https://dev.to/aliirz/i-built-a-file-transfer-tool-that-cant-spy-on-you-even-if-it-wanted-to-2p39 수집일: 2026-03-27 22:40:20