# nilidesk — Security model Remote desktop tools are a high-value target. AnyDesk (Feb 2024 production-system compromise, code-signing cert revoked) and TeamViewer (2016 credential-stuffing wave, 2024 corporate environment breach by APT29) have both been forced into deep security reworks after public incidents. This document explains how nilidesk is built defensively against those same classes of attack, and what is **explicitly out of scope for V1**. --- ## Threat model | # | Attacker capability | nilidesk defense | |---|---|---| | T1 | Brute-force the 4–6-char session password against a known ID | Per-agent escalating lockout (3 fails → 60s, 5 → 5min, 8 → 1h, 12 → 24h) — `security.py:_LOCKOUT_THRESHOLDS` | | T2 | Enumerate valid 9-digit IDs to find online targets | Per-IP sliding window (30 attempts / 60s); all failure modes return the same 403 with the same body and the same minimum response time (800ms floor) — `server.py:_MIN_CONNECT_RESPONSE_MS` | | T3 | Tech-support scam: trick a user into installing and reading their ID + password aloud | (a) First-run scam warning popup forces the user to acknowledge that legitimate vendors never ask for this. (b) Bright red banner with viewer IP appears the moment a session starts — visible regardless of which window has focus. (c) One-click Disconnect on the banner tears down all sessions AND rolls the session password. | | T4 | Man-in-the-middle on the signaling channel | TLS-everywhere via Caddy. WebRTC DTLS-SRTP fingerprint pinning happens automatically inside aiortc / the browser — the SDP exchange carries the fingerprint, so a MITM that swaps it gets noticed at handshake. | | T5 | Server breach reveals long-term secrets | The server stores NO passwords (hashed or plain). The agent holds the only hash. A full server compromise leaks the audit log + the agent ID list — both meaningful for forensics but not for impersonation. | | T6 | Stolen credentials shared with many simultaneous attackers | Server caps concurrent viewers per agent at 3. Extras get the same generic 403. | | T7 | Code-signing certificate compromise → malicious "update" pushed to all agents | Out of scope V1 (we don't ship updates yet). When auto-update is added, the agent will pin the public key of the update server out-of-band. | | T8 | Clipboard exfiltration during a session | nilidesk V1 does not share the clipboard. Period. | | T9 | Unattended access password leak | Permanent passwords are hashed with PBKDF2-HMAC-SHA256, 200K iterations + 128-bit salt, stored in `%ProgramData%\nilidesk\agent.ini`. Plaintext is never persisted. | | T10 | Replay an SDP/ICE message captured by a passive observer | Each session has a fresh DTLS handshake. Captured SDPs are useless for replay. | --- ## What's implemented in V1 ### Server side (`security.py`, `server.py`) - **Sliding-window per-IP rate limit** at `/api/connect`. 30 attempts per 60s. - **Per-agent lockout** with four escalating tiers. Strikes decay after 10 min of inactivity. - **Constant-time response floor (800ms)** on every `/api/connect` call. Same generic 403 body for *every* failure case (wrong password, agent offline, agent locked, bad format). Indistinguishable to an attacker. - **Concurrent-viewer cap** at 3 per agent. - **Audit log** (`audit.log`, JSONL) — every connect attempt with IP, agent ID, outcome. - **Strict security headers** on every response: - `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload` - `Content-Security-Policy` with `frame-ancestors 'none'`, `default-src 'self'` - `X-Content-Type-Options: nosniff` - `Referrer-Policy: strict-origin-when-cross-origin` - `Permissions-Policy: geolocation=(), microphone=(), camera=()` - **Short-lived session tokens** (60s TTL) — viewer must open the signaling WS within that window or the token is invalidated. - **Server never sees the password** *as stored data* — it's forwarded once to the agent for validation and then forgotten. (See "Roadmap" below for end-to-end auth that closes the in-flight gap.) ### Agent side (`agent.py`) - **Red `LIVE` banner** appears the moment a session starts, showing the viewer's IP. Survives Alt-Tab, can't be hidden by the viewer. - **First-run scam warning** the user must acknowledge before the agent shows its ID/password. - **Disconnect button** on the banner tears down ALL active sessions and immediately rotates the session password. - **Auto-rotating session passwords** — fresh password on every start. Old passwords are useless even if leaked. - **PBKDF2 hashing** for the optional permanent password (200K iterations, 128-bit salt). - **No clipboard, no file transfer** in V1. Less attack surface. --- ## Roadmap (post-V1, ordered by priority) 1. **End-to-end password auth** — server forwards a `(salt, nonce)` to the viewer; viewer computes `HMAC(PBKDF2(password, salt), nonce)` in-browser; server only sees the HMAC. The server becomes a relay even for the auth step, eliminating the "trust the server" requirement. 2. **Code signing on `nilidesk_agent.exe`** — EV cert from DigiCert or similar. Without this, Windows SmartScreen warns on first run and downloads. 3. **Self-hosted coturn** — replacing the free Open Relay TURN. Open Relay is rate-limited and a privacy liability (a TURN operator sees encrypted SRTP packets without keys, but the metadata alone is concerning). 4. **Per-IP geo / ASN alerts** — when a connection comes from a country / network the user has never connected from before, prompt for accept on the agent side. 5. **2FA on permanent password** for unattended access — TOTP, app of user's choice. 6. **Tamper-evident audit log** — chain each entry to a HMAC of the previous one, so an attacker who compromises the server can't quietly delete their tracks. 7. **Auto-update with pinned key** — the agent fetches `https://nilidesk.com/updates/latest.json`, verifies a signature against a public key shipped in the binary. Mitigates T7 even if the code-signing cert is later compromised. 8. **Crash + telemetry reporting (opt-in)** — surfaces exceptions in the field so we can fix things fast. Strict opt-in, no machine-identifying data. 9. **Recording-detection signal** — if the OS reports a screen recording is active during a remote session, show that to the viewer (privacy signal, not enforcement). --- ## Reporting a vulnerability Email **security@niliwebpro.com** with subject `nilidesk: `. We respond within 72 hours, publish CVEs via MITRE, and credit reporters in release notes unless asked to stay anonymous. Coordinated-disclosure window: 90 days from initial report. If a fix isn't shipped by then, the reporter is free to publish. We do not threaten security researchers.