FormslySicherheitArchitektur
Whitepaper · Sicherheitsarchitektur

Wie wir Schweizer Daten technisch schützen.

Diese Seite ist die öffentliche Zusammenfassung unseres internen Sicherheitsdokuments. Sie beschreibt jede Schicht im Detail — vom Browser des Ausfüllenden bis zum verschlüsselten Backup in Zürich. Keine Marketingformeln, kein Wischiwaschi: echte Architektur-Entscheidungen, dokumentierte Restrisiken, prüffähig.

Datenstandort Zürich DSG / nDSG-konform AES-256-GCM + ECIES P-256
Grundprinzipien

Vier Entscheidungen, die alles andere bestimmen.

Eine Sicherheitsarchitektur ist die Summe ihrer Voreinstellungen. Unsere vier Default-Entscheidungen — und wie wir sie technisch erzwingen.

01Default

Daten verlassen die Schweiz nicht

Alle VMs, Backups und das Object-Storage liegen in Exoscale Zürich (eu-central-2). Keine Replikation, kein CDN-Spiegel ausserhalb der CH.

02Default

Server kennt den Inhalt nicht (wenn E2EE aktiv)

Im Pro-Plan werden Antworten bereits im Browser des Ausfüllenden mit ECIES + AES-256-GCM verschlüsselt. Private Schlüssel verlassen den Browser nie.

03Default

Mandantentrennung auf DB-Ebene

Row-Level Security in PostgreSQL ist auf jeder Tabelle aktiv — auch wir können nicht versehentlich Mandanten mischen, selbst via direktem SQL nicht.

04Default

Restrisiken werden dokumentiert

Was nicht eliminiert werden kann, wird benannt — in known-security-risks.md, mit Bewertung und Kompensationsmassnahme.

02 · Datenfluss

Vom Klick auf „Absenden" bis ins Dashboard.

Der Pfad jeder Antwort, mit Verschlüsselungs-Schritt, Zwischenhalt und Speicherort. Bei E2EE-Formularen passiert der Klartext-Schritt ausschliesslich im Browser des Ausfüllenden und im Browser des Admins — nie auf unserem Server.

verschlüsselt Klartext (lokal) in der Schweiz
Pfad einer Einreichung · E2EE-Modus
01lokal
Browser · Ausfüllende:r

Antworten werden lokal mit ECIES (P-256) + AES-256-GCM verschlüsselt. Private Key verbleibt nie auf dem Gerät.

KlartextWeb Crypto API
TLS 1.3
HSTS · 12 Monate
02
Edge · formsly.ch

Nginx mit Nonce-CSP und ALTCHA-Bot-Check. Keine eigene Logik auf Antwortinhalten.

ALTCHACSP nonce
HTTP intern
Loopback
03Server
App · Zürich (CH)

Speichert ausschliesslich Ciphertext und gewrappte Schlüssel. Bei Nicht-E2EE: zusätzlich ALE mit ENCRYPTION_KEY auf Server-Ebene.

AES-256-GCMALE
04
DB · Supabase RLS

PostgreSQL mit Row-Level Security auf jeder Tabelle. Mandantenübergreifende Reads sind technisch ausgeschlossen.

RLSmandantengetrennt
Snapshot
täglich, 30 Tage
05
Backup · Object Storage

Tägliche Snapshots, AES-256 verschlüsselt, Aufbewahrung 30 Tage, dann automatische und unwiderrufliche Löschung.

AES-25630 Tage
Passkey · 2FA
Login Admin
06lokal
Dashboard des Admin

Entschlüsselung lokal im Admin-Browser. Auto-Lock nach 30 Minuten ohne Aktivität.

E2E openAuto-Lock 30m
0
US-Subunternehmer im Datenpfad
200'000
PBKDF2-Iterationen für Master-Pwd
12 B
zufällige IV pro AES-GCM-Block
< 10 min
Bekanntwerden bei Ausfall (Uptime Robot)
03 · Schutz-Schichten

Fünf Schichten. Jede für sich allein wirksam.

Defense in Depth heisst nicht „eine Wand pro Risiko". Es heisst: selbst wenn eine Schicht versagt, schützen die übrigen vier. Wir konstruieren die App so, als wären die anderen Schichten bereits kompromittiert.

Weiter zur Verschlüsselung
01

Netzwerk

TLS 1.2/1.3 erzwungen, HSTS preload, Nginx-Reverse-Proxy, keine HTTP-Fallbacks.

02

Anwendung

Nonce-basierte CSP mit strict-dynamic, script-src-attr 'none', Zod-Validierung auf jedem API-Endpoint, HMAC-signierte Webhooks.

03

Verschlüsselung

Application-Level Encryption (AES-256-GCM) für jede Submission. E2EE auf Wunsch — Server kennt den Inhalt nie.

04

Datenbank

Row-Level Security auf jeder Tabelle. Service-Role-Key nur serverseitig, nie im Browser. Direkter SQL-Zugriff nur via SSH-Tunnel.

05

Infrastruktur

Exoscale Zürich, ISO 27001/27017/27018. Docker non-root (UID 1001). SSH nur mit Ed25519-Key + Passphrase, kein Passwort-Login.

04 · Verschlüsselung

Zwei Schichten. Eine immer aktiv. Die zweite optional auf Knopfdruck.

Application-Level Encryption (ALE) ist auf jedem Formular aktiv — keine Konfiguration, kein Plan-Upgrade nötig. Wer Antworten zusätzlich vor uns selbst verbergen will, aktiviert Ende-zu-Ende-Verschlüsselung (E2EE). Der entscheidende Unterschied: wer den Schlüssel besitzt.

Implementierung ALElib/encryption.ts
Implementierung E2EElib/e2e-encryption.ts
Format-Headerv1:base64(iv):base64(authTag):base64(ciphertext)
Eigenschaft
ALE · Standard
E2EE · ab Starter
Wer kann entschlüsseln?
Formsly-Server (mit ENCRYPTION_KEY)
Nur der eingeloggte Admin im eigenen Browser
Algorithmus Antwortinhalt
AES-256-GCM
ECDH P-256 + ECIES + AES-256-GCM
Schlüsselableitung
Statischer ENCRYPTION_KEY (Server-Umgebung)
PBKDF2-SHA-256, 200'000 Iterationen aus Master-Passwort
IV / Nonce
12 Byte zufällig pro Operation
12 Byte zufällig pro Operation
Wo entsteht der Schlüssel?
Serverseitig (Env-Variable .env.local)
Browser des Nutzers, Web Crypto API
Was passiert bei DB-Dump?
Ohne ENCRYPTION_KEY ist Ciphertext unleserlich
Ohne Master-Passwort des Nutzers ist Ciphertext unleserlich
2FA-Voraussetzung
Empfohlen
Technisch erzwungen — ohne TOTP keine E2EE-Aktivierung
Auto-Lock
n/a
30 Minuten Inaktivität → Private Key wird aus dem Browser-Speicher gelöscht
Verfügbar in Plan
Standard (alle Pläne)
Alle Pläne — bereits ab Starter aktivierbar
Vollständige technische Spezifikation: docs/compliance/e2e-encryption-spec.md
05 · Zugriff und Mandanten

Nichts ist „nur Application-Logik".

Jede Autorisierungsentscheidung ist doppelt verankert — einmal in der API-Route, einmal in der Datenbank selbst. Ein Bug in einer Schicht kompromittiert nicht die andere.

Endnutzer

Authentifizierung der Formsly-Nutzer

  • E-Mail + Passwort über Supabase Auth, bcrypt mit Salt pro User
  • TOTP-2FA optional — für E2EE technisch erzwungen
  • Keine geteilten Accounts; jeder Zugriff ist einem User zuordenbar
  • Edit-Token für Einreichungen: SHA-256-Hash in DB, Klartext nur im E-Mail-Link
Datenbank

Row-Level Security (RLS) auf jeder Tabelle

  • profiles, forms, submissions, webhooks, form_key_shares, consent_audit, e2e_access_log — alle mit RLS-Policies
  • Authentifizierter User sieht ausschliesslich eigene Zeilen — auch via direktem SQL
  • Mandantenübergreifende Reads sind auf DB-Ebene technisch ausgeschlossen
  • Service-Role-Key umgeht RLS — wird ausschliesslich serverseitig in API-Routes verwendet, nie im Browser
Betreiber

Authentifizierung der Infrastruktur

  • SSH ausschliesslich mit Ed25519-Key + Passphrase, kein Passwort-Login
  • Key auf verschlüsseltem USB-Stick — nicht in Cloud-Diensten
  • GitHub, Google, Infomaniak, Stripe: 2FA mit YubiKey (Primary + Backup)
  • Supabase-Dashboard nicht öffentlich exponiert — nur via SSH-Tunnel erreichbar
06 · Standort

Ein Datacenter. Eine Region. Eine Rechtsordnung.

Wir betreiben Formsly auf dedizierten virtuellen Maschinen bei Exoscale im Rechenzentrum Zürich. Region eu-central-2. ISO 27001, 27017 und 27018 zertifiziert. Es gibt keine sekundäre Region, kein CDN-Edge ausserhalb der Schweiz, keine asynchrone Replikation in andere Rechtsordnungen — bewusste Architektur-Entscheidung.

Region
eu-central-2
Datacenter
Exoscale Zürich, CH
Zertifizierungen
ISO 27001 / 27017 / 27018
CLOUD-Act-Exposure
Nein · keine US-Subunternehmer im Datenpfad
Datenresidenz · liveoperativ
Zürich
47.37° N · 8.55° E
0
andere Regionen
3
ISO-Zertifikate
100%
CH-Datenresidenz
07 · Subunternehmer

Vier Subunternehmer. Drei in der Schweiz.

Stripe ist die einzige Abhängigkeit ausserhalb der Schweiz — und sieht ausschliesslich Abrechnungsdaten, nie Formular-Antworten. Neue Subunternehmer durchlaufen Datenschutz- und Sicherheitsprüfung und werden in anbieter-und-transfers.md dokumentiert, bevor sie produktiv gehen.

Exoscale
Schweiz · Zürich
Hosting, VMs, Block Storage, Backups
Physischer Serverstandort. Kein App-Zugriff.
exoscale-dpa-v3.pdf
Supabase
Self-Hosted · Zürich
Auth, PostgreSQL, Storage Backend
Selbst gehostet auf eigenen VMs. Kein Drittanbieter.
Entfällt
Infomaniak
Schweiz · Genf
Transaktions-E-Mails (Bestätigungen, Edit-Links)
Nur Metadaten der Zustellung. Keine Antwortinhalte.
Infomaniak-Portal
Stripe
USA · global
Zahlungsabwicklung (Abonnement-Kunden)
Nur Abrechnungsdaten des Kunden. Keine Submissions.
Stripe DPA + SCCs
08 · Betrieb

Sicherheit ist ein Lebenszyklus, kein Launch-Event.

Patches, Backups, Monitoring und Incident Response sind im Tages- und Wochenrhythmus verankert. Jede Zahl unten ist im internen Dokument einer Person und einem Prozess zugeordnet.

Operativ
7Tage

Patch-Management

Sicherheits-Updates innerhalb von 7 Tagen nach Bekanntwerden via pnpm audit und Watchlist.

Operativ
BranchProtection

Deployment

Kein Direkt-Push auf main. GitHub Actions baut den Container nach jedem PR-Merge.

Operativ
< 10 minAlert

Monitoring

Uptime Robot prüft formsly.ch und admin.formsly.ch alle 5 Minuten. E-Mail-Alert bei Ausfall.

Operativ
30 dAufbewahrung

Backups

Tägliche Snapshots, AES-256 verschlüsselt, in Exoscale Object Storage Zürich. Danach automatische Löschung.

Operativ
RTO 24hRPO 24h

Wiederherstellung

Recovery-Tests jährlich oder nach Architekturwechseln. Letzter Test: 23. März 2026 — erfolgreich.

Operativ
72 hMeldefrist

Incident Response

Meldung an EDÖB nach Art. 24 nDSG, an Betroffene nach Art. 24 Abs. 2 — Plan in incident-response-plan.md.

09 · Bekannte Restrisiken

Was nicht eliminierbar ist, wird benannt.

Jedes ausgereifte System hat Restrisiken. Wir dokumentieren sie öffentlich, mit Bewertung und Kompensationsmassnahme — die vollständige Liste liegt in known-security-risks.md im Repository.

Zur Dokumenten-Übersicht
01Metadaten von Submissions liegen unverschlüsselt in der DB

Felder wie submitted_at, completion_time_seconds, variant_id, consent_given und utm_params werden im Klartext gespeichert. Bewusste Designentscheidung: Diese Felder enthalten für sich genommen keine Identifikatoren — ein Angreifer mit reinem DB-Zugriff sieht Zeitstempel und Zahlen, aber nicht wer was eingereicht hat. Verschlüsselung dieser Felder würde Dashboards, Löschfristen und Analysen verunmöglichen, ohne wesentlichen Sicherheitsgewinn.

02At-Rest-Verschlüsselung auf Infrastruktur-Ebene nicht garantiert

Exoscale verschlüsselt VM-Disks und Block Storage nicht automatisch. Kompensationsmassnahme: alle personenbezogenen Daten werden auf Applikationsebene mit AES-256-GCM verschlüsselt (ALE), bevor sie die Datenbank erreichen. Bei E2EE-Formularen zusätzlich vorher im Browser.

03Stripe als US-Subunternehmer im Bezahlflow

Stripe sieht ausschliesslich Abrechnungsdaten zahlender Kunden — niemals Formularantworten oder Daten von Endnutzern. Übertragung auf Basis von Standardvertragsklauseln (SCCs); DPA via Stripe. Dokumentiert in anbieter-und-transfers.md.

04Browser-Kompromittierung kompromittiert E2EE

E2EE schützt vor Server-Kompromittierung, nicht vor einem kompromittierten Endgerät. Wenn der Browser des Admins infiziert ist, kann der lokal entschlüsselte Klartext extrahiert werden. Kompensation: Auto-Lock nach 30 Minuten, 2FA technisch erzwungen, CSP mit nonce-basiertem strict-dynamic.

05Wer den ENCRYPTION_KEY besitzt, kann alle ALE-Antworten entschlüsseln

ALE schützt vor reinem DB-Zugriff (z. B. Backup-Diebstahl), nicht vor einem kompromittierten Server. Daher die Option auf E2EE für vertrauliche Daten — dort wird der Klartext auf dem Server nie sichtbar, selbst nicht für uns.

Sicherheits-Kontakt

Eine Frage, eine Meldung, ein Audit-Termin?

Responsible-Disclosure-Meldungen und Audit-Anfragen gehen direkt an die Person, die diese Seite verantwortet — nicht an einen Bot. Antwort üblicherweise innert 24 Stunden, an Werktagen schneller.

PGP-Fingerprint auf Anfrage