저자 주: 이 글에서 언급된 공격은 현재의 I2P 버전에서는 불가능합니다.
자기 조직화된 피어 투 피어 네트워크로서 I2P는 네트워크에 무엇이 존재하는지와 그것에 어떻게 도달할 수 있는지에 관한 정보를 공유할 방법을 네트워크에 참여하는 router가 갖추고 있다는 점에 의존한다. I2P router는 NetDB를 사용하여 이러한 정보 공유를 수행하는데, NetDB는 Kademlia를 기반으로 하되 I2P에 맞게 수정된 DHT이다. NetDB는 두 가지 주요 유형의 엔트리를 공유해야 한다. 피어가 다른 router와 직접 통신할 때 사용하는 “RouterInfos”(router 정보)와, 다른 피어가 익명 tunnel을 통해 I2P 클라이언트와 통신할 때 사용하는 “LeaseSets”(클라이언트와의 익명 tunnel 통신을 위한 정보)이다. router는 자주 서로 NetDB 엔트리를 주고받는데, 정보를 router나 클라이언트에게 보내거나, router나 클라이언트로부터 정보를 요청하는 방식이다. 이는 네트워크의 필요와 클라이언트의 역량에 따라 엔트리가 직접 또는 간접으로, 익명 또는 비익명으로 도착할 수 있음을 의미한다. 그러나 익명화 네트워크인 만큼 익명으로 전송된 정보를 비익명으로 되돌려 요청하는 것이 불가능하게 유지되는 것도 중요하다. 또한 비익명으로 전송된 정보를 익명으로 되돌려 요청하는 것이 불가능해야 한다는 점도 중요하다. 그 둘 중 어느 상황이라도 가능해지면, 공격자가 클라이언트와 router가 NetDB에 대한 공통된 관점을 공유하는지 판별할 수 있게 하는 링킹 공격을 수행할 수 있다. 두 대상이 NetDB에 대한 공통된 관점을 공유한다는 것을 신뢰성 있게 확인할 수 있다면, 그들이 동일한 router에 있을 가능성이 매우 높아지며 대상의 익명성은 크게 약화된다. 익명화 네트워크가 매우 드물고 라우팅 테이블이 DHT의 동작을 통해 공유되는 곳은 I2P가 유일하기 때문에, 이러한 유형의 공격은 사실상 I2P에만 고유하며 이를 해결하는 일은 I2P의 성공에 중요하다.
다음 시나리오를 고려해 보자: I2P 클라이언트를 호스팅하는 I2P router가 있다. 이 router는 RouterInfo를 게시하고, I2P 클라이언트는 자신의 LeaseSet을 게시한다. 둘 다 NetDB에 게시되므로, 다른 I2P router들은 NetDB를 조회하여 이들과 어떻게 통신할지 알아낼 수 있다. 이는 I2P가 구현한 유형의 오버레이 네트워크가 동작하는 데 정상적이며 필수적이다. 공격자는 I2P router를 실행하고, 대상 RouterInfo와 대상 LeaseSet을 얻기 위해 NetDB를 조회한다. 그런 다음 고유하며 심지어 위조일 수도 있는 새로운 LeaseSet을 만들어, 공격 대상으로 삼은 클라이언트의 LeaseSet으로 tunnel을 통해 전송한다. 클라이언트는 그 조작된 LeaseSet을 처리하고 자신의 NetDB에 추가한다. 이후 공격자는 NetDB에서 얻은 RouterInfo를 사용하여, router로부터 그 조작된 LeaseSet을 직접 다시 요청한다. 만약 조작된 LeaseSet이 응답으로 되돌아오면, 공격자는 대상 클라이언트와 대상 router가 NetDB에 대한 공통된 뷰를 공유하고 있다고 결론지을 수 있다.
이는 한 신원으로 다른 사람의 NetDB에 항목을 추가한 뒤, 다른 신원으로 그것을 다시 요청해 가져오는 방식에 의존하는 NetDB 익명성 해제 공격 유형의 단순한 예시다. 이 경우 여기서 해당되는 신원은 “router"와 “클라이언트” 신원이다. 그러나 일부 설계에서는 피해가 더 적은 클라이언트 간 연계도 가능하다. 이러한 종류의 공격에 대한 방어책을 설계하려면, router에게 잠재적 신원과 특정 정보를 주고받는 것이 안전한지 여부를 판별할 수 있는 방법을 제공해야 한다.
그렇다면 이 문제를 어떻게 생각해야 할까요? 우리가 여기서 실제로 다루는 것은 네트워크 상의 서로 다른 “정체성"의 linkability(연결 가능성)과 관련이 있습니다. 이러한 모든 정체성들이 공통의 데이터 구조를 공유하기 때문에 연결될 가능성이 생기며, 그 데이터 구조는 자신이 누구와 통신했는지, 또 누가 자신과 통신했는지를 “기억"합니다. 또한 그 통신이 어떤 방식으로 이루어졌는지도 “기억"합니다.
잠시 공격자의 입장이 되어 보자. 당신이 변장의 달인의 정체를 밝혀내려 한다고 상상해 보라. 그의 진짜 얼굴을 본 적이 있다는 것도, 그리고 그의 변장된 모습 중 하나와 정기적으로 소통하고 있다는 것도 확실히 알고 있다. 그렇다면 그 변장된 정체와 실제 정체가 동일 인물임을 어떻게 입증할 수 있을까? 나는 변장한 사람에게 비밀을 하나 말할 수도 있다. 만약 변장하지 않은 사람이 그 비밀 정보를 사용해 반응한다면, 나는 변장하지 않은 사람이 그 비밀을 알고 있다고 판단할 수 있다. 변장한 사람이 그 비밀을 다른 누구에게도 전하지 않았다고 가정하면, 변장하지 않은 사람과 변장한 사람이 사실상 동일 인물이라고 추정할 수 있다. 변장의 달인이 아무리 많은 가면을 쓰더라도, 마음은 하나뿐이다.
I2P 클라이언트의 신원을 성공적으로 보호하려면, I2P는 위에서 설명된 것보다 더 뛰어난 위장술의 달인처럼 동작할 수 있어야 한다. NetDB에 어떻게 참여했는지에 관한 몇 가지 중요한 정보를 “기억"하고, 그 세부 사항을 바탕으로 적절히 대응할 수 있어야 한다. 다음을 기억해낼 수 있어야 한다:
- Whether a NetDB Entry was received directly, or received down a client tunnel
- Whether a NetDB Entry was sent by a peer in response to our lookup, or sent unsolicited
- Which NetDB Entry was received down Which client Tunnel
- Multiple versions of the same entry for different client tunnels
구조적으로, 이 패턴을 처리하는 가장 이해하기 쉽고 신뢰할 수 있는 방법은 “Sub-DBs.“를 사용하는 것이다. Sub-DB’s(서브 DB)는 NetDB가 항목을 놓치지 않고 정리하도록 돕는 소형 NetDB이다. 모든 클라이언트는 자체적으로 사용할 Sub-DB를 하나씩 부여받고, router 자체는 완전한 NetDB를 가진다. Sub-DB’s를 사용함으로써, 우리는 변장의 달인에게 그 비밀을 누가 그와 공유했는지에 따라 정리된 비밀의 주소록을 쥐여준다. 요청이 클라이언트로 보내질 때에는 클라이언트에게 전달된 항목만 찾고, 요청이 router로 보내질 때에는 router 전역 NetDB만 사용된다. 이렇게 하면 가장 단순한 형태의 공격을 해결할 뿐만 아니라, 전체 공격 범주의 위력도 약화시킨다.