Security architecture
A form provider that cannot read its own customers' data.
Schweizerform is a zero-knowledge system. Every submission is locked inside the respondent's browser before it leaves the page, and the keys that unlock it never exist outside the owner's session. This page explains how — and what we cannot do as a result.
Symmetric cipher
AES-256-GCM
Asymmetric cipher
RSA-OAEP 2048, SHA-256
Key derivation
PBKDF2-SHA-256 · 100 000
Hosting jurisdiction
Switzerland — Infomaniak
First principles
Three things we believe, four things we don't do.
Security isn't a feature we shipped on top of a form builder. It's the shape the form builder was built around.
Principle 01
The operator must not be able to read your data.
Not "will not." Cannot. A trusted operator is a single point of failure — and a magnet for court orders, insider threats, and stolen credentials. Our system removes us from the unlocking path entirely.
Principle 02
Sovereignty is physical, not contractual.
A "data residency" clause in a contract is paper — switching jurisdictions takes one amendment. We run the whole stack — application, database, file storage, and email — on Swiss infrastructure. No US or EU vendor ever sits in the data path.
Principle 03
The respondent's browser belongs to the respondent.
The public form page loads no third-party scripts. No Google Analytics, no Sentry, no Mixpanel, no Intercom, no advertising pixel. The only network the respondent touches is ours.
Key hierarchy
Four layers of keys. None of them on our servers.
Your Access Code, typed into your browser, produces a Master Key in memory. That Master Key opens each form's Form Key. The Form Key opens the form's private key. The private key opens every submission's one-time key. The chain ends in your head — never in our database.
Layer 01 · Owner secret
Access Code
Origin: A passphrase you choose (6 chars minimum)
Storage: Only in the owner's head — never sent anywhere
↓ PBKDF2-SHA-256 · 100 000 rounds · 32-byte salt
Layer 02 · Derived
Master Key
Algorithm: AES-256-GCM (used as a Key-Encryption Key)
Storage: In browser memory only · cleared on logout
↓ unwraps
Layer 03 · Per form
Form Key
Algorithm: AES-256-GCM · bound to form_key_creation
Storage: Stored in the database — but only the sealed version, never the open one
↓ unwraps
Layer 04 · Per form
Form RSA Private Key
Algorithm: RSA-OAEP 2048 · SHA-256 · bound to form_private_key
Storage: In the database, sealed under the Form Key
↓ unwraps
Layer 05 · Per submission
Submission Key
Algorithm: AES-256-GCM · bound to formId:submissionId
Storage: Sealed under the form's RSA public key
Submission lifecycle
Plaintext enters the respondent's browser. Plaintext exits the owner's browser. In between, only scrambled bytes.
Respondent's browser
Encrypt → send
- 1Loads the public form page. Receives only the form's public lock (an RSA public key) — never the key that opens it.
- 2If the form is password-protected, the questions stay on the server until the password is checked — the question list never even reaches the browser before then.
- 3Fills in the answers. Selects the files to attach.
- 4The browser generates a fresh AES-256 Submission Key using the built-in Web Crypto API — the same one Chrome, Firefox and Safari ship with.
- 5Encrypts the answer payload with AES-GCM, bound to this form's ID and this submission's ID so it can't be replayed elsewhere.
- 6Encrypts each file separately. Generates a random filename for each.
- 7Seals the Submission Key with the form's RSA public lock (OAEP, SHA-256).
- 8Sends the sealed key, the initialisation vector, and the encrypted blobs to our API.
Schweizerform server
Persist → forget
- 1Checks request size, rate limits, and the password challenge if one is set.
- 2Writes the encrypted blobs to Infomaniak's S3-compatible storage under {userId}/forms/{formId}/submissions/{submissionId}/.
- 3Saves the sealed key and initialisation vector in MySQL on the submission row. That is the only "key-shaped" data we store — and it's useless without your Access Code.
- 4Bumps the response counter on the form.
- 5Returns 201 Created. That's the end of the server's involvement.
- 6We never see plaintext. We never derive a key. We never hold a private key.
Decryption — owner side
When you open a submission, your browser walks the four-key chain in reverse: Access Code → Master Key → Form Key → Form Private Key → Submission Key → readable content. All of it happens locally; nothing decrypted ever leaves your device.
Threat model
What an attacker would have to do to read your forms.
We documented these so our customers and their security teams can review them. If you spot a scenario we haven't covered, write to support@schweizerform.ch.
| Adversary | What they get | What we do about it |
|---|---|---|
| Database dump leaked or stolen | Sealed data blobs and sealed key blobs. No readable content. | Everything is encrypted with AES-256-GCM. Without the owner's Access Code, not a single row decrypts. |
| Compromised server admin or insider | Same as above. Our server-side code never sees the Master Key or any plaintext. | An append-only internal audit log records every privileged action. Production is least-privilege; our engineers cannot read submissions through the dashboard. |
| Subpoena / lawful order to disclose | Ciphertext blobs and metadata (created-at, IP, request ID). | We can comply with a valid order and still hand over nothing readable. We document this on the public page. |
| Stolen owner session cookie | Access to the dashboard shell — but every form is still locked behind the Access Code, which we don't keep in the session. | Sessions are short-lived and slide on activity. The Access Code must be entered each session and lives only in browser memory. |
| Phishing / malicious browser extension | Whatever the user types. This is outside our trust boundary. | Strict Content Security Policy on the public form page. No third-party scripts allowed. We warn owners against shared or untrusted devices during onboarding. |
| Brute force on the Access Code | A slow wall of 100 000 PBKDF2 rounds standing between them and a single guess. | Each guess costs ~80 ms in a modern browser. We rate-limit logins on the server, lock accounts after repeated failures, and steer owners toward long, hard-to-guess Access Codes. |
| Forged / replayed submission | Junk bytes that fail authentication during decryption. | Each ciphertext is bound to its specific form and submission ID. A blob replayed against another form will simply fail to authenticate and be rejected. |
Infrastructure
Every byte, every server, in Switzerland.
Application servers, MySQL database, S3-compatible file storage, and transactional email all run on Infomaniak — a 100% Swiss-owned, employee-controlled hosting provider. The only outside calls we make from our servers are a brief IP lookup (3-second timeout, fail-closed) and Stripe for owner billing.
Stripe never touches respondent data. It only sees owner billing details for the subscription itself.
Application servers (Next.js)
Infomaniak · CH
MySQL database
Infomaniak · CH
Object storage (S3-compatible)
Infomaniak · CH
Transactional email (SMTP)
Infomaniak Mail · CH
DNS / network edge
Infomaniak Envoy · CH
Geo IP resolution
ipinfo.io · server-to-server only
Billing (owners only)
Stripe · owners only, never respondent data
Honest limits
Things we cannot do — and won't pretend to.
Zero-knowledge cuts both ways. Here's what it costs you, stated plainly.
Recover a lost Access Code.
Your Access Code is the starting point for every key that protects your forms. We never receive it, never store it, never email it. If it's lost, the data sealed under it is unrecoverable. You can change it from inside your account — that re-encrypts every form under the new code.
Reset a password and "decrypt your data automatically."
Some "encrypted" services do this — which quietly means they had the keys all along. We don't. A password reset gets you back into the dashboard, but you still need your Access Code to read any form.
Look up a submission's content for support.
If you write to us asking "what did so-and-so submit on Tuesday?", we genuinely cannot answer. Our support team sees the same scrambled bytes as everyone else. We can help with metadata: counts, timestamps, billing.
Run AI / analytics on submission content.
We don't train AI models on your form data — because we don't have your data in any form we could train on. Aggregate counts come from server-side metadata, never from the answers themselves.
What's encrypted, what isn't
Total transparency on every byte.
Some data has to stay readable for the system to work — your email address (for password resets), and the form's public lock (so respondents can encrypt to it). Everything else is opaque.
| Data | Visible to us | Why / how protected |
|---|---|---|
| Form answers (text, numbers, choices) | No | AES-256-GCM. The key never leaves the owner's browser. |
| File uploads (PDFs, images, etc.) | No | Each file is encrypted with the same one-time Submission Key. Original filenames are replaced with random IDs server-side. |
| Form titles, question labels | Partial | Stored on the form record so the public page can render the form. Hidden from respondents behind password protection until the password is accepted. |
| Owner email + name | Yes | Needed for login, billing, one-time codes, and password reset. Treated as personal data under nFADP and GDPR. |
| Owner password hash | No | bcrypt with 12 rounds. We never see the password itself, only the hash. |
| Submission timestamps + respondent IP | Yes | Needed for rate-limiting and audit. The IP is recorded in the audit log; it never appears as plaintext inside the form payload. |
| Submission ciphertext + wrapped key | No | Stored in S3 and MySQL. Both are useless without the chain of keys that ends in your Access Code. |
| Site analytics events (operator-side) | Aggregated | Pseudonymous (a hashed fingerprint), hosted by us on our Swiss servers. Used only for the marketing site and our internal observability — never for respondent answers. |
Compliance
Aligned with the rules that matter for sensitive data.
Schweizerform isn't a compliance-checkbox tool, but the architecture is designed so that meeting these rules becomes much easier for our customers.
nFADP
Revised Swiss Data Protection Act
In force since 1 September 2023. We align with its requirements: Swiss data residency, breach-notification readiness, data-subject rights, and encryption of personal data both at rest and in transit.
Architecture aligned · Compliant by designGDPR
EU General Data Protection Regulation
Compatible architecture: a lawful basis for the limited owner-side data we hold; respondent data never leaves Switzerland (so no Standard Contractual Clauses needed for it); a Data Processing Agreement is available on request for business customers.
Architecture aligned · DPA on requestHIPAA
US Health Insurance Portability & Accountability Act
Our encryption building blocks are compatible with the HIPAA Security Rule (encryption, access control, audit). We don't currently sign Business Associate Agreements — talk to us if you need one and we'll share where it sits on the roadmap.
Primitives compatible · BAA on roadmapBeyond encryption
The boring controls that hold the rest of it up.
Append-only audit log (operator-side)
We keep an internal, tamper-evident trail of every login, one-time code, form publish, and billing event — with IP, browser, and request ID — that we use for security review and incident response. It's an internal tool, not a customer-facing dashboard.
Session & OTP
Sessions slide for up to 7 days, with 128-character opaque tokens stored in HttpOnly + Secure + SameSite cookies. We use a 6-digit one-time code on signup and password reset.
Rate limiting
Persistent token-bucket rate limits on login, one-time codes, password attempts, and Access Code attempts. Survives restarts and works across multiple instances.
CSRF protection
Cross-site request forgery is blocked by a double-submit token across the whole authenticated surface. The public form route is exempt by design (no session) but is rate-limited.
Strict CSP & headers
The public form page disallows third-party scripts at the browser level via Content Security Policy. HSTS, X-Frame-Options, and X-Content-Type-Options are applied across the surface.
First-party error & site analytics
Our error logger and marketing-site analytics run on our own Swiss servers. Sentry, Google Analytics, and Mixpanel are not in the picture — and respondent submission data is never touched by either system.
Disclosure
Found something?
We take security reports seriously and reply within one business day. Please disclose responsibly — we credit researchers on request.
Contact
support@schweizerform.chPGP key on request. Please include reproduction steps, impact, and any logs — and don't include real customer data in your report.
Common questions