Skip to content
Docs

Developer guide

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 body getContent()). The moment you put an unsanitized value in, it's XSS.
  • When putting values into a JS context, use @json($value) or the |escapejs filter. 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 using in_array(..., true).
  • As long as you use query XML bindings, SQL injection is blocked, but tighten further with filter="number" and notnull. 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 permission attribute in module.xml; for actions that need an ownership check (editing your own post, etc.), compare member_srl inside 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\Security has 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?