
브라우저 저장소와 HTTP 캐시 기법이 방문자를 지속적으로 재식별할 수 있는 방법을 보여주는 교육용 evercookie 데모.
웹사이트가 어떻게 당신을 지속적으로 식별하는지, 그리고 "쿠키를 지우는 것"이 더 이상 충분하지 않은 이유를 보여주는 교육용 데모입니다.
ubercookie는 단일 무작위 id를 한 번에 12개의 서로 다른 브라우저 저장소 벡터에 심습니다. 방문할 때마다 모든 벡터를 읽고, 합의(consensus)를 거쳐 id를 모든 곳에 다시 작성합니다. 저장소 하나를 지우거나 쿠키를 모두 지워도, 살아남은 벡터가 조용히 id를 *부활(respawn)*시킵니다. 이 사이트는 일반 언어로 id가 정확히 어디에 숨어 있는지, 브라우저를 얼마나 자주 인식했는지 보여줍니다.
이것은 Samy Kamkar의 고전 evercookie와 같은 아이디어입니다. 여기서는 저장소 부활과 슈퍼쿠키 지속성에 초점을 맞춘 개방적이고 투명한 교육 도구로 구축되었습니다.
[!IMPORTANT] 이 프로젝트는 인식 제고를 위해 방문자를 의도적으로 추적합니다. 퍼스트파티 전용이며, 무작위 id, 방문 횟수, 첫/마지막 방문 타임스탬프, 복구 출처 레이블만 저장하고 제3자와 공유하지 않으며, 실제 "Forget me" 버튼을 제공합니다. 사람들의 인지나 동의 없이 추적하는 용도로 변경하지 마세요. 그것은 이 프로젝트의 목적과 반대입니다. 윤리 참조.
| Vector | 종류 | JS 필요? | JS로 삭제 가능? | 설명 |
|---|---|---|---|---|
document.cookie | 클라이언트 | 예 | 예 | 기본 트래커 |
localStorage | 클라이언트 | 예 | 예 | 쿠키 삭제를 견딤 |
sessionStorage | 클라이언트 | 예 | 예 | 탭별 중복성 |
IndexedDB | 클라이언트 | 예 | 예 | 사람들이 지우는 것을 잊는 전체 DB |
Cache API (caches) | 클라이언트 | 예 | 예 | 위와 분리된 프로그래밍 가능한 저장소 |
window.name | 클라이언트 | 예 | 예 | 탐색 간에도 유지됨 |
| OPFS (Origin Private File System) | 클라이언트 | 예 | 예 | 수동 정리가 놓치는 샌드박스 파일시스템 |
| Service Worker + Cache | 클라이언트 | 예 | 예 | 백그라운드 스크립트가 오프라인에서도 id를 다시 제공 |
서버 쿠키 (HttpOnly) | 서버 | 아니요 | 서버를 통해 | JS에 보이지 않지만 모든 요청에 전송됨 |
| ETag 슈퍼쿠키 | 서버 | 아니요 | 캐시 삭제만 | If-None-Match에 id가 다시 반향됨 |
| Last-Modified 슈퍼쿠키 | 서버 | 아니요 | 캐시 삭제만 | 캐시된 리소스의 날짜에 id가 인코딩됨 |
| HTTP 캐시 (id 내장 스크립트) | 서버 | 아니요 | 캐시 삭제만 | immutable 캐시 파일에 id가 내장됨 |
이에 더해, 페이지는 브라우저에 영구 저장소(persistent storage)(navigator.storage.persist())를 요청하여 IndexedDB / Cache / Service Worker / OPFS 사본이 자동 삭제(eviction)에서 제외되도록 합니다. 즉, 이를 더욱 제거하기 어렵게 만듭니다.
세 가지 캐시 기반 벡터가 지속성의 핵심입니다. 이들은 브라우저의 HTTP 캐시에 존재하므로 JavaScript(우리 자신의 "Forget me" 포함)는 이를 삭제할 수 없습니다. 오직 브라우저 캐시를 지우는 것만이 가능합니다. 그렇게 해서 id는 죽음에서 돌아옵니다.
구현된 저장소 벡터에 대한 전체 설명과 이 프로젝트에 여전히 들어맞는 HSTS 슈퍼쿠키 아이디어는 **docs/techniques.md**에 있습니다.
ubercookie/
├── backend/ FastAPI — server-side vectors + the observation log (SQLite)
│ └── app/
│ ├── main.py endpoints: /api/visit, /api/whoami, /api/etag-id,
│ │ /api/lastmod-id, /api/cache-id.js, /api/clear-cookie,
│ │ /api/forget
│ ├── store.py "we've seen this browser N times" memory
│ └── ids.py mint/validate the 32-hex tracking id
└── frontend/ Vanilla JS + Vite — the dashboard and the client vectors
└── src/ubercookie/
├── index.js orchestrator: read-all → consensus → respawn → report
└── vectors/ one self-contained module per storage vector
방문이 동작하는 방식 (frontend/src/ubercookie/index.js):
HttpOnly 쿠키를 설정합니다.요구 사항: Python ≥ 3.11 (uv 포함) 및 Node ≥ 18.
make install # backend deps (uv) + frontend deps (npm)
# then, in two terminals:
make backend # FastAPI on http://localhost:8000
make frontend # Vite on http://localhost:5173 (proxies /api → :8000)
**http://localhost:5173**을 열고 당신이 추적되는 것을 지켜보세요. DevTools를 열고 일부 저장소를 삭제한 다음 Re-scan을 누르면 부활하는 것을 볼 수 있습니다.
두 서버를 동시에 실행하는 한 줄 명령:
./scripts/dev.sh
make build # → frontend/dist
cd backend && uv run uvicorn app.main:app --port 8000
# open http://localhost:8000
둘 모두를 하나의 오리진에서 서빙하는 것이 가장 정확한 설정입니다. 캐시/ETag 벡터는 실제 동일 오리진 HTTP 캐싱에 의존하기 때문입니다.
make test # backend pytest (server-side vector logic + observation log)
make build # frontend production build
백엔드 테스트는 서버 측 벡터와 관찰 로그를 검증합니다. 브라우저 동작은 여러 벡터가 오리진 저장소, Service Worker, HTTP 캐싱에 의존하므로 실제 브라우저로 확인해야 합니다.
이것은 방어 / 인식 제고 도구입니다. 설계에 내장된 지침은 다음과 같습니다.
포크(fork)도 같은 정신을 유지해 주세요: 추적이 어떻게 작동하는지 사람들에게 가르쳐 스스로 방어할 수 있게 하는 것, 몰래 추적하는 데 사용하지 마세요.
MIT — LICENSE 참조.