What Plikli does to keep sites safe
This page lists only measures built into Plikli today, described by what they do.
Keeping sites apart
Database row-level security
Tables holding site data use PostgreSQL row-level security (FORCE RLS), so one site’s rows can’t be read or written from another.
Checked in the API too
The API refuses requests for sites the signed-in user has no role on.
Sign-in and accounts
How passwords are stored
Passwords are hashed with scrypt and a random salt. They must be at least 10 characters.
Two-step sign-in
Authenticator apps (TOTP), plus 10 single-use backup codes stored as hashes. Each user can turn it on.
Sessions
Every request is checked against a server-side session. Signing out ends it at once, sessions expire after 7 days, and a password reset signs out every device. Roles are re-read on each request.
Rate limits
Sign-in, two-step codes, sign-up and password resets are limited to 10 tries per 15 minutes per IP address. The API, comments and contact forms have limits too.
No account lookups
The password-reset screen doesn’t reveal whether an email address has an account.
Roles and records
Six roles
Owner, administrator, editor, author (own posts only), contributor (drafts only) and viewer, set per site.
Audit log
Changes to content, media, settings, users and roles, domains, keys and exports are recorded.
Secrets
Encrypted at rest
Google and Shopify tokens, two-step secrets and WordPress import credentials are encrypted with AES-256-GCM. Import credentials are deleted when the import ends.
API keys stored as hashes
API keys and app passwords are stored only as SHA-256 hashes, shown once and revocable. One-time tokens are hashed and expire.
No default secrets
In production the app refuses to start with default secrets.
Content and files
HTML allowlist
Post HTML is cleaned against an allowlist both when saved and when shown: scripts, iframes in rich text and event handlers are removed, and only http, https, mailto and tel links are kept.
Checked uploads
Image types are detected from the file’s bytes, not its name, and SVG is refused. Images are served with nosniff and a sandbox CSP.
No plugin code runs
An uploaded WordPress plugin zip is only read for its header (up to 10 MB). Its code is never run or stored.
Connections
Admin CSP and HSTS
The admin sends a strict Content-Security-Policy (own scripts only, no framing by other sites), and the admin and API send HSTS.
Baseline headers on sites
Every public site sends nosniff, Referrer-Policy, X-Frame-Options (SAMEORIGIN) and Permissions-Policy.
Safe outbound fetching
When the server fetches outside pages (imports, blog cards, link checks), it connects only to public addresses, re-checks every redirect, and caps size and time.
Signed webhooks
Stripe and Shopify webhooks are processed only after their signatures are verified.
Readers’ privacy
Consent banner
Off, EU readers only, or everyone. Until the reader accepts, Google Analytics, Tag Manager, Google Ads, AdSense, embedded scripts and iframes and custom code are held back. Plikli itself sets no cookies.
External-transmission notice
A page (/third-party-services) listing each service, recipient, data and purpose, for Japan’s external-transmission rules. GA4, GTM, Google Ads, AdSense and a theme’s Google Fonts web font are added automatically, and owners can add more.
Privacy policy template
A template written for Japan’s APPI, with GDPR notes. Fill in the 【】 parts; it is not legal advice.
Data we don’t collect
Page views are kept as a daily count per post with no IP address stored (an IP is held in memory for 30 minutes to remove repeats). Comments and contact messages store no IP address either.
Start by moving one site.
New sites get 14 days of Pro for free, no card needed. The WordPress import shows you a preview before it writes anything.