Security
Security
This page describes what the application actually does today. It is not a certification claim. Where something depends on how your copy is hosted, it says so.
Accounts and sessions
- Passwords are hashed with PHP's
password_hash, never stored or logged in plain text. - Sessions use HttpOnly, SameSite=Lax cookies, and are marked Secure when served over HTTPS. The session id is regenerated at sign-in.
- A session is tied to the current password. Changing or resetting it signs out every other device.
- Two-factor authentication uses time-based one-time codes with recovery codes.
- Sign-in, registration, password reset and magic-link requests are rate limited.
Requests and pages
- Every state-changing request from a signed-in browser needs a CSRF token.
- The Content Security Policy allows scripts only from the site itself and from inline blocks that carry a per-request nonce. There are no inline event handlers.
- Responses send
X-Content-Type-Options: nosniff, a strict referrer policy and a restrictive permissions policy. Over HTTPS they add HTTP Strict Transport Security. - Only public form pages can be framed by other sites, so they can be embedded. Everything else refuses to be framed.
- Database access uses prepared statements. Output is escaped in templates.
Access control
- Every read and write of a form checks the person's role in the workspace and on the form. Someone with no access gets a 404, so a form's existence is not revealed.
- Workspace roles and per-form roles decide who can view, edit and manage.
- Settings, access and key changes are written to an audit log, kept for a year. Viewing the log is part of the team plan.
Respondents and abuse
- Public form endpoints are rate limited and protected by a signed page token. A hidden honeypot field silently discards bot submissions.
- Cloudflare Turnstile can be switched on by the operator.
- Uploads are checked against an allow-list of file types, given random names and stored outside the public web root. They are only served through the application, after a permission check.
- Respondents' resume tokens are stored as SHA-256 hashes.
- IP addresses are never stored as they are. A salted hash that changes daily is used for abuse control.
API keys and webhooks
- API keys start with
fwk_, are shown once and are stored only as a SHA-256 hash. Read keys cannot write, and a key never exceeds its owner's permissions. - Webhook deliveries are signed with HMAC-SHA256 over a timestamp and the body. Signing secrets are encrypted at rest with AES-256-GCM.
- Webhook targets are resolved and checked before a request is made. Private, loopback and link-local addresses are refused, and the resolved address is pinned so a second DNS answer cannot redirect the request.
Your data
- You can export everything tied to your account and delete the account.
- A form in the trash is deleted after 30 days. Unresumed partial responses are deleted after 30 days.
- Data is stored in the database and file storage of the server your copy of Formwork runs on. This site adds no third-party analytics scripts.
Reporting a problem
If you think you found a vulnerability, please use the contact page and describe what you saw. Do not include other people's data. We read every report.