Security

SynaptikCMS ships with a layered security setup out of the box. This page covers what is protected, how, and what you should configure before going to production.


Admin folder

During installation, install.php lets you choose a custom name for the admin folder. Use something non-obvious — avoid admin, cms, backend, or any other common name. This is the single most effective measure against automated scanners.


Protected paths

Direct HTTP access to sensitive paths is blocked at the .htaccess level.

Blocked files

PatternWhy
settings.json, data.jsonSite config and content database
admin-credentials.phpHashed admin password
.htaccess filesPrevent rule disclosure

Blocked directories

PathMethod
/bckps/Rewrite rule — returns 403 unless the referer contains admin
/data/.htaccess inside the folder — Deny from all
/private/.htaccess inside the folder — Deny from all

⚠️

The /bckps/ protection relies on the HTTP_Referer header, which can be spoofed. Do not treat it as a hard security boundary. Keep backups out of the web root for production deployments.


Security headers

Set via mod_headers in the root .htaccess:

HeaderValuePurpose
X-Frame-OptionsSAMEORIGINPrevents clickjacking — the site can only be framed by itself
X-Content-Type-OptionsnosniffBrowser must honour the declared Content-Type
Referrer-Policystrict-origin-when-cross-originLimits referer leakage to external domains
Permissions-Policycamera, mic, geo, payment all disabledDisables unused browser APIs
Content-Security-PolicySee belowControls which origins can load scripts, styles, fonts, frames

Default CSP

default-src 'self';
script-src  'self' 'unsafe-inline' https://js.hcaptcha.com https://cdn.jsdelivr.net;
frame-src   https://newassets.hcaptcha.com;
style-src   'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net;
font-src    'self' https://fonts.gstatic.com https://cdn.jsdelivr.net;
img-src     'self' data: blob:;
connect-src 'self' https://hcaptcha.com https://api.hcaptcha.com;
frame-ancestors 'self'

If you are not using hCaptcha, remove the js.hcaptcha.com, newassets.hcaptcha.com, hcaptcha.com, and api.hcaptcha.com entries. If you are not using Google Fonts, remove fonts.googleapis.com and fonts.gstatic.com.

ℹ️

'unsafe-inline' is required for scripts because the CMS injects inline JS for the i18n bridge (window.CMS_LANG) and gallery initialisation. Removing it will break the front end.


Contact form

The contact form (contact-process.php) has four independent protection layers:

LayerMechanism
CSRFHMAC-SHA256 token derived from a 32-byte secret stored in /private/contact.secret
HoneypotHidden field that bots fill in — submission is rejected silently
Rate limitingMax 5 submissions per IP per hour. IPs are stored hashed (SHA-256) in /private/contact_rate.json
hCaptchaOptional server-side verification. Configure via Settings → Contact

Theme preview token

The live theme preview uses a signed HMAC-SHA256 token with a 2-hour TTL. The secret is derived from the bcrypt hash of the stored admin password — no additional secret file is needed. The token only works when $_SESSION['admin'] === true.

See Live Theme Preview for full details.


Browser caching strategy

Assets are cached aggressively. The .htaccess sets:

Asset typeCache-ControlMax-age
CSS, JSpublic, immutable1 year
Images, fontspublic1 year
HTML, PHPno-storeNever cached

CSS and JS files served by render_header_scripts() include a ?v=<mtime> cache-busting parameter, so browsers automatically pick up changes without needing to clear cache.


Production checklist

[ ] Admin folder renamed to something non-obvious
[ ] /data/ and /private/ not accessible from the web (test: curl -I yourdomain.com/data/_index.json should return 403)
[ ] settings.json not accessible (curl -I yourdomain.com/settings.json should return 403)
[ ] HTTPS enabled — the CSP and cookie session are ineffective over HTTP
[ ] Directory listing disabled on the server (Options -Indexes or server config)
[ ] PHP error display off in production (display_errors = Off in php.ini)
[ ] /bckps/ moved out of the web root, or protected by server-level auth