Security rules
Security rules that third-party resources must follow. Every item here comes from a real type of incident.
Output: XSS
- Always use
{{ }}(escaped) when outputting user input. - Use
{!! !!}only for HTML the core has sanitized (editor bodygetContent()). The moment you put an unsanitized value in, it's XSS. - When putting values into a JS context, use
@json($value)or the|escapejsfilter. Don't build scripts by concatenating strings.
Input: validate on the server
- Validation on the screen (JS) is only a convenience. Redo all validation in the proc action (server).
- Cast numbers with
(int), and check values with an allow list usingin_array(..., true). - As long as you use query XML bindings, SQL injection is blocked, but tighten further with
filter="number"andnotnull. Always put notnull on key conditions so an update/delete never runs without conditions.
CSRF and action design
- Actions that change data must be proc actions over POST. The core checks CSRF tokens automatically.
- Never change data in GET (disp). A structure where a single link triggers a delete also bypasses the CSRF check.
- Only callbacks called by external servers get
standalone="true" check-csrf="false", and inside that action you must do your own verification (signatures, state tokens, server-to-server rechecks). Don't trust values that came through the browser.
Permission checks
- Don't rely only on the
permissionattribute in module.xml; for actions that need an ownership check (editing your own post, etc.), comparemember_srlinside the action. - Guard admin features in two layers:
permission="manager"plus a permission branch on the screen. Merely hiding a button on the screen is not protection.
File uploads
- Check extensions with an allow list (block lists can be bypassed).
- Set a size limit, and generate a new stored file name instead of using the original.
- So that executable files (.php, etc.) can't run from the upload path, use the core file module path (
files/attach). The web server configuration blocks PHP execution on this path.
Storing secrets
- When storing tokens or keys in the DB, store a hash (for lookup) or encrypt them (when decryption is needed). The core's
Zittme\Framework\Securityhas encryption tools. - If you create temporary state files under
files/cache, confirm they can't be accessed directly over the web. For a new path not covered by the web server's block rules, you must add a rule. There was a real incident where this path was exposed with HTTP 200. - Don't hard-code credentials. Store them in module settings (insertModuleConfig) and show them masked on screen.
External integrations
- Callbacks our server receives: don't trust values that came through the browser; re-verify via server-to-server communication (payment amounts, authentication results, etc.).
- For authorization code exchange, use standard protections such as a state token (state) and PKCE.
- Make sure no unnecessary Set-Cookie goes out in responses. If a session cookie is newly issued on a cross-site callback, it can overwrite the user's existing session.
Final checklist
- Is there anywhere user input is output without escaping?
- Is there anywhere data changes via GET without proc?
- Are there any update/delete queries without conditions?
- Are upload extension and size checks done on the server?
- Are secrets stored in plain text or in a path exposed to the web?