OpenNIT-Vault-Extension/docs/ARCHITECTURE.md
Claude 65a07fbc18
Sechs Review-Punkte: Speicherort, Domain-Pruefung, Rechte, Generator, Notizen, Bearbeiten
1. Passwort mehrstufiger Logins nicht mehr auf der Platte: Der Auftrag lag
   als __pendingFill in chrome.storage.local, also im Klartext auf der
   Festplatte. Die 30-Sekunden-Pruefung verhinderte nur die Verwendung,
   nicht die Speicherung - wurde der zweite Schritt nie erreicht, blieb
   das Passwort liegen. Er liegt jetzt ausschliesslich im Speicher des
   Service Workers, je Tab, und wird beim Abholen verbraucht, nach 30 s
   verworfen, beim Sperren geleert und beim Schliessen des Tabs entfernt.
   Reste frueherer Versionen raeumt onInstalled ab.

2. Domain-Warnung: fillDomainMatches akzeptierte mit
   eh.endsWith('.' + pageHost) auch die Gegenrichtung - ein Eintrag fuer
   vpn.firma.de galt auf firma.de als passend und die Warnung blieb aus.
   Diese Klausel entfaellt.

3. scripting und activeTab werden nicht mehr angefordert; beide waren
   unbenutzt (das Content-Script laeuft ueber content_scripts, der
   Tab-Zugriff ueber host_permissions). PERMISSIONS.md begruendete
   scripting mit dem nativen Value-Setter, was nichts damit zu tun hat.

4. Passwort-Generator: buf lieferte dieselben Werte fuer Zeichenwahl und
   Mischreihenfolge, wodurch die Permutation mit dem Inhalt korrelierte.
   Beides zieht jetzt getrennt ueber randomBelow(), das den obersten,
   unvollstaendigen Block verwirft (gleichverteilt statt Rest-Modulo).
   Laenge (12-48) und Sonderzeichen sind waehlbar.

5. Notizen laufen ueber copySecret und werden damit ebenfalls aus der
   Zwischenablage entfernt; sie enthalten in der Praxis oft
   Wiederherstellungscodes. copyToClipboard entfaellt.

6. urlmatch.js buendelt die drei abweichenden matchUrl-Fassungen zu einer
   Regel, geladen in Service Worker, Popup und Seiten. escAttr escapt jetzt
   auch & < > und Apostroph, traegt also in jedem Attributkontext.
   Eintraege lassen sich im Popup bearbeiten und loeschen; beim Bearbeiten
   bedeutet ein leeres Passwortfeld unveraendert, sodass das Passwort das
   Popup nicht verlaesst.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G12DRMpe4UjYDwuRU1sjy1
2026-07-31 13:17:23 +00:00

86 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Architektur
## Überblick
Die Erweiterung ist ein **Manifest-V3-Client** ohne eigenen Server. Sie besteht aus vier Kontexten, die
über `chrome.runtime`-Nachrichten kommunizieren:
```
┌───────────────┐ Messages ┌──────────────────────┐ HTTPS/Bearer ┌──────────────────┐
│ popup.html │ ───────────▶ │ background.js │ ───────────────▶ │ OpenNIT-Server │
│ popup.js │ ◀─────────── │ (Service Worker) │ ◀─────────────── │ /api/vault/... │
└───────────────┘ │ - Cache (5 Min) │ └──────────────────┘
┌───────────────┐ Messages │ - Lock-Gate │
│ content.js │ ───────────▶ │ - Favicon-Cache │
│ (jede Seite) │ ◀─────────── │ - Clipboard-Timer │
└───────────────┘ └──────────┬───────────┘
│ CLIP_WRITE
┌────────▼─────────┐
│ offscreen.html │ (Zwischenablage schreiben/leeren)
└──────────────────┘
```
## Komponenten
| Datei | Rolle |
|-------|-------|
| `background.js` | Zentrale Logik: API-Aufrufe, 5-Minuten-Cache, **Lock-Gate**, Favicon-Cache (Data-URLs), Zwischenablage-Timer. Alle Secrets fließen hier durch. |
| `content.js` | Wird auf jeder Seite ausgeführt. Erkennt Benutzer-/Passwort-/OTP-Felder (inkl. Shadow-DOM, mehrstufige Logins, segmentierte OTP-Felder), zeigt das Vorschlags-Dropdown und füllt Felder framework-kompatibel (React/Vue/Angular). |
| `popup.html` / `popup.js` | Toolbar-Popup: Liste, Suche, Detailansicht, Anlegen + Generator, PIN-Schirm. |
| `options.html` / `options.js` | Einstellungen: Server-URL, SSO-Anmeldung, PIN-Sperrdauer, Zwischenablage. |
| `offscreen.html` / `offscreen.js` | Minimaldokument, das ausschließlich die Zwischenablage beschreibt bzw. leert (MV3-konform). |
| `urlmatch.js` | Gemeinsame Zuordnung Eintrag ↔ Seite (`VaultUrl`), geladen in allen drei Kontexten damit Vorschlagsliste und Sicherheitswarnung dieselbe Regel anwenden. |
## Nachrichten (Auszug)
| Typ | Von → Nach | Zweck |
|-----|-----------|-------|
| `CHECK_STATUS` | popup/content → bg | Token prüfen, App-Name/User, `pin_enabled` |
| `GET_LOCK` / `DO_UNLOCK` / `LOCK_NOW` | popup → bg | PIN-Sperre abfragen/entsperren/sperren |
| `GET_ENTRIES` / `GET_MATCHING_ENTRIES` | popup/content → bg | Einträge (alle / passend zur URL) |
| `GET_PASSWORD` / `GET_TOTP` | popup/content → bg | Secret **on demand** |
| `CREATE_ENTRY` / `UPDATE_ENTRY` / `DELETE_ENTRY` | popup → bg | Eintrag anlegen / ändern / löschen |
| `SET_PENDING_FILL` / `TAKE_PENDING_FILL` | content → bg | Passwort für den zweiten Login-Schritt hinterlegen bzw. abholen (nur im Speicher, je Tab) |
| `GET_FAVICON` | popup/content → bg | Favicon als Data-URL (serverseitig gecacht) |
| `VAULT_FILL` | popup → content | Aktives Tab-Formular ausfüllen |
| `SCHEDULE_CLIP_CLEAR` | popup/content → bg | Zwischenablage-Leerung planen |
| `CLIP_WRITE` | content → bg | In die Zwischenablage schreiben, wenn die Seite selbst keinen Zugriff bekommt |
## Server-API (in OpenNIT)
Alle Endpunkte unter `/api/vault/extension/` mit `Authorization: Bearer <token>`:
- `GET /entries` Liste (Titel, Benutzer, URL, Notizen, `has_totp`, `favicon_domain`, `has_favicon`)
- `GET /entries/{id}/password` Passwort (protokolliert im Audit-Log)
- `GET /entries/{id}/totp` aktueller TOTP-Code + Restsekunden
- `GET /entries/{id}/favicon?fetch=1` gecachtes Favicon (bei Bedarf serverseitig geholt)
- `POST /entries` neuen Eintrag anlegen
- `POST /entries/{id}` Eintrag ändern (leeres Passwortfeld = unverändert)
- `POST /entries/{id}/delete` Eintrag löschen
- `GET /status` Token gültig? + `pin_enabled` / `pin_lock_secs`
- `POST /unlock` Tresor-PIN verifizieren + serverseitiges Entsperr-Fenster für den Token setzen
- `POST /lock` Token sofort wieder sperren (Entsperr-Fenster zurücksetzen)
- `GET /oauth/authorize` · `POST /oauth/authorize` SSO-Anmeldung/Zustimmung (Session, PKCE)
- `POST /oauth/token` Authorization-Code- bzw. Refresh-Grant (öffentlich, PKCE) → Access/Refresh
- `POST /oauth/revoke` Refresh-Kette widerrufen (Logout)
Details zum SSO-Flow: [`SSO-PLAN.md`](SSO-PLAN.md). Die Erweiterung nutzt SSO über `chrome.identity`;
Access-Tokens werden im Hintergrund still per Refresh (mit Rotation) erneuert.
## Lock-Gate (serverseitig erzwungen)
Ist für den Nutzer ein **Tresor-PIN** aktiv, liefern die Server-Endpunkte für Einträge/Passwort/TOTP
erst nach frischer PIN-Entsperrung Daten (`unlocked_until` pro Token) und antworten sonst mit **HTTP 423**.
Der Client spiegelt den Zustand nur (PIN-Schirm) die eigentliche Durchsetzung liegt im Server, damit ein
**gestohlener Token allein wertlos** ist. Der Client hält seinen Entsperr-Status zusätzlich in
`chrome.storage.session` (verfällt beim Schließen des Browsers). Die Einstellung „PIN-Sperre" legt nur die
Fensterdauer fest; „Bis der Browser geschlossen wird" nutzt ein langes Serverfenster + Client-Sitzungsende.
## Sicherheitsprinzipien
- **Kein Remote-Code** alle Skripte im Paket (MV3-CSP-konform, keine Inline-Skripte).
- **Secrets on demand** Passwörter/TOTP erst bei Nutzung, nie in der Liste.
- **Kein persistentes Secret** nur URL, Sitzungstoken, Einstellungen in `chrome.storage`. Das Passwort
für einen mehrstufigen Login liegt ausschließlich im Speicher des Service Workers (je Tab, 30 s),
nie in `chrome.storage`, das auf die Festplatte geschrieben würde.
- **Server-seitige Krypto** Ver-/Entschlüsselung im OpenNIT-Server, nicht im Browser.