docs: Benutzerverwaltung, Passkeys und Sicherheitsmassnahmen dokumentieren
README: Abschnitt zur ersten Anmeldung mit den beiden Betriebsarten und der Einrichtung eines Passkeys, erweiterte Variablentabelle mit dem Hinweis, dass Name und URL zur Laufzeit gelesen werden, vollstaendige API-Uebersicht sowie ein Sicherheitsabschnitt, der auch die Grenzen benennt: kein HSTS, keine TLS-Terminierung, Ratenbegrenzung nur im Arbeitsspeicher, kein Passwort-Zuruecksetzen per E-Mail, und warum Anfragen ohne Origin- und Sec-Fetch-Site-Header durchgelassen werden. FEATURES: neues Kapitel zu Konten und Zugang, aktualisiertes Datenmodell und ausgebauter Sicherheitsabschnitt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+80
-4
@@ -5,6 +5,52 @@ Für Installation und Betrieb siehe [README.md](README.md).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 0. Konten und Zugang
|
||||||
|
|
||||||
|
### 0.1 Betriebsarten
|
||||||
|
|
||||||
|
Beim ersten Aufruf führt `/setup` durch die Einrichtung des Administratorkontos. Dort — und
|
||||||
|
später jederzeit unter *Admin-Einstellungen* — wird die Betriebsart gewählt:
|
||||||
|
|
||||||
|
| Modus | Verhalten |
|
||||||
|
| --- | --- |
|
||||||
|
| **Einzelbetrieb** | Genau ein Konto. Keine Registrierung, keine Benutzerverwaltung. |
|
||||||
|
| **Mehrbenutzerbetrieb** | Beliebig viele Konten mit strikt getrennten Daten. Selbstregistrierung optional zuschaltbar. |
|
||||||
|
|
||||||
|
Der Wechsel in den Einzelbetrieb wird abgelehnt, solange mehr als ein Konto existiert —
|
||||||
|
sonst würden Konten ausgesperrt. Nach der Einrichtung ist `/setup` dauerhaft gesperrt.
|
||||||
|
|
||||||
|
### 0.2 Anmeldung
|
||||||
|
|
||||||
|
- **Benutzername und Passwort.** Mindestens 10 Zeichen, offensichtliche Passwörter werden
|
||||||
|
abgelehnt. Hashing mit scrypt.
|
||||||
|
- **Passkey (WebAuthn).** Fingerabdruck, Gesichtserkennung oder Sicherheitsschlüssel. Die
|
||||||
|
Anmeldung läuft über auffindbare Credentials, ein Benutzername ist also nicht nötig.
|
||||||
|
Mehrere Passkeys pro Konto, benennbar und einzeln entfernbar.
|
||||||
|
- Ein Konto kann **ausschließlich** per Passkey nutzbar sein. Der letzte Passkey lässt sich
|
||||||
|
dann nicht entfernen, solange kein Passwort gesetzt ist.
|
||||||
|
- Fehlversuche werden gezählt; nach acht sperrt sich das Konto für 15 Minuten.
|
||||||
|
- Die Fehlermeldung ist immer dieselbe und die Antwortzeit gleich lang, egal ob es den
|
||||||
|
Benutzernamen gibt — so lässt sich nicht ausprobieren, welche Konten existieren.
|
||||||
|
|
||||||
|
### 0.3 Mein Konto
|
||||||
|
|
||||||
|
Anzeigename und E-Mail ändern · Passwort setzen oder wechseln · Passkeys verwalten ·
|
||||||
|
aktive Sitzungen einsehen (Browser, Betriebssystem, IP, letzte Aktivität) · überall
|
||||||
|
abmelden, wobei das eigene Gerät angemeldet bleibt.
|
||||||
|
|
||||||
|
### 0.4 Benutzerverwaltung (Administratoren)
|
||||||
|
|
||||||
|
- Konten anlegen, umbenennen, Rolle wechseln, deaktivieren, löschen
|
||||||
|
- Passwort eines Kontos neu setzen — beendet dessen Sitzungen sofort
|
||||||
|
- Übersicht je Konto: Projektzahl, Passkeys, letzte Anmeldung, Sperrstatus
|
||||||
|
- Selbstregistrierung an- und abschaltbar
|
||||||
|
- Schutzregeln: Der letzte aktive Administrator kann weder herabgestuft, deaktiviert noch
|
||||||
|
gelöscht werden; das eigene Konto lässt sich nicht löschen oder deaktivieren.
|
||||||
|
- **Administratoren sehen keine fremden Projekte.** Sie verwalten Konten, nicht Inhalte.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 1. Projekte
|
## 1. Projekte
|
||||||
|
|
||||||
### 1.1 Anlegen und Verwalten
|
### 1.1 Anlegen und Verwalten
|
||||||
@@ -157,21 +203,49 @@ Firmen-Gitea nebeneinander.
|
|||||||
|
|
||||||
| Tabelle | Inhalt |
|
| Tabelle | Inhalt |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `Project` | Projektstammdaten, Status, Fortschritt, Repo-Verknüpfung |
|
| `User` | Konten, Rolle, Passwort-Hash, Sperrzähler |
|
||||||
|
| `Session` | Serverseitige Sitzungen (nur der Token-Hash) |
|
||||||
|
| `Credential` | Passkeys inkl. öffentlichem Schlüssel und Signaturzähler |
|
||||||
|
| `WebAuthnChallenge` | Kurzlebige, einmalig verwendbare Challenges |
|
||||||
|
| `AppSettings` | Betriebsart und Registrierung (eine Zeile) |
|
||||||
|
| `Project` | Projektstammdaten, Status, Fortschritt, Repo-Verknüpfung, Besitzer |
|
||||||
| `Note` | Notizen (n:1 zu Projekt, Cascade-Delete) |
|
| `Note` | Notizen (n:1 zu Projekt, Cascade-Delete) |
|
||||||
| `GitConnection` | Git-Systeme inkl. verschlüsseltem Token |
|
| `GitConnection` | Git-Systeme inkl. verschlüsseltem Token, Besitzer |
|
||||||
| `RepoSnapshot` | Zwischengespeicherter Repo-Stand (1:1 zu Projekt) |
|
| `RepoSnapshot` | Zwischengespeicherter Repo-Stand (1:1 zu Projekt) |
|
||||||
|
|
||||||
|
`Project.ownerId` und `GitConnection.ownerId` sind bewusst optional: so überleben Daten aus
|
||||||
|
der Zeit vor der Benutzerverwaltung die Migration und werden beim Anlegen des ersten Kontos
|
||||||
|
übernommen. Slug und Verbindungsname sind je Besitzer eindeutig — zwei Personen dürfen
|
||||||
|
beide ein Projekt „dashboard" haben.
|
||||||
|
|
||||||
Listen wie Branches oder Commits liegen als `JSONB`, damit providerspezifische Felder
|
Listen wie Branches oder Commits liegen als `JSONB`, damit providerspezifische Felder
|
||||||
nicht verloren gehen. Indizes auf Status, Verbindung und Änderungsdatum.
|
nicht verloren gehen. Indizes auf Status, Verbindung und Änderungsdatum.
|
||||||
|
|
||||||
### 6.2 Sicherheit
|
### 6.2 Sicherheit
|
||||||
|
|
||||||
|
- **Zugangsschutz in zwei Ebenen.** Die Middleware prüft nur, ob ein Sitzungscookie
|
||||||
|
überhaupt mitkommt — im Edge-Runtime gibt es keine Datenbank. Verbindlich entschieden
|
||||||
|
wird serverseitig in jeder Seite und jedem Endpunkt (`requireUserPage` /
|
||||||
|
`requireUserApi`). Das ist die eigentliche Sicherheitsgrenze.
|
||||||
|
- **Datentrennung** über eine Besitzerbedingung in jeder Abfrage. Schreibzugriffe laufen
|
||||||
|
über `updateMany`/`deleteMany` mit `ownerId` in der `where`-Klausel, treffen fremde
|
||||||
|
Datensätze also gar nicht erst. Fremde IDs liefern `404` statt `403`.
|
||||||
|
- **CSRF**: Origin- bzw. `Sec-Fetch-Site`-Prüfung für alle schreibenden Methoden, dazu
|
||||||
|
erzwungener `Content-Type: application/json`.
|
||||||
|
- **CSP mit Per-Request-Nonce** und `strict-dynamic`, plus `frame-ancestors 'none'`,
|
||||||
|
`object-src 'none'`, `base-uri 'none'` und die üblichen Schutz-Header.
|
||||||
|
- **SSRF-Schutz** vor jedem ausgehenden Git-Aufruf: Hostname wird aufgelöst, jede
|
||||||
|
Antwortadresse geprüft (schützt auch gegen DNS-Rebinding über einen zweiten A-Record),
|
||||||
|
Redirect-Ziele werden erneut geprüft. Konfigurierbar über `GIT_ALLOW_LOOPBACK`,
|
||||||
|
`GIT_BLOCK_PRIVATE` und `GIT_ALLOWED_HOSTS`.
|
||||||
|
- **Rate-Limiting** für Anmeldung (je IP und je Konto), ausgehende Git-Aufrufe und
|
||||||
|
WebAuthn-Optionen.
|
||||||
- Access Tokens verschlüsselt mit **AES-256-GCM**, Schlüssel per **scrypt** aus
|
- Access Tokens verschlüsselt mit **AES-256-GCM**, Schlüssel per **scrypt** aus
|
||||||
`APP_SECRET` abgeleitet, Chiffrat versioniert (`v1.<iv>.<tag>.<data>`).
|
`APP_SECRET` abgeleitet, Chiffrat versioniert (`v1.<iv>.<tag>.<data>`).
|
||||||
- Tokens werden nie an den Browser gesendet — alle API-Antworten nutzen eine Feldauswahl
|
- Tokens werden nie an den Browser gesendet — alle API-Antworten nutzen eine Feldauswahl
|
||||||
ohne `tokenCipher`.
|
ohne `tokenCipher`.
|
||||||
- **Zod-Validierung** jeder Eingabe an der API-Grenze mit Feldfehlern für die UI.
|
- **Zod-Validierung** jeder Eingabe an der API-Grenze mit Feldfehlern für die UI, Body auf
|
||||||
|
256 KB begrenzt.
|
||||||
- Prisma arbeitet ausschließlich mit parametrisierten Abfragen.
|
- Prisma arbeitet ausschließlich mit parametrisierten Abfragen.
|
||||||
|
|
||||||
### 6.3 Betrieb
|
### 6.3 Betrieb
|
||||||
@@ -204,4 +278,6 @@ Bewusst nicht umgesetzt, aber vorgemerkt:
|
|||||||
- Volltextsuche über Notizen hinweg
|
- Volltextsuche über Notizen hinweg
|
||||||
- Export eines Projekts als Markdown
|
- Export eines Projekts als Markdown
|
||||||
- Automatischer Sync im Hintergrund (Intervall pro Verbindung)
|
- Automatischer Sync im Hintergrund (Intervall pro Verbindung)
|
||||||
- Mehrbenutzerbetrieb mit Login
|
- Passwort-Zurücksetzen per E-Mail (aktuell setzt ein Administrator das Passwort neu)
|
||||||
|
- Zwei-Faktor per TOTP als Alternative zum Passkey
|
||||||
|
- Geteilte Projekte zwischen Konten (heute ist die Trennung absichtlich hart)
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ Jedes Projekt bekommt einen **Status** (von der rohen Idee bis „Fertig"), beli
|
|||||||
Branches, Tags, Commits, Releases und offene Issues/PRs — egal ob **GitHub**, **GitLab**
|
Branches, Tags, Commits, Releases und offene Issues/PRs — egal ob **GitHub**, **GitLab**
|
||||||
oder eine selbst gehostete **Gitea**-Instanz.
|
oder eine selbst gehostete **Gitea**-Instanz.
|
||||||
|
|
||||||
|
Die Anmeldung läuft über **Benutzername und Passwort oder Passkey** (WebAuthn). Nebula
|
||||||
|
kennt zwei Betriebsarten: **Einzelbetrieb** für ein Konto oder **Mehrbenutzerbetrieb**, in
|
||||||
|
dem jede Person ihren eigenen, streng getrennten Bereich hat. Umschalten geht jederzeit in
|
||||||
|
den Admin-Einstellungen.
|
||||||
|
|
||||||
Alle Daten liegen in **deiner lokalen PostgreSQL**. Es gibt keinen Cloud-Dienst dahinter,
|
Alle Daten liegen in **deiner lokalen PostgreSQL**. Es gibt keinen Cloud-Dienst dahinter,
|
||||||
Access Tokens werden verschlüsselt abgelegt.
|
Access Tokens werden verschlüsselt abgelegt.
|
||||||
|
|
||||||
@@ -106,21 +111,55 @@ Alle Variablen sind in [`.env.example`](.env.example) dokumentiert.
|
|||||||
|
|
||||||
| Variable | Bedeutung | Default |
|
| Variable | Bedeutung | Default |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| `APP_NAME` | Anzeigename: Seitentitel, Kopfzeile, Passkey-Dialog | `Nebula` |
|
||||||
|
| `APP_TAGLINE` | Kleine Zeile unter dem Namen | `Vibe Coding` |
|
||||||
|
| `APP_URL` | Öffentliche Adresse — bestimmt die WebAuthn-RP-ID | `http://localhost:3000` |
|
||||||
| `DATABASE_URL` | PostgreSQL-Verbindung (Prisma) | `postgresql://nebula:nebula@localhost:5432/nebula` |
|
| `DATABASE_URL` | PostgreSQL-Verbindung (Prisma) | `postgresql://nebula:nebula@localhost:5432/nebula` |
|
||||||
| `APP_SECRET` | Schlüssel für die Token-Verschlüsselung (AES-256-GCM) | — **unbedingt setzen** |
|
| `APP_SECRET` | Schlüssel für die Token-Verschlüsselung (AES-256-GCM) | — **unbedingt setzen** |
|
||||||
|
| `SESSION_TTL_DAYS` | Lebensdauer einer Anmeldung in Tagen | `30` |
|
||||||
| `PORT` | Port der Anwendung | `3000` |
|
| `PORT` | Port der Anwendung | `3000` |
|
||||||
| `APP_PORT` | Host-Port-Mapping des App-Containers | `3000` |
|
| `APP_PORT` | Host-Port-Mapping des App-Containers | `3000` |
|
||||||
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | Zugangsdaten des DB-Containers | `nebula` |
|
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | Zugangsdaten des DB-Containers | `nebula` |
|
||||||
| `POSTGRES_PORT` | Host-Port der Datenbank | `5432` |
|
| `POSTGRES_PORT` | Host-Port der Datenbank | `5432` |
|
||||||
| `ADMINER_PORT` | Host-Port von Adminer (`--profile tools`) | `8080` |
|
| `ADMINER_PORT` | Host-Port von Adminer (`--profile tools`) | `8080` |
|
||||||
| `GIT_API_TIMEOUT_MS` | Timeout für Git-API-Aufrufe | `15000` |
|
| `GIT_API_TIMEOUT_MS` | Timeout für Git-API-Aufrufe | `15000` |
|
||||||
|
| `GIT_ALLOW_LOOPBACK` | Git-Instanz auf demselben Rechner erlauben | `false` |
|
||||||
|
| `GIT_BLOCK_PRIVATE` | Zugriff ins private LAN sperren | `false` |
|
||||||
|
| `GIT_ALLOWED_HOSTS` | Strengster Modus: nur diese Hosts erlauben | leer |
|
||||||
|
|
||||||
|
`APP_NAME`, `APP_TAGLINE` und `APP_URL` werden **zur Laufzeit** gelesen. Dasselbe
|
||||||
|
Docker-Image lässt sich also ohne Neubau umbenennen oder unter einer anderen Adresse
|
||||||
|
betreiben — Container neu starten genügt.
|
||||||
|
|
||||||
> **`APP_SECRET` nachträglich ändern** macht bereits gespeicherte Git-Tokens unlesbar.
|
> **`APP_SECRET` nachträglich ändern** macht bereits gespeicherte Git-Tokens unlesbar.
|
||||||
> Sie müssen dann in den Verbindungen neu hinterlegt werden — die restlichen Daten bleiben
|
> Sie müssen dann in den Verbindungen neu hinterlegt werden — Konten, Passwörter und
|
||||||
> unberührt.
|
> Passkeys bleiben unberührt.
|
||||||
|
|
||||||
|
> **`APP_URL` nachträglich ändern** (anderer Hostname) macht bestehende **Passkeys**
|
||||||
|
> ungültig, weil sie an die RP-ID gebunden sind. Passwörter funktionieren weiter.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Erste Anmeldung
|
||||||
|
|
||||||
|
Beim ersten Aufruf landest du auf `/setup`. Dort legst du das Administratorkonto an und
|
||||||
|
wählst die Betriebsart:
|
||||||
|
|
||||||
|
- **Einzelbetrieb** — genau ein Konto, keine Registrierung, keine Benutzerliste.
|
||||||
|
- **Mehrbenutzerbetrieb** — beliebig viele Konten. Projekte, Notizen und Git-Verbindungen
|
||||||
|
sind pro Konto strikt getrennt; auch ein Administrator sieht fremde Projekte nicht.
|
||||||
|
|
||||||
|
Nach der Einrichtung ist `/setup` gesperrt. Bereits vorhandene Projekte ohne Besitzer
|
||||||
|
(etwa aus den Seed-Daten) werden dem neuen Konto zugeordnet.
|
||||||
|
|
||||||
|
**Passkey einrichten:** unter *Mein Konto → Passkeys → Passkey hinzufügen*. Danach genügt
|
||||||
|
auf der Anmeldeseite ein Klick auf „Mit Passkey anmelden" — ohne Benutzernamen, weil
|
||||||
|
Nebula auffindbare Credentials nutzt.
|
||||||
|
|
||||||
|
> Passkeys brauchen einen sicheren Kontext. Sie funktionieren über `https://` und über
|
||||||
|
> `http://localhost`, **nicht** über eine LAN-IP ohne TLS. `APP_URL` muss dabei exakt der
|
||||||
|
> Adresse entsprechen, unter der du die App aufrufst.
|
||||||
|
|
||||||
## Git-Verbindung einrichten
|
## Git-Verbindung einrichten
|
||||||
|
|
||||||
1. Im Git-System einen **Personal Access Token** mit Leserechten erzeugen:
|
1. Im Git-System einen **Personal Access Token** mit Leserechten erzeugen:
|
||||||
@@ -197,9 +236,23 @@ nebula/
|
|||||||
|
|
||||||
Alle Endpunkte liefern und erwarten JSON.
|
Alle Endpunkte liefern und erwarten JSON.
|
||||||
|
|
||||||
|
Bis auf `/api/health` und `/api/auth/*` erfordern alle Endpunkte eine Anmeldung und liefern
|
||||||
|
ausschließlich Daten des angemeldeten Kontos.
|
||||||
|
|
||||||
| Methode | Pfad | Zweck |
|
| Methode | Pfad | Zweck |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `GET` | `/api/health` | Status inkl. Datenbankverbindung (Docker-Healthcheck) |
|
| `GET` | `/api/health` | Status inkl. Datenbankverbindung (Docker-Healthcheck) |
|
||||||
|
| `POST` | `/api/auth/setup` | Administratorkonto anlegen (nur beim ersten Mal) |
|
||||||
|
| `POST` | `/api/auth/login` `/api/auth/logout` `/api/auth/register` | Anmeldung |
|
||||||
|
| `GET` | `/api/auth/session` | Aktuelle Sitzung und Instanzzustand |
|
||||||
|
| `POST` | `/api/auth/passkey/{login,register}/{options,verify}` | WebAuthn |
|
||||||
|
| `PATCH` | `/api/account/profile` | Eigenes Profil |
|
||||||
|
| `POST` | `/api/account/password` | Eigenes Passwort setzen |
|
||||||
|
| `GET` `DELETE` | `/api/account/sessions` | Eigene Sitzungen / überall abmelden |
|
||||||
|
| `PATCH` `DELETE` | `/api/account/passkeys/:id` | Passkey umbenennen / entfernen |
|
||||||
|
| `GET` `PATCH` | `/api/admin/settings` | Betriebsart und Registrierung (Admin) |
|
||||||
|
| `GET` `POST` | `/api/admin/users` | Konten auflisten / anlegen (Admin) |
|
||||||
|
| `PATCH` `DELETE` | `/api/admin/users/:id` | Konto ändern / löschen (Admin) |
|
||||||
| `GET` `POST` | `/api/projects` | Projekte auflisten / anlegen |
|
| `GET` `POST` | `/api/projects` | Projekte auflisten / anlegen |
|
||||||
| `GET` `PATCH` `DELETE` | `/api/projects/:id` | Projekt lesen / ändern / löschen |
|
| `GET` `PATCH` `DELETE` | `/api/projects/:id` | Projekt lesen / ändern / löschen |
|
||||||
| `POST` | `/api/projects/:id/sync` | Repo-Daten vom Git-System holen |
|
| `POST` | `/api/projects/:id/sync` | Repo-Daten vom Git-System holen |
|
||||||
@@ -220,16 +273,36 @@ curl -X POST http://localhost:3000/api/projects \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sicherheitshinweise
|
## Sicherheit
|
||||||
|
|
||||||
|
| Bereich | Umsetzung |
|
||||||
|
| --- | --- |
|
||||||
|
| **Anmeldung** | Passwort-Hashing mit **scrypt** (N=32768, r=8, p=1), serverseitige Sitzungen — in der Datenbank liegt nur der SHA-256-Hash des Cookie-Tokens |
|
||||||
|
| **Passkeys** | WebAuthn über SimpleWebAuthn, Challenges einmalig verwendbar und in der DB abgelegt, Signaturzähler wird fortgeschrieben |
|
||||||
|
| **Cookies** | `HttpOnly`, `SameSite=Lax`, `Secure` automatisch bei https |
|
||||||
|
| **CSRF** | Jede schreibende Anfrage muss vom eigenen Origin kommen (`Sec-Fetch-Site` / `Origin`); zusätzlich wird `Content-Type: application/json` erzwungen, was den Formular-Trick ohne Preflight ausschließt |
|
||||||
|
| **CSP** | Strikte Policy mit Per-Request-**Nonce** und `strict-dynamic`, dazu `frame-ancestors 'none'`, `object-src 'none'`, `base-uri 'none'` |
|
||||||
|
| **Weitere Header** | `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, `Cross-Origin-Opener-Policy`, `Cross-Origin-Resource-Policy` |
|
||||||
|
| **SSRF** | Die Basis-URL einer Git-Verbindung ist Benutzereingabe. Vor jedem ausgehenden Aufruf werden Hostname und **alle** aufgelösten IPs geprüft (auch nach Redirects); Loopback, Link-Local samt Cloud-Metadaten, Multicast und CGNAT sind gesperrt |
|
||||||
|
| **Datentrennung** | Jede Abfrage filtert auf den Besitzer. Fremde IDs liefern `404`, nicht `403` — die Antwort verrät also nicht einmal, dass es den Datensatz gibt |
|
||||||
|
| **Rate-Limiting** | Anmeldung pro IP und Konto, ausgehende Git-Aufrufe, WebAuthn-Optionen; Konto sperrt sich nach 8 Fehlversuchen für 15 Minuten |
|
||||||
|
| **Eingaben** | Zod-Schemas an jeder API-Grenze, Body auf 256 KB begrenzt |
|
||||||
|
| **Git-Tokens** | AES-256-GCM, Schlüssel per scrypt aus `APP_SECRET`. Werden nie an den Browser ausgeliefert — die API zeigt nur `ghp_…9f3a` |
|
||||||
|
| **Sitzungsende** | Deaktivieren, Löschen oder ein neues Passwort beenden sofort alle Sitzungen des Kontos |
|
||||||
|
|
||||||
|
Grenzen, die du kennen solltest:
|
||||||
|
|
||||||
- Nebula hat **keine Benutzerverwaltung** und ist für den Betrieb im eigenen Netz gedacht.
|
|
||||||
Vor einer Veröffentlichung im Internet unbedingt einen Reverse Proxy mit Authentifizierung
|
|
||||||
davorschalten.
|
|
||||||
- Access Tokens werden mit **AES-256-GCM** verschlüsselt gespeichert, aus `APP_SECRET` per
|
|
||||||
scrypt abgeleitet. Sie werden ausschließlich serverseitig entschlüsselt und nie an den
|
|
||||||
Browser ausgeliefert — die API zeigt nur einen Hinweis wie `ghp_…9f3a`.
|
|
||||||
- Ist `APP_SECRET` nicht gesetzt, warnt die App beim Start und nutzt einen unsicheren
|
- Ist `APP_SECRET` nicht gesetzt, warnt die App beim Start und nutzt einen unsicheren
|
||||||
Entwicklungsschlüssel.
|
Entwicklungsschlüssel.
|
||||||
|
- Ohne `Origin`- **und** `Sec-Fetch-Site`-Header wird eine schreibende Anfrage
|
||||||
|
durchgelassen. Das betrifft nur Nicht-Browser-Clients (curl, Skripte) und ist bewusst so,
|
||||||
|
damit die API skriptbar bleibt — ein CSRF-Vektor ist es nicht, weil jeder Browser
|
||||||
|
mindestens einen der beiden Header sendet.
|
||||||
|
- Das Rate-Limiting liegt im Arbeitsspeicher und wird beim Neustart zurückgesetzt.
|
||||||
|
- Es gibt **kein** HSTS und keine TLS-Terminierung. Für den Betrieb im Internet gehört ein
|
||||||
|
Reverse Proxy mit gültigem Zertifikat davor — der ist ohnehin nötig, damit Passkeys
|
||||||
|
funktionieren.
|
||||||
|
- Passwort-Zurücksetzen per E-Mail gibt es nicht; ein Administrator setzt Passwörter neu.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -245,6 +318,10 @@ curl -X POST http://localhost:3000/api/projects \
|
|||||||
| Tokens plötzlich ungültig | `APP_SECRET` wurde geändert — Tokens neu eintragen |
|
| Tokens plötzlich ungültig | `APP_SECRET` wurde geändert — Tokens neu eintragen |
|
||||||
| Port 3000 belegt | `APP_PORT` in der `.env` ändern (Docker) bzw. `npm run dev -- -p 3001` |
|
| Port 3000 belegt | `APP_PORT` in der `.env` ändern (Docker) bzw. `npm run dev -- -p 3001` |
|
||||||
| `package.json#prisma is deprecated` | Harmlose Warnung von Prisma 6.19. Die Umstellung auf `prisma.config.ts` steht aus, weil dann die `.env` nicht mehr automatisch geladen wird — vor Prisma 7 zu erledigen |
|
| `package.json#prisma is deprecated` | Harmlose Warnung von Prisma 6.19. Die Umstellung auf `prisma.config.ts` steht aus, weil dann die `.env` nicht mehr automatisch geladen wird — vor Prisma 7 zu erledigen |
|
||||||
|
| Passkey-Knopf fehlt / „kein sicherer Kontext" | `APP_URL` zeigt weder auf `https://` noch auf `http://localhost` |
|
||||||
|
| Passkey wird nicht akzeptiert | `APP_URL` weicht von der aufgerufenen Adresse ab, oder der Hostname wurde geändert (RP-ID) |
|
||||||
|
| Nach `docker compose down -v` ist alles weg | Das Volume enthielt Konten und Projekte — `-v` löscht es |
|
||||||
|
| Kein Zugriff auf die eigene Gitea im LAN | Läuft sie auf demselben Rechner? Dann `GIT_ALLOW_LOOPBACK=true` setzen |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user