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
86 lines
6.2 KiB
Markdown
86 lines
6.2 KiB
Markdown
# 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.
|