Security principles
Anvil is designed for environments that are audited, e.g. against ISO/IEC 27001. A library cannot be certified on its own, but it can provide the technical controls an audit expects and make the secure way the easy way.
Priorities
Section titled “Priorities”- Security
- Performance
- Stability
When these goals conflict, the higher one wins.
Principles
Section titled “Principles”| Principle | Implementation |
|---|---|
| Deny by default | The gate denies unknown permissions. Every endpoint action and API endpoint needs a permission. Tenant scoped data fails closed without a tenant. |
| No internal identifiers on the client | Only UUIDs leave the server (Config\Anvil::$internalFields), see data rules. |
| The client knows nothing about the server | One endpoint per panel with encrypted, session bound tokens; no class names in requests. |
| Least privilege | Permissions are generated per panel, resource, action, relation action, export and page (php spark anvil:permissions); roles grant them per tenant, see Users and roles. Policies restrict single records. |
| Traceability | Every change and every security event is written to a tamper evident audit log. |
| Purpose bound keys | All keys are derived from encryption.key via HKDF, one per purpose (Anvil\Security\KeyRing); the key must have at least 32 bytes and can be rotated (encryption.previousKeys, anvil:rekey). The audit log can have a key of its own. |
| No third-party CDNs | All assets (CSS, JS, fonts, Chart.js) are bundled and served by the application with integrity hashes, see Styling. |
| Strict CSP | No inline scripts, event attributes or style attributes; htmx without eval, Alpine in its CSP build, see Styling. |
| Secure defaults | Insecure options must be enabled explicitly and are documented as such. |
Built-in protections
Section titled “Built-in protections”| Area | Protection |
|---|---|
| Headers | Panel pages and endpoint answers carry X-Frame-Options: DENY, X-Content-Type-Options: nosniff and Referrer-Policy: same-origin (Anvil\Security\SecurityHeaders); headers set by the application are kept, and CodeIgniter’s secureheaders filter, which runs later, turns the frame header into SAMEORIGIN. Framing by other sites is refused by the CSP (frame-ancestors 'none'), so keep the CSP enabled. CSP and HSTS stay with the application, see Deployment and Styling. |
| Rate limits | Component endpoint: Config\Anvil::$endpointRateLimit (120 per minute and IP); single sign-on: 30 per minute and IP; API: $apiRateLimit per client and $apiIpRateLimit per IP. Exceeded limits answer 429 and are recorded as security events. |
| Login | Two-factor authentication with lockout, single sign-on with signed tokens and assertions, panels restricted to login methods (Panel::authMethods()). |
| Escalation | Users can only grant permissions they hold themselves, see Users and roles. |
| Links and redirects | Redirects of components only accept relative, same-origin paths (Anvil\Support\SafeUrl). External links only lead to the systems in Config\Anvil::$links, over HTTPS and with noopener unless a system is trusted (Anvil\Support\Link). |
| Uploads | Allowed types are checked against the content, files are stored outside the web root and can be scanned for viruses, see Files. |
| API | Hashed tokens, IP allow lists, optional request signatures, expiry and review notices, see API. |
Reporting vulnerabilities
Section titled “Reporting vulnerabilities”Do not open a public issue. Report vulnerabilities by e-mail as described in
SECURITY.md of the repository.