Security Model for Survey Submissions#
This document explains the protections used during form rendering, submission,
and result storage. It covers the checks performed in @@viewer,
@@get-form-json, and @@save-poll, plus the configuration surface at both
global and per-survey levels.
Scope#
These safeguards apply to:
Rendering forms for end users (
@@viewerand@@get-form-json).Submitting survey results (
@@save-poll).Storing results in ZODB or SQL backends.
Global Configuration (Plone Registry)#
All options below live in IFormsSettings and affect every survey.
Authenticity token (JWT) options#
These settings require a short-lived, signed token for submissions. Tokens are embedded in the rendered form and must be sent back on submit.
authenticity_token_enabled(Bool, default:True)When enabled, submissions must include a valid JWT in
auth_token.When disabled, JWTs are not required.
authenticity_token_secret(Password, default: empty)HMAC secret used to sign and verify JWTs.
Required if JWT enforcement is enabled.
authenticity_token_ttl_seconds(Int, default:600, min:60)Token lifetime in seconds.
Enforced via the
expclaim.
authenticity_token_issuer(Text, default:zopyx.surveyjs)Expected
issclaim value.
authenticity_token_audience(Text, default:zopyx.surveyjs)Expected
audclaim value.
authenticity_token_cache_path(Text, default:var/token_cache.db)Filesystem location for diskcache.
Tracks issued and received tokens for replay prevention.
Result storage options (security relevant)#
result_storage_backend(Choice, default:zodb)Selects the storage backend. Validation is unchanged, but persistence and operational controls differ.
database_uri(Text, default:sqlite:///var/surveyjs-results.db)Database URL for relational result storage.
Survey-Specific Configuration#
These options are set on each Survey content item.
Form access policy#
access_mode(Choice, default:public)public: Form is accessible without a URL access token.trusted: Form requires a trusted access token for rendering and submit.
trusted_access_ttl_hours(Int, default:168, min:1)Lifetime of trusted access tokens in hours.
Drives diskcache expiration and
expires_atmetadata.
Submission validation and limits#
max_payload_size_mb(Int, default:1)Maximum request payload size (bytes derived from MB).
Checked before JSON parsing.
force_server_side_validation(Bool, default:True)Runs an external validator on every submission.
Failed validation rejects the submission.
Security Measures: How They Work#
1. CSRF protection (Plone default)#
Submission requests include the _authenticator token (Plone CSRF). The client
sends it and Plone validates it to block cross-site request forgery for
authenticated sessions. Anonymous submissions may still succeed if the view is
public and Plone accepts the authenticator.
2. Trusted access token (URL-based access control)#
Purpose#
Limits form rendering and submission to users who possess a trusted URL token.
Token type and storage#
Opaque short token generated server-side.
Stored in diskcache under
trusted:<token>with metadata:form_id: survey identifierform_version: version at issuance timeissued_at: ISO timestampexpires_at: ISO timestampstate: currentlyISSUED
Enforcement points#
@@get-form-json: checks foraccess_tokenin query or form.@@save-poll: checks foraccess_tokenin form data.PDF submission path: same trusted access check.
Failure behavior#
Missing or invalid token -> HTTP 403 with JSON error.
Cache unavailable -> HTTP 503 with JSON error.
Form mismatch -> HTTP 403 with JSON error.
3. Authenticity token (JWT with replay protection)#
Purpose#
Confirms submissions were rendered by the server (short-lived JWT) and reduces replay risk.
Token creation#
Generated for each form render via
build_auth_token.Embedded in the page (
AUTH_TOKENJS variable).Claims include:
iss/audexp/nbf/iatform_idform_versionjti(token id)
Validation#
@@save-polland PDF submission path verify JWT signature and claims.Invalid or expired tokens reject the submission with HTTP 403.
Replay protection#
diskcache keys:
issued:<token>set at issuancereceived:<token>set at first submission
If
received:<token>is present, submission is rejected as replay.Cache TTL: 24 hours for authenticity tokens.
4. Payload size enforcement#
Content-Lengthand raw payload size are checked.Requests above
max_payload_size_mbare rejected with HTTP 413.Protects against oversized payloads and memory pressure.
5. Server-side validation#
If
force_server_side_validationis enabled, an external validator runs on every submission.Validation failures reject the submission (status per validator).
Token Lifetimes and Defaults#
Authenticity JWT (global):
Default: 600 seconds (10 minutes).
Min: 60 seconds.
Trusted access token (per survey):
Default: 168 hours (7 days).
Min: 1 hour.
Diskcache TTL:
Authenticity JWT replay cache: 24 hours.
Trusted access tokens: per-survey TTL.