Investigation into Facebook's surveillance capabilities on iOS devices.
Website (fb.definitelynot.ai)
Astro SSR site with steganographic key-based authentication.
Architecture
- undefined
Security Features (Implemented 2026-01-15)
| Feature | Implementation |
|---|---|
| Session persistence | File-based at `/var/lib/fbexposed/sessions.json` |
| Token hashing | SHA-256 hash stored, not plaintext |
| Key hashing | Keys stored as SHA-256 hashes |
| Rate limiting | 5 attempts/minute per IP |
| Generic errors | All auth failures return "Authentication failed" |
| Image size limit | 5MB max upload |
Key Management
Keys are stored as SHA-256 hashes on the server. The key image contains the plaintext key which gets hashed during validation.
Generate New Key (on server)
Bash
ssh adept
cd /var/www/fb.definitelynot.ai
node /usr/local/bin/fbexposed-keygen.mjs generate /path/to/output.png
List Valid Keys
Bash
node /usr/local/bin/fbexposed-keygen.mjs list
Revoke a Key
Bash
node /usr/local/bin/fbexposed-keygen.mjs revoke <hash-prefix>
Local Key Generation
From local machine (requires sharp):
Bash
cd ./website
node /usr/local/bin/fbexposed-keygen.mjs generate ./keys/new-key.png
Key Files
Valid key images are stored in `./keys/`:
| File | Description |
|---|---|
| `tom-base.png` | Base image for encoding keys |
| `key-3-1c0797a5.png` | Valid key (hash: 1c0797a5...) |
**Note**: Key images encode the key `FBEXPOSED-<64-char-hex>` into pixel values along row 0.
Server Paths
| Path | Purpose |
|---|---|
| `/var/www/fb.definitelynot.ai/` | SSR deployment |
| `/var/lib/fbexposed/valid-keys.json` | Hashed valid keys |
| `/var/lib/fbexposed/sessions.json` | Active sessions |
| `/var/lib/fbexposed/tom-base.png` | Base image for key generation |
Building & Deploying
Bash
cd ./website
npm run build
rsync -avz dist/ adept:/tmp/fb-dist/
ssh adept "sudo rm -rf /var/www/fb.definitelynot.ai.backup && \
sudo mv /var/www/fb.definitelynot.ai /var/www/fb.definitelynot.ai.backup && \
sudo mv /tmp/fb-dist /var/www/fb.definitelynot.ai && \
sudo chown -R caddy:caddy /var/www/fb.definitelynot.ai"
ssh adept "cd /var/www/fb.definitelynot.ai && \
sudo cp /var/www/fb.definitelynot.ai.backup/package.json . && \
sudo chown caddy:caddy package.json && \
sudo -u caddy npm install --omit=dev"
ssh adept "sudo systemctl restart fb-exposed"
Service Management
Bash
ssh adept "systemctl status fb-exposed"
ssh adept "journalctl -u fb-exposed -f"
ssh adept "sudo systemctl restart fb-exposed"
Project Structure
Plain Text
CLAUDE.md # This file
├── keys/ # Valid key images
│ ├── tom-base.png # Base image for encoding
│ └── key-3-*.png # Valid key images
├── website/ # Astro source
│ ├── src/
│ │ ├── lib/ # Auth, sessions, rate limiting
│ │ ├── pages/ # Routes
│ │ └── components/ # React components
│ └── dist/ # Build output
├── evidence/ # Investigation evidence
├── scripts/ # Analysis scripts
└── data-exfil/ # Data exfiltration research
Steganography Format
Keys are encoded into images using LSB-style encoding:
- undefined
The encoding uses high/low values (220/35) for each bit, making extraction robust against minor image manipulation.