[P2P 보안] P2P에서 trust boundary는 어디에 그어야 할까
PonsWarp에서 초대 링크를 눌러 파일을 받는 장면을 떠올려 본다. 브라우저끼리 직접 연결됐고, 채널은 암호화되어 있고, 서버를 덜 거쳤다. 그래도 안심하기에는 이르다. 상대 peer가 어떤 파일을 요청할 수 있는지, 방 안에서 어떤 행동을 할 수 있는지, 잘못된 요청을 얼마나 자주 보낼 수 있는지는 아직 따로 정해야 한다. 직접 연결이 열렸을 때 제품은 무엇을 믿고, 무엇을 끝까지 의심해야 할까?

P2P 보안이 커진 문제
- peer id와 사용자 계정을 어떤 필드로 분리해 기록하는가?
- 초대 링크의 최소 권한과 만료 시각은 어디서 검증하는가?
- capability가 특정 시간, 방, 파일, 행동보다 커지는 순간은 없는가?
- 암호화 채널 밖에서 막아야 할 잘못된 파일, spam 요청, 권한 없는 행동은 무엇인가?
- rate limit과 block list를 중앙 서버가 적은 구조에서 어떻게 집행하는가?
- audit log에 남길 metadata와 남기지 않을 metadata를 둘 다 정했는가?
- revoke 뒤 이미 연결된 peer에게 어떤 사용자 문구를 보여 줄 것인가? metadata 결정은 특히 조심스럽다. abuse를 분석하려면 어떤 peer가 어떤 시간에 어떤 capability로 어떤 요청을 했는지 일부 단서가 필요하다. 하지만 민감한 제품에서 모든 metadata를 길게 남기면 다른 위험을 만든다. 그래서 audit 정책은 “무엇을 남길까”와 “무엇을 절대 남기지 않을까”를 한 문서에서 같이 다뤄야 한다. capability를 작게 만들면 구현은 조금 번거로워진다. 파일 받기, 댓글 쓰기, 방 입장, 화면 공유, 재전송 요청을 각각 다른 권한으로 봐야 하기 때문이다. 대신 문제가 생겼을 때 전체 연결을 끊지 않고 특정 행동만 막을 수 있다. 사용자에게도 “상대가 위험합니다”보다 “이 링크로는 이 작업을 할 수 없습니다”가 덜 과격하고 더 정확하다. identity, capability, encryption, audit가 한 연결 버튼 뒤에서 어떤 순서로 만나는지 보는 흐름.

처음에는 피어를 다 믿으려 했다
또 하나의 경계는 파일 검증이다. 암호화 채널을 통해 파일을 받았더라도 그 파일이 약속한 파일인지, 크기와 hash가 맞는지, 상대가 너무 많은 조각을 요구하지 않는지 확인해야 한다. P2P 전송에서 안전하다는 말은 채널만이 아니라 payload와 요청 빈도까지 포함해야 한다. 이 구분은 다음 Merkle/checksum 노트로도 이어진다. 제품에서는 peer를 신뢰하는 것이 아니라 특정 시간, 특정 방, 특정 파일, 특정 행동만 허용하는 capability를 만들어야 한다.
이 기준이 없으면 “링크를 가진 사람”이라는 넓은 표현이 곧 권한 정책이 된다. 데모는 빨라지지만 abuse가 시작된 뒤 회수하기 어렵다. 상태 이름도 좁게 두는 편이 낫다. pending-identity, offered-capability, verified-encryption, relayed-abuse, stored-audit처럼 나누면 연결 실패라는 한 단어 뒤에 숨은 원인을 볼 수 있다. identity가 비어 있는지, capability가 거절됐는지, encryption 협상이 실패했는지, abuse 조건에 걸렸는지, audit 기록이 누락됐는지 분리된다. rate limit과 block list는 중앙 서버가 없을수록 더 설계하기 어렵다.
직접 연결을 중시하는 구조에서는 모든 요청을 중앙에서 검사하기 어렵고, peer가 떠난 뒤에도 abuse 근거가 남아야 한다. 그래서 어떤 metadata를 남길지와 남기지 않을지를 동시에 결정해야 한다. 민감한 제품에서는 많이 남기는 것도 위험하고, 아무것도 남기지 않는 것도 운영을 막는다. 구현 전에는 아래 질문을 먼저 통과시키는 편이 안전하다. 답이 없으면 보안 기능이 부족한 것이 아니라 제품 약속이 아직 넓게 열려 있다는 뜻이다.

신뢰 붕괴가 선명해진 순간
capability를 작게 쪼개면 지원 대응도 달라진다. 문의가 들어왔을 때 운영자는 연결 로그만 보는 것이 아니라 거절된 capability, 만료 시각, revoke 여부, rate limit 발동 여부를 같이 봐야 한다. 이 정보가 없으면 보안 정책이 사용자에게는 임의의 실패처럼 보인다. 직접 연결을 줄였다는 장점보다 설명되지 않는 거절이 더 크게 남는다. 보안 실험은 자동화 테스트만으로 끝내기 어렵다.
사용자가 링크를 복사해서 다른 사람에게 보내는 장면, 탭을 닫았다 다시 여는 장면, 네트워크가 바뀌어 peer id가 바뀌는 장면을 같이 봐야 한다. 공격자는 항상 정교한 취약점만 노리지 않는다. 오래 살아 있는 권한, 너무 친절한 재시도, 과한 기본 허용값도 충분한 입구가 된다. P2P 보안 실험은 공격자처럼 작게 시작해야 한다. 만료된 링크를 다시 쓴다.
권한이 낮은 peer가 큰 파일을 요청한다. 정상 peer처럼 보이는 쪽이 작은 spam 메시지를 반복해서 보낸다. 암호화 채널이 열렸다는 사실은 이 행동들을 막지 않는다. 제품이 허용한 capability만 실제로 통과하는지 확인해야 직접 연결을 안전하다고 말할 수 있다. 암호화된 DataChannel이나 전송 경로는 필요한 방어선이다. 다만 암호화가 보장하는 것은 전송 중 노출을 줄이는 일이지, 요청의 의도가 안전하다는 뜻은 아니다.
잘못된 파일, 큰 파일을 반복 요청하는 peer, 만료된 링크 재사용, spam 메시지, 권한 없는 삭제 요청은 다른 층에서 막아야 한다. P2P 보안을 흐리게 만드는 첫 번째 착각은 사용자 계정과 peer identity를 같은 것으로 보는 것이다. peer id는 연결을 구분하는 데 필요하지만, 계정 소유자나 초대받은 사람을 자동으로 증명하지 않는다. 같은 사용자가 여러 기기에서 들어올 수도 있고, 오래된 링크가 다른 사람에게 전달될 수도 있다.
경계가 작을수록 UI도 더 구체적이어야 한다. “권한이 없습니다” 한 문장만 두면 사용자는 다시 시도해야 하는지, 새 링크가 필요한지, 방장에게 요청해야 하는지 모른다. capability 이름을 그대로 노출하지 않더라도 다음 행동은 알려 줘야 한다. P2P를 설명할 때 서버/클라이언트/사용자 중 누구 문장으로 말하는지 먼저 고르면 논쟁이 줄었다.
trust boundary 그리기
P2P 보안 모델은 P2P를 더 순수하게 만드는 장치가 아니다. 책임의 위치를 드러내는 지도에 가깝다. identity와 capability 일부는 peer와 브라우저로 옮겨지고, abuse와 audit 일부는 edge나 서버에 남고, 나머지는 사용자가 이해할 문장으로 번역된다. 마지막 기준은 이렇다. 직접 연결을 믿는 순간이 아니라, 허용된 행동이 가장 작아지는 순간에 trust boundary를 그어야 한다.
보안 경계는 연결이 실패했을 때보다 연결이 성공했을 때 더 자주 흐려진다. 화면에 “연결됨”이 뜨면 사용자는 상대를 믿어도 된다고 느끼고, 개발자는 암호화 채널이 열렸으니 큰 위험이 줄었다고 생각하기 쉽다. 하지만 제품이 허용한 행동 범위가 넓으면 성공한 연결이 곧 공격면이 된다. 운영자가 가장 빨리 알아야 할 신호는 “누가 성공했는가”보다 “무엇이 거절됐는가”다. 거절 로그가 쌓이면 제품은 권한 정책을 좁히거나 문구를 바꿀 수 있다. 거절이 보이지 않으면 사용자는 불편을 겪고, 운영자는 보안 정책이 효과가 있는지조차 판단하지 못한다. revoke도 같은 이유로 중요하다. 초대 링크가 만료됐거나 방장이 권한을 회수했는데 이미 연결된 peer가 계속 요청을 보낼 수 있다면 trust boundary는 화면에만 있는 셈이다. 연결 상태와 권한 상태를 분리해서 보지 않으면, 제품은 직접 연결의 편리함 뒤에 회수 불가능한 권한을 남긴다.
메커니즘이 버틴 이유
PonsLink/PonsWarp에 이 기준을 적용하면 보안은 기능 출시 뒤에 붙이는 장식이 아니다. 방 생성, 링크 공유, 파일 전송, 재시도, 차단, 문의 대응까지 같은 권한 모델을 공유해야 한다. 그래야 작은 제품에서도 중앙 서버가 덜 보이는 순간을 안전하게 다룰 수 있다. 테스트 계정과 운영 계정의 차이도 따로 본다. 내부 테스트에서는 모든 권한을 가진 계정으로 빠르게 지나가고 싶지만, 실제 사용자는 좁은 초대와 만료된 링크를 더 자주 만난다.
보안 QA는 관리자 계정이 아니라 권한이 부족한 peer에서 시작해야 한다. P2P라는 말에는 친밀한 느낌이 있지만 직접 연결은 신뢰의 증거가 아니다. peer id는 네트워크 식별자이고 사용자 신원 보증은 별도 문제다. 암호화는 전송 중 노출을 줄이지만 잘못된 파일이나 spam 요청을 막지 않는다. 보안 채널은 길을 보호하고, capability는 행동을 제한하고, audit log는 나중에 설명할 근거를 남긴다.
이전 노트인 2026-07-04-p2p-protocol-04-crdt-p2p-collaboration이 merge 책임을 물었다면, 여기서는 identity와 capability의 경계를 본다. PonsLink와 PonsWarp에서 필요한 질문은 단순하다. “P2P를 켤 수 있는가?”가 아니라 “이 peer에게 지금 이 행동만 허용할 근거가 있는가?”다. PonsLink/PonsWarp의 작은 제품 맥락에서는 큰 중앙 정책으로 뒤늦게 덮는 전략이 잘 맞지 않는다. 처음부터 방, 링크, 파일, 행동 권한을 작게 나누고 거절 로그를 읽을 수 있어야 한다. 번거로워 보여도 나중에 정책을 고치는 비용은 이쪽이 낮다.
'[P2P 보안 P2P에서 trust boundary는 어디에 그어야 할까'을 관통하는 질문은 이 섹션에서도 같았고, 답만 더 구체적이 됐다.
아직 열어 둔 비용
- capability 거절 수와 거절 이유
- 만료 링크 재사용 시도
- 비정상 요청 차단 수
- peer별 rate limit 발동 수
- block list 적용 뒤 재시도 양상
- audit log 누락률
- 사용자가 다시 시도한 횟수와 문의 문구
이 표의 핵심은 권한을 크게 주지 않는 것이다. 초대 링크에는 최소 권한과 만료가 있어야 한다. 파일 받기 권한과 파일 삭제 권한은 다른 capability여야 한다. 방에 들어오는 권한과 모든 peer에게 요청을 보내는 권한도 분리해야 한다. 사용자 문구도 이 경계를 따라가야 한다. “P2P 보안 확인 중”보다 “이 링크로는 파일 받기만 가능합니다”, “이 방에서는 공유 요청이 만료되었습니다”, “이 peer는 이 작업을 요청할 수 없습니다”가 더 정확하다.
쉬운 말이 책임을 흐리면 보안 문구로는 실패다. PonsWarp처럼 링크 기반 전송을 다루는 제품에서는 링크 공유가 곧 보안 UX다. 링크가 만료됐을 때, 이미 열린 세션이 남아 있을 때, 수신자가 다시 시도할 때의 문구를 정해야 한다. 링크를 가진 사람에게 어디까지 허용할지 말하지 않으면 사용자는 편리함으로 느끼지만 운영자는 회수하기 어려운 권한을 떠안는다.
운영에서 볼 것은 성공한 연결보다 거절된 행동이다. 어떤 peer가 어떤 capability를 들고 왔고, 어떤 작업이 왜 막혔고, 거절 뒤에 사용자가 어떤 문구를 봤는지 남아야 한다. 그래야 보안을 단순한 불편함으로만 느끼지 않게 만들 수 있다. 예를 들어 파일 내용은 남기지 않더라도 파일 크기 범위, 요청 횟수, 거절된 capability 이름, 만료된 링크 재사용 여부는 운영에 도움이 될 수 있다.
반대로 사용자 신원을 과하게 추론할 수 있는 정보는 짧게 보존하거나 해시 처리해야 한다. P2P라고 해서 개인정보 문제가 사라지는 것은 아니다.
마치며
P2P 기능을 켜기 전 peer identity와 capability 범위를 다시 묻는 점검표. *직접 연결을 믿은 순간 권한이 넓어지는 경로를 따로 표시한 실패 지도. | 경계 | 믿어도 되는 것 | 따로 확인할 것 | 실패했을 때 보일 문제 | |---|---|---|---| | peer identity | 네트워크 연결의 상대를 구분한다 | 사용자 신원, 세션 소유권 | 낯선 기기가 같은 사용자처럼 보임 | | capability | 특정 시간·방·파일·행동을 제한한다 | 만료, 재사용, 권한 범위 | 링크 하나로 너무 많은 행동이 가능함 | | encryption | 전송 중 노출을 줄인다 | 데이터 의미, spam, 잘못된 파일 | 암호화됐지만 위험한 요청이 통과함 | | abuse boundary | rate limit, block list, revoke 정책을 둔다 | 중앙 서버가 적을 때의 집행 방식 | 악성 peer가 작은 요청을 계속 보냄 | | audit | 거절된 행동과 근거를 남긴다 | 남길 metadata와 버릴 metadata | 사용자는 불편을 느끼고 운영자는 원인을 못 봄 |
P2P 보안에서 제품 문구가 중요한 이유도 여기에 있다. 사용자가 보는 문장은 “안전합니다”가 아니라 “이 작업은 허용되지 않습니다”에 가까워야 한다. 전자는 너무 넓고, 후자는 근거를 확인할 수 있다. 거절 사유가 작게 나뉘면 사용자는 다시 초대를 받거나, 권한이 있는 계정으로 들어가거나, 파일 요청 범위를 줄일 수 있다.