먼저 좁은 결론을 적어둡니다. 열람 횟수에 도달하거나 만료된 뒤에는 서버의 암호문이 없고, 같은 번호로 원문을 다시 받을 수 없습니다. 이 문장은 「상대가 스크린샷을 안 찍었다」, 「단톡방에 제3자가 안 열었다」, 「채팅 백업에 전체 링크가 없다」를 뒷받침하지 않습니다. 확인 방법은 두 번 열기와 Network이지, 화면의 「파기됨」을 상대 기기 감사로 읽는 일이 아닙니다.
검색 의도를 한 문장으로 먼저 답하기
열람 후 파기가 뒷받침하는 것은 호스트가 더 이상 암호문을 들고 있지 않다는 점입니다. 상대의 화면, 클립보드, 채팅 기록이 지워졌다는 주장은 뒷받침하지 않습니다. 암호문을 한 번 성공적으로 가져온 뒤 같은 링크를 다시 열면, 열람 페이지는 파기됨 또는 만료됨을 보여 줍니다. HTTP 410 Gone의 의미는 원본 서버에서 대상 자원이 더 이상 쓸 수 없고, 그 상태가 대개 영구라는 것입니다. 이 코드가 답하는 질문은 「서버가 아직 내주느냐」이지, 「상대가 사본을 남겼느냐」가 아닙니다.
이 글은 개발자, 운영, 그리고 일회용 비밀번호·API Key·복구 코드를 동료에게 보내기 전에 「링크가 죽었다」와 「상대가 아무것도 안 남겼다」를 나누고 싶은 사람을 위한 것입니다. 열람 후 파기 사용 설명을 대신하지 않으며, 「링크를 어떻게 만드는가」도 반복하지 않습니다. 가져가야 할 것은 어느 층이 타는지, 새로고침 실패가 증명하는 범위, 스크린샷이 왜 그 밖에 있는지, 두 번 열기와 Network로 호스트 쪽을 그 자리에서 대조하는 방법입니다.
「파기됨」과 「상대가 사본을 안 남겼다」는 다릅니다
일상 말에서는 서로 다른 세 가지를 「읽고 나면 사라진다」한 문장으로 묶습니다. 첫째, 서버가 암호문을 더 이상 보관하지 않는다. 그 번호의 기록이 지워지고, 이후 요청은 파기됨 또는 만료됨만 받습니다. 둘째, 지금 연 탭이 평문을 더 이상 보여 주지 않는다. 탭을 닫거나, 「이 페이지에서 지우기」를 누르거나, 떠날 때 입력칸을 비우는 스크립트가 이 일을 합니다. 셋째, 상대 기기에 사본이 없다. 스크린샷도, 붙여넣기도, 전체 링크 전달도, 평문을 다시 채팅에 붙인 일도 없다.
열람 후 파기가 강제할 수 있는 것은 첫째뿐입니다. 둘째는 기껏해야 이 탭의 표시 영역을 비우는 데 그칩니다. 셋째에는 강제력이 없습니다. 열람 페이지가 「복사나 스크린샷은 막지 못한다」고 쓰는 것은 겸손이 아니라 경계입니다. 일반 웹페이지는 다른 휴대폰이 화면을 찍는 일을 막을 수 없고, 상대가 「평문 복사」를 누른 뒤에 시스템 클립보드나 메신저의 로컬 저장소를 지울 수도 없습니다.
따라서 「내가 새로고침했는데 안 열린다」가 뒷받침하는 결론은 좁습니다. 이번 번호로 암호문을 달라고 했을 때 서버가 내주지 않았다는 것입니다. 「그 동료만 봤다」, 「스크린샷을 안 찍었다」, 「티켓 검색에 이 문자열이 다시 안 나온다」는 뒷받침하지 않습니다. 뒤의 문장들을 확인할 수 있는 사실로 쓰려면, 보낸 쪽의 두 번째 열기가 아니라 평문이나 전체 URL이 이 탭을 나갔는지를 봐야 합니다.
서버가 실제로 지우는 층
제로 지식 열람 후 파기는 위치와 복호화를 나눕니다. 형태는 s.html?id={id}#{key}입니다. 물음표 뒤에는 번호만 있고, 키는 해시 뒤에만 있습니다. RFC 9110은 요청의 대상 URI에 프래그먼트를 넣지 않는다고 적습니다. 브라우저가 서버에 문서를 요청할 때 #와 그 뒤를 뗍니다. 설계상 서버가 보는 것은 번호이며, 키도 평문도 아닙니다.
만들 때 이 탭은 Web Crypto로 AES-256-GCM을 씁니다. 난수 32바이트 키, 12바이트 IV, 평문을 이 기기에서 암호문으로 만든 뒤 POST /secrets합니다. 나가는 필드는 암호문, 만료, 열람 횟수입니다. 키는 이후에 조립하는 공유 링크에만 나타납니다. 한 건은 32 KB까지입니다. 만료는 1시간, 24시간, 7일, 또는 열람 후에만 파기를 고를 수 있고, 열람 횟수는 1–10이며 기본값은 1입니다. 만들기와 읽기 모두 열어 바로 사용합니다. 계정이 없고, 「누가 열었는지」를 표시할 로그인 신원도 없습니다.
읽을 때 서버는 암호문을 내주고 설정 횟수에 도달하면 그 암호문을 삭제합니다. 보낸 쪽이 다시 열면 파기됨 또는 만료됨을 봅니다. 이것이 증명하는 것은 호스트가 그 암호문 바이트를 더 이상 들고 있지 않다는 점입니다. 키가 채팅 기록에 한 번도 안 나타났다는 것, 평문이 상대 화면에 안 떴다는 것, 제3자가 같은 전체 링크에서 「열어 보기」를 안 눌렀다는 것은 증명하지 않습니다.
전체 URL을 가진 사람은 키도 가집니다. https://…/s.html?id=…#…를 단톡방에 붙이면 번호와 열쇠를 함께 넘긴 것입니다. 서버가 이미 암호문을 지웠어도, 그때 이미 열고 평문을 남긴 사람을 되돌릴 수는 없습니다. 횟수가 남아 있으면 나중에 누른 사람도 암호문을 가져갈 수 있습니다. 암호문을 지우는 일은 「호스트가 평문이나 암호문을 오래 보관한다」는 층을 줄입니다. 「문자열 전체를 본 사람」이라는 층은 건드리지 않습니다.
새로고침 실패가 증명하는 것과, 증명하지 못하는 것
두 번째 열기가 실패해도 상태는 하나가 아닙니다. 세 가지 안내를 나누세요. 파기됨: 암호문이 없다. 횟수를 다 썼거나, 번호가 처음부터 유효하지 않았을 수 있습니다. 만료됨: 만들 때 고른 TTL이 끝나 아무도 안 읽어도 서버가 지웁니다. 프래그먼트 키 없음: 번호는 남아 있을 수 있으나 이 기기는 복호화할 수 없습니다. 카카오톡, Slack, Teams, 일부 메일 클라이언트가 # 뒤를 잘라 냅니다. 수신자가 보는 「프래그먼트 키가 없습니다」는 「다른 사람이 이미 읽었다」와 같은 사건이 아닙니다.
410 Gone은 원본 서버에서 대상 자원을 더 이상 쓸 수 없고, 그 상태가 영구일 가능성이 크다는 뜻입니다. 열람 페이지는 410을 파기됨과 만료됨으로 나눕니다. 응답에 expired가 있으면 TTL이고, 없으면 파기로 처리합니다. 404도 파기됨으로 보여 줍니다. 수신자에게 결과는 같기 때문입니다. 다시 암호문을 가져갈 수 없습니다. 네트워크 실패는 파기가 아닙니다. 「열어 보기」를 누른 뒤에 끊겼고 만들 때 한 번만 읽도록 했다면, 이 탭이 복호화하지 못했더라도 암호문은 이미 가져가졌을 수 있습니다.
그러므로 「내 쪽에서 새로고침하니 안 열린다」가 뒷받침하는 것은 이것입니다. 지금 이 번호로는 서버가 암호문을 내주지 않는다. 뒷받침하지 않는 것은 이렇습니다. 연 사람이 지명한 그 동료다. 복사하지 않았다. 스크린샷을 안 찍었다. 미리보기나 보안 스캐너가 횟수를 먼저 쓰지 않았다. 계정이 없으면 이름을 붙인 읽음 확인도 없습니다. 횟수는 서버 쪽 카운터이지, 상대 기기의 감사가 아닙니다.
실제 비밀번호, API Key, 마스킹하지 않은 접속 문자열로 실험하지 마세요. 이번에만 쓰는 테스트 문장을 준비하세요. 대조하는 것은 상태 인터페이스, 암호문 가져가기, 두 번째 열기의 안내입니다. 살아있는 자격 증명을 다시 노출하는 일이 아닙니다.
스크린샷, 복사, 채팅 기록이 이 층 밖에 있는 이유
평문이 화면에 그려지는 순간, 질문은 「서버에 암호문이 있는가」에서 「상대 단말에서 무슨 일이 있었는가」로 바뀝니다. 시스템 스크린샷, 화면 녹화, 다른 휴대폰으로 찍는 사진은 호스트에 요청을 보내지 않습니다. 열람 페이지는 그 동작을 가로챌 수 없고, 사후에 알려 줄 수도 없습니다. 어떤 제품이 기능을 빠뜨린 것이 아닙니다. 일반 웹페이지에는 「상대가 자기 화면을 기록하지 못하게 하라」는 스위치가 주어지지 않습니다.
복사도 같은 울타리 밖에 있습니다. 열람 페이지는 「평문 복사」를 제공할 수 있고, 떠날 때 칸을 비울 수 있습니다. 비우는 것은 이 DOM 노드이지, 시스템 클립보드도, 메신저 초안함도 아닙니다. macOS와 일부 입력기는 클립보드 기록을 남깁니다. 「이 페이지에서 지워짐」은 지금 탭이 그 문장을 더 이상 보여 주지 않는다는 뜻일 뿐입니다. 그 문장이 클립보드에 한 번도 안 들어갔다는 뜻이 아닙니다.
채팅 기록은 또 다른 저장소입니다. 전체 링크가 단톡방에 한 번 나타났다면, 대화 검색, 여러 기기 동기화, 이후 내보내기에서 해시 뒤의 키가 다시 보입니다. 서버가 암호문을 지운 뒤에도 URL은 「한때 유효했다」는 증거가 되고, 횟수가 다 쓰인 줄 모르는 사람이 반복해서 눌러 「링크가 고장 났다」고 오해하기에도 충분합니다. HTTP 층은 여전히 깨끗할 수 있습니다. Referer는 규격상 프래그먼트를 실으면 안 됩니다. 다만 채팅 기록을 열 수 있는 사람에게는 그 보호가 이미 없습니다.
티켓, 메일, 브라우저 히스토리도 같은 분리입니다. 주소창에 보였던 # 프래그먼트는 대개 로컬 히스토리에 들어가고, 동기화를 켠 브라우저는 그 줄을 다른 기기로 보낼 수 있습니다. 탭을 닫는 것은 히스토리를 지우는 일이 아닙니다. 열람 후 파기가 줄이는 것은 서버가 조각을 들고 있는 시간과, 같은 암호문을 반복해서 가져갈 기회입니다. 디지털 저작권 관리가 아니고, 포렌식 보고서도 아닙니다.
상태 조회가 횟수를 쓰지 않는 이유
랜딩을 처음 열 때 암호문을 가져간다면, 카카오톡 링크 미리보기, 메일 보안 게이트웨이, 일부 사내 스캐너가 수신자보다 먼저 유일한 한 번을 쓸 수 있습니다. 수신자가 눌렀을 때는 「파기됨」만 보이고, 키는 미리보기 호스트에 넘어간 적이 없습니다. 타 버린 것은 열람 횟수이지, 프래그먼트의 비밀이 아닙니다. 이전 글은 해시가 기본적으로 HTTP에 안 들어가는 이유를 적었습니다. 이 글은 횟수만 봅니다. 미리보기 요청과 「실제로 암호문을 가져가는 일」을 나누지 않으면, 「파기됨」은 「수신자가 봤다」조차 증명하지 못합니다.
FastPwd 열람 페이지는 이 두 단계를 나눕니다. 페이지를 열면 /secrets/{id}/status만 요청해 암호문이 아직 있는지만 묻고, 가져가지는 않습니다. 수신자가 「열어 보기」를 누른 뒤에야 GET /secrets/{id}로 암호문을 가져오고, 해시 뒤의 키로 이 기기에서 복호화합니다. # 키가 없으면 페이지는 암호문을 가져가지 않아 남은 횟수를 낭비하지 않습니다. 만들기 페이지와 열람 페이지 모두 계정이 필요 없습니다.
미리보기 봇은 보통 랜딩만 가져가고 버튼을 대신 누르지 않습니다. 「미리보기 = 파기」의 확률은 낮아지지만, 모든 스캐너가 랜딩에서 멈춘다는 증명은 아닙니다. Network에서는 먼저 status가 보이고, 그다음에 암호문 가져가기가 보여야 합니다. 열기만 하고 클릭하지 않으면, 암호문을 성공적으로 가져간 응답이 나오면 안 됩니다. 횟수는 여전히 서버 쪽 카운터이며, 프래그먼트의 일부가 아닙니다.
| 보이는 현상 | 뒷받침할 수 있는 결론 | 뒷받침할 수 없는 결론 |
|---|---|---|
| 두 번째 열기에 파기됨 | 지금 이 번호로는 암호문을 가져갈 수 없다 | 상대가 스크린샷·복사를 안 했다 |
| 만료됨이 뜬다 | TTL이 끝나 서버가 삭제했다 | 반드시 누군가 성공적으로 읽었다 |
| 프래그먼트 키가 없다 | 주소에 # 뒤의 키가 없다 |
암호문이 반드시 이미 가져가졌다 |
| 페이지만 열고 버튼을 안 눌렀다 | status 조회는 횟수를 쓰면 안 된다 | 모든 미리보기 봇이 암호문을 안 가져간다 |
| Network의 POST에 암호문만 있다 | 이번 만들기가 원문을 업무 필드로 보내지 않았다 | 전체 링크를 채팅에 붙인 뒤에도 나만 본다 |
| 이 페이지에서 평문을 지웠다 | 지금 탭이 그 문장을 더 이상 보여 주지 않는다 | 클립보드와 스크린샷에도 없다 |
그 자리에서 대조하기: 두 번 열기, status, Network
구호는 증거가 되지 않습니다. canary-fp-20260905-burn-vs-shot처럼 버려도 되는 카나리아를 준비하세요. 실제 비밀번호를 재사용하지 마세요. 열람 횟수는 기본값 1로 두고, 만료는 24시간이면 충분합니다.
- 만들기 페이지에 카나리아를 넣고 링크를 만듭니다. 개발자 도구의 Network를 열고 Preserve log를 켭니다. 카나리아와 해시 뒤의 키를 각각 검색하세요.
POST /secrets본문에는 암호문, 만료, 열람 횟수만 있어야 하며, 원문이나 키는 없어야 합니다. - 같은 전체 링크로 열람 페이지를 열고, 아직 「열어 보기」는 누르지 마세요. Network에는
/secrets/{id}/status가 보여야 하고, 암호문을 성공적으로 가져간GET /secrets/{id}는 없어야 합니다. 페이지는 확인 대기에 머물러야 합니다. - 「열어 보기」를 누릅니다. 이때야 번호로 암호문을 가져오고, 이 기기에서 AES-256-GCM으로 엽니다. 성공 안내에는 서버가 이 암호문을 삭제했다고 쓰이거나—횟수를 1보다 크게 두었다면—설정 횟수에 도달해야 삭제한다고 쓰입니다.
- 열람 페이지를 새로고침하거나, 새 브라우저 컨텍스트에서 같은 링크를 엽니다. 기본 1회라면 파기됨이 보여야 합니다. Network를 다시 검색하면 암호문 가져가기는 410이거나 파기로 처리되는 응답이어야 합니다. 카나리아가 어떤 요청 본문에도 평문으로 다시 나오면 안 됩니다.
이 네 단계가 증명하는 변화는 하나입니다. 당신이 통제하는 두 번 열기 사이에, 서버의 암호문이 「있음」에서 「없음」으로 바뀌었다. 이것이 실제 비밀번호를 동료에게 보냈을 때 상대가 스크린샷을 안 찍었다는 증명은 되지 않습니다. 「상대가 남길지」는 인수인계 메모에 적어야 합니다. 캡처하지 말 것, 평문을 단톡방에 다시 붙이지 말 것, 쓰고 나면 바로 교체할 것. 화면의 「파기됨」이 상대 단말을 대신 감사해 주기를 바라면 안 됩니다.
전체 링크와 암호문을 같은 경로로 보내지 마세요. 클라우드나 메일로는 .lock을 보내고, 비밀번호는 열람 후 파기로 넘기세요. 열람 후 파기의 전체 URL을 그 폴더 메모에 다시 적지 마세요. 열쇠와 상자를 한 메시지에 넣으면, 암호문을 지워도 문자열 전체를 이미 본 사람은 되돌릴 수 없습니다.
「파기됨」을 너무 넓게 읽는 세 가지
첫째, 만료를 누군가 읽은 증거로 봅니다. 만들 때 1시간을 골랐고, 동료가 다음 날 누르면 안내는 만료됨입니다. 서버는 암호문을 지웠지만, 이유는 TTL이지 횟수 소진이 아닙니다. 만료되어 읽지 못한 내용은 되돌릴 수 없고, 「이미 열렸다」와 같지도 않습니다.
둘째, 프래그먼트 없음을 이미 파기된 것으로 봅니다. 카카오톡과 일부 메일 클라이언트는 # 뒤를 잘라 냅니다. 수신자가 연 주소는 s.html?id=…이고 키가 없습니다. 열람 페이지는 키가 없으면 암호문을 가져가지 않습니다. 당신이 나중에 전체 링크를 열면 내용이 아직 있을 수 있습니다. 이때는 해시가 포함된 전체 문자열을 다시 달라고 해야지, 「누가 먼저 봤다」고 단정하면 안 됩니다.
셋째, 이 페이지에서 지움을 전역 삭제로 봅니다. 열람 페이지는 떠날 때 칸을 비울 수 있고, history.replaceState로 주소창의 프래그먼트를 뗄 수 있습니다. 영향은 지금 탭의 표시와, 이후 새로고침에 키가 남는지뿐입니다. 시스템 스크린샷, 클립보드 기록, 채팅 백업, 동기화된 브라우저 히스토리는 이 지우기 범위 밖에 있습니다.
자주 묻는 질문
상대가 새로고침한 뒤 「파기됨」을 보면, 내용을 남기지 않았다는 뜻인가요?
아닙니다. 서버에 암호문이 더 이상 없어서, 같은 번호로 원문을 다시 받을 수 없다는 뜻일 뿐입니다. 스크린샷, 복사, 화면 녹화, 채팅 백업, 전체 링크를 제3자에게 넘기는 일은 이번 삭제를 거치지 않습니다. 「파기됨」은 상대 기기가 깨끗하다는 증거가 아닙니다.
카카오톡 링크 미리보기가 내용을 먼저 파기할 수 있나요?
미리보기는 보통 랜딩 페이지만 엽니다. 열람 페이지는 먼저 암호문이 아직 있는지만 묻고 가져가지 않으며, 「열어 보기」를 눌러야 번호로 암호문을 가져갑니다. 미리보기 봇은 대개 그 버튼을 대신 누르지 않지만, 모든 스캐너가 랜딩에서 멈춘다고 단정할 수는 없습니다. 횟수는 서버 쪽 카운터입니다. 대조 방법은 이렇습니다. 열기만 하고 클릭하지 않으면, Network에 암호문을 성공적으로 가져간 요청이 나오면 안 됩니다.
열람 후 파기는 누가 열었는지 증명할 수 있나요?
없습니다. 만들기와 읽기 모두 계정이 없고, 서버도 로그인 신원으로 열람자를 표시하지 않습니다. 확인할 수 있는 것은 이번 가져가기 뒤에 그 번호에 암호문이 남아 있는지뿐입니다. 「파기됨」을 누가 열었는지로 뒤집을 수 없고, 읽음 확인으로 적을 수도 없습니다.
만들기와 읽기에 가입이 필요한가요? 평문을 올리나요?
가입은 필요 없습니다. 지금 연 탭이 AES-256-GCM으로 암호화한 뒤 암호문만 올립니다. 키는 링크의 # 프래그먼트에 둡니다. 카나리아로 Network를 검색하면 본문에는 암호문만 있어야 하며, 원문이나 해시 뒤의 키는 없어야 합니다. 32 KB를 넘거나 파일 전체라면 파일 암호화에서 이 기기로 처리하세요. 열람 후 파기를 클라우드 드라이브처럼 쓰지 마세요.
같은 카나리아를 열어 바로 사용하는 도구로 한 번 대조하기
계산 경계를 글로 적어 둔 페이지에서 연습하려면 FastPwd 열람 후 파기를 여세요. 가입은 필요 없습니다. 위의 카나리아를 넣고 열람 횟수는 1로 둔 채 링크를 만든 뒤, 네 번째 절의 두 번 열기를 끝까지 하세요. 동시에 Network를 보세요. 만들기 POST의 암호문, 열람 페이지의 status, 클릭 뒤의 가져가기, 두 번째 열기의 파기 응답이 보여야 합니다. 원문이나 해시 뒤의 키가 업무 필드로 보이면 안 됩니다.
비밀번호를 반드시 동료에게 넘겨야 할 때만 이 경로를 쓰세요. 난수 비밀번호는 비밀번호 생성기에서 이 기기로 만들 수 있습니다. 길이는 6–128자, 기본값은 16자이며, 8자 미만이면 보안이 낮다고 알려 줍니다. 생성 과정은 올리지 않습니다. 계약서 전체, 신분증 스캔, 32 KB를 넘는 자료는 파일 암호화에서 같은 AES-256-GCM으로 .lock / .enc를 만들고, 한 파일은 5 GB를 넘지 않습니다. 비밀번호는 따로 열람 후 파기로 보내고, 클라우드 폴더 메모에 적지 마세요.
다음에 보내기 전에 세 가지만 기억하세요. 첫째, 파기됨이 증명하는 것은 서버의 암호문이 없어졌다는 것이지, 상대가 스크린샷을 안 찍었다는 것이 아닙니다. 둘째, 랜딩만 열면 횟수를 쓰면 안 되고, 「열어 보기」를 눌러야 암호문을 가져갑니다. 셋째, 전체 링크를 채팅에 붙이면 해시 뒤의 키는 그 메시지를 볼 수 있는 사람에게 이미 평문으로 보입니다. 반복해서 관찰할 수 있는 사실만 여러분 인수인계 메모에 적을 가치가 있습니다.
더 읽을거리
해시 뒤가 기본적으로 HTTP에 안 들어가는 이유와, 전체 링크를 붙일 때 어느 층이 사라지는지는 URL 해시 뒤에 키를 두는 이유, 그리고 그 보호가 깨질 때에 있습니다. 「로컬 암호화, 올리지 않음」은 카나리아로 Network를 검색하기 전에는 증거가 되지 않습니다. 브라우저 로컬 암호화에서 원문이 이 기기를 떠나지 않았는지 확인하는 방법. 티켓 본문의 신분증 번호를 이 기기를 떠나기 전에 가리려면 티켓과 로그를 AI에 붙이기 전에, 어떤 항목이 이 기기를 떠나는지 먼저 확인하는 방법을 보세요.