Secure Blog CMS is a security-first PHP blogging platform that stores content in JSON files instead of a database. It includes a full admin UI, comment moderation, image uploads, and a Resilience Center for static exports and IPFS pinning.
- File-based storage (no SQL/database required)
- Drafts and published posts, slugs, excerpts, and pagination
- Optional search, private posts, and password-protected posts
- RSS feed generation
- Image uploads with server-side security checks
- Admin dashboard for posts, comments, users, and settings
- Roles: admin, editor, author
- Comment moderation (pending/approved/spam/trash)
- Backups and restore from the admin UI
- Static site export (HTML + RSS) for static hosting
- ZIP bundles for easy distribution
- Optional auto-pinning to IPFS via Pinata
- Export bundles stored in
data/exports/
- CSRF protection on all forms (single-use tokens, no replay)
- XSS sanitization and output escaping (DOM-based HTML purification)
- CSP headers enabled by default, plus standard HTTP security headers
- Rate limiting (login, comments, uploads, short URLs, post passwords)
- Account lockout after failed login attempts
- Session hardening with IP + User-Agent fingerprinting
- Security event logging to
data/logs/ - Mandatory SHA-256 checksums on upgrades; auto-upgrade disabled for safety
- Proxy header spoofing protection (Cloudflare/X-Forwarded headers gated behind config toggle)
- COOP/CORP security headers for cross-origin isolation
- Permissions-Policy header to restrict browser APIs
- HSTS with preload and includeSubDomains (respects proxy headers)
- Comment author name sanitization (strip tags, length limit, email validation)
- Site URL validation (scheme whitelist prevents javascript: and data: URLs)
- Per-user daily upload rate limiting (50/day per user)
- Post password brute-force protection (5 attempts per IP per 5 minutes)
- Check for updates from the admin panel
- Download and verify files with SHA-256 integrity checks
- Automatic backup before upgrade
- Config file never overwritten during updates
- One-click upgrade process
- PHP 7.4+ (PHP 8.x recommended)
- Web server (Apache/Nginx) or PHP built-in server
- Write access to the
data/directory - Extensions (optional but recommended):
curlfor Pinata IPFS pinning and in-app updateszipfor export ZIP bundlesdomfor DOM-based HTML sanitization (fallback regex available)
- Copy the project into your web root.
- Ensure the
data/directory is writable by the web server. - Visit
/install/index.phpin your browser and complete the wizard. - After install, keep
data/installed.lockin place (re-install requires deleting it). - Delete the
install/directory after installation for best security.
- Copy the project into your web root.
- Ensure the
data/directory is writable by the web server. - Generate an Argon2id password hash (use single quotes to avoid
$interpretation):
cat > /tmp/hashpass.php << 'EOF'
<?php
echo password_hash('YourSecurePassword123!', PASSWORD_ARGON2ID) . PHP_EOL;
EOF
php /tmp/hashpass.php- Update
includes/config.phpwith your credentials (use single quotes for the hash):
define('ADMIN_USERNAME', 'your_username');
define('ADMIN_PASSWORD_HASH', '$argon2id$v=19$m=65536,...'); // single quotes!- Update site settings in
includes/config.phpor the admin UI. - Open
/admin.phpand log in.
⚠️ Important: Always use single quotes aroundADMIN_PASSWORD_HASH. Argon2id hashes contain$characters which PHP interprets as variable references inside double-quoted strings, corrupting the hash and breaking login.
If your CMS is behind Cloudflare or a reverse proxy that sets X-Forwarded-For or CF-Connecting-IP headers, enable trusted proxy mode in includes/config.php:
define('TRUST_PROXY_HEADERS', true);Warning: Only enable this when actually behind a trusted proxy. Enabling it without a proxy allows IP spoofing and session bypass.
Most settings are managed in the admin UI at /admin/settings.php and stored in:
data/settings/site.json
These settings override defaults from includes/config.php.
Comments can require hCaptcha. Configure via environment variables (recommended):
HCAPTCHA_SITEKEYHCAPTCHA_SECRET
Or set hcaptcha_sitekey in data/settings/site.json and keep the secret in env.
Configure Pinata credentials in /admin/settings.php to enable auto-pinning of exports.
- Go to
/admin/upgrade.phpand check for updates. - The upgrader downloads
update/manifest.jsonfrom the configured update source. - All file updates require SHA-256 checksum verification — no file is written without integrity verification.
includes/config.phpis never overwritten — your credentials and settings are preserved.- Auto-upgrade has been disabled for security. All upgrades must be manually triggered.
- Backup
data/andincludes/config.php. - Replace application files with the new release.
- Do NOT overwrite
includes/config.php— preserve your existing credentials and settings. - Re-check your settings and log in to confirm.
For maintainers, use the generate_manifest.sh script to prepare updates:
cd update/
./generate_manifest.sh 1.6.0 "Description of changes"
# Then commit, tag, and push:
git add update/ && git commit -m "v1.6.0: update manifest"
git tag -a v1.6.0 -m "v1.6.0"
git push origin main --tagssecure-blog-cms/
admin/ Admin UI (posts, comments, users, settings, resilience, upgrade)
cli/ CLI utilities (password reset)
data/ JSON data storage (posts, users, comments, logs, backups)
includes/ Core classes (Security, Storage, Comments, Resilience, Uploads)
install/ Installation wizard (delete after install)
templates/ Public templates
update/ Update packages, manifest, and release files
index.php Public homepage
post.php Single post view
rss.php RSS feed
s.php Short URL redirect handler
A sample nginx config is included as nginx.conf with:
- Pretty URL rewrites (WordPress-style
/post/slug/,/category/tech/, etc.) - Security deny rules for
data/,includes/,install/, andcli/directories - Static file caching headers
When deploying behind CloudPanel with Varnish:
- Set
TRUST_PROXY_HEADERStotrueinincludes/config.php - Add nginx deny rules for
data/,includes/,install/, andcli/directories - Ensure parent directory permissions are
755(CloudPanel may reset to770) - Delete the
install/directory after setup
includes/config.phpcontains your admin credentials and site settings- Never overwrite it during updates — the updater skips it automatically
includes/config.php.exampleis provided as a reference template- When deploying manually, always exclude
includes/config.phpfrom file copies - The
ADMIN_PASSWORD_HASHmust use single quotes (not double quotes) to prevent PHP from interpreting$in Argon2id hashes
New Features:
- User Management — Create, edit, and delete users from the admin panel
- Password Policy — Enforced minimum 12 characters with uppercase, lowercase, digit, and special character requirements
- Role-Based Permissions — Admin (full access), Editor (publish/edit any post, moderate comments), Author (create/edit own posts only)
- Password Strength Meter — Visual strength indicator on user creation and edit forms
- Edit User Modal — Change role and password with admin password confirmation required
- Self-Demotion Protection — Admins cannot demote themselves to a lower role
- CLI Password Reset —
cli/reset_password.phputility for emergency password resets when locked out; interactive mode avoids shell expansion of special characters - Installer Password Policy — Visual checklist enforces password requirements during installation
- Special Character Safety — Passwords with
$,!,*, etc. are properly handled throughout the system (CLI, admin UI, installer, JSON storage)
Security:
nginx.confupdated: addedcli/to blocked directories (both Option A and Option B)cli/.htaccessdenies all web access to CLI scripts
Improvements:
admin/users.php— No-cache headers for CSRF token freshness behind Cloudflare/Varnish- Password hashing uses Argon2id with tuned parameters (bcrypt fallback)
New Features:
- Category & Tag Deletion — Delete categories and tags with automatic cleanup of post references
- Slug Collision Resolution — Auto-appends
-2,-3, etc. when a slug already exists - Duplicate Prevention — Case-insensitive name matching rejects exact duplicates; slug collisions auto-resolved
Improvements:
- No-cache headers on admin categories page for CSRF token freshness behind Cloudflare/Varnish
- Branding updated to Digital Systems LLC / AfterPacket
- Removed duplicate version display in public footer
- Admin categories page now shows post count per category/tag
Bug Fixes:
- Fixed PHP syntax error in
addCategory()return statement - Fixed delete confirmation dialog quoting issues
- Fixed CSRF token invalidation on category/tag management page
Critical Bug Fixes:
- [CRITICAL] Admin session logout on idle — session fingerprint validation was destroying sessions when IP or User-Agent shifted between requests behind Cloudflare/Varnish proxies. Now logs a warning and updates the fingerprint instead of destroying the session.
- [CRITICAL] Argon2id password hash corruption — hashes containing
$characters were corrupted by PHP variable interpolation in double-quoted strings, causing admin login failures.ADMIN_PASSWORD_HASHnow uses single quotes. - [CRITICAL] Updater "Requested version not found in manifest" error — Upgrader required exact version string match. Now uses the manifest version directly and only checks that it's newer than the current version. Update cache also cleared before upgrading.
Improvements:
- Session regenerate interval increased from 30 minutes to 4 hours (less disruption for users)
config.phphas prominent DO NOT OVERWRITE warning header- Added
config.php.exampleas install template — installer copies from example if config.php doesn't exist includes/config.phpremoved from update manifest — updater will never overwrite it- Built-in updater now has real SHA-256 hashes for integrity verification
- Added
generate_manifest.shscript for maintainers to generate release manifests
Critical Bug Fix:
- [CRITICAL] Password hash corruption fix (same root cause as v1.5.3, addressed in config.php template and installer)
Improvements:
- Added
config.php.exampleas install template - Installer copies from config.php.example; uses single-quote replacement for password hash
- Removed config.php from update manifest
Security Fixes:
- [MEDIUM] Rate limiting added to post password attempts (5 per IP per 5 minutes) — prevents brute-force attacks against password-protected posts.
- [MEDIUM] Session fingerprint now hashes User-Agent with SHA-256 — strengthens session binding beyond IP-only, format:
sha256(ip | sha256(user_agent)). - [MEDIUM] Site URL setting validation added —
filter_var(FILTER_VALIDATE_URL)and scheme whitelist (http/https) prevent open redirect and XSS via malicious URL values. - [MEDIUM] Cross-Origin isolation headers added —
Cross-Origin-Opener-Policy: same-originandCross-Origin-Resource-Policy: same-originprevent cross-origin information leakage. - [MEDIUM] Comment author name sanitization — HTML tags stripped, length capped at 100 characters. Email validated when provided.
- [MEDIUM] Per-user daily upload rate limit added (50 uploads/day per user) alongside existing per-IP hourly limit.
Security Fixes:
- [HIGH] Removed CSRF token from image upload URL query string — tokens were being logged in server access logs and browser history. Now sent only via
X-CSRF-Tokenheader and POST body. - [HIGH] Removed
data:from public CSPimg-src— prevents SVG-based XSS throughdata:image/svg+xmlURIs. Admin CSP still allowsdata:for TinyMCE paste compatibility. - [HIGH] HSTS header now respects
TRUST_PROXY_HEADERS— previously only checked$_SERVER["HTTPS"], which is empty behind Cloudflare/Varnish. Sites behind proxies now correctly send HSTS. - [MEDIUM] Short URL redirect (
s.php) changed from 301 to 302 — prevents browser cache poisoning if target changes. - [MEDIUM] Short URL redirect now validates resolved slug corresponds to a published post — prevents open redirect.
- [MEDIUM] Post password hashing upgraded from bcrypt to Argon2id with fallback.
- [MEDIUM] Removed
ini_set()calls forallow_url_fopen/allow_url_include— these arePHP_INI_SYSTEMdirectives. Added comments for php.ini configuration. - [LOW] Removed debug
error_log()fromImageUpload.php. - [LOW] Fixed
Content-Dispositionfilename escaping inserve-image.php.
Bug Fixes:
- [CRITICAL] All internal links now use
cms_path()— fixes broken pagination, search, admin links, RSS, and comment forms in subfolder installs. - [CRITICAL] Fixed
index.phpline 469 — missing?>closing tag caused 500 parse error on PHP 8.x.
Security Fixes:
- [HIGH] Password input fields for post protection were
type="text"— changed totype="password". - [HIGH]
ENABLE_UPLOAD_MALWARE_SCANwasfalseby default — changed totrue. - [MEDIUM] Session cookie
secureflag now respectsTRUST_PROXY_HEADERS. - [MEDIUM] Removed debug
console.logstatements from create/edit post pages.
Bug Fixes:
- Image URL insertion: TinyMCE
valid_elementsnow allowsclassandstyleon<img>. - Image paste: Enabled
paste_data_images: truein TinyMCE. - RSS self-link: Now uses
cms_path()for correct URLs in subfolder installs.
Critical Fixes:
- [CRITICAL] Removed Remote Code Execution vector in upgrade system —
download_urlno longer accepted from POST data. Upgrades useperformUpgradeFromManifest()with hardcoded manifest URL. SHA-256 checksums mandatory. Auto-upgrade disabled. - [CRITICAL] Added credential placeholder detection — warns if
REPLACE_ME_*defaults are still in place.
High Fixes:
- [HIGH] Replaced regex XSS sanitizer with DOM-based HTML purification (DOMDocument + XPath). Regex fallback for servers without
domextension. - [HIGH] CSP headers enabled by default.
- [HIGH] CSRF tokens now single-use for all forms — removed
image_upload/edit_post_formreuse exception. - [HIGH] Session fingerprint gated behind
TRUST_PROXY_HEADERSconfig (default:false). - [HIGH] Removed version disclosure header (
X-SecureBlogCMS-Version) — only sent ifSHOW_VERSION_HEADERis explicitlytrue. - [HIGH] Password protection enforced on public pages. Private posts hidden from listings, search, and RSS.
Medium Fixes:
- Role validation whitelist enforced in
addUser()andupdateUser(). - Password hashing unified to Argon2id across all user management.
- Rate limiting on comment submissions (3/IP/hour).
- Install directory
.htaccesshardened. - CORS credentials set to
falseon image endpoints. - Debug logging reduced in upload endpoint.
- Error reporting hardened to
E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE.
Version: 1.5.6
Last Updated: 2026-07-14
Created by: Digital Systems LLC / AfterPacket
Security Level: High