Skip to content

Security & hardening

ShopClass ships with CSRF protection, hardened sessions, login throttling and CAPTCHA support. What follows is the deployment side — the parts that are your server’s job rather than the application’s.

The single most effective thing on this page. Most compromised PHP sites are running a version with a published, patched vulnerability.

  • Watch releases.
  • Apply core updates promptly — see updating.
  • Update plugins and themes too. An abandoned plugin is a liability regardless of how current core is.
  • Remove packages you do not use. Disabled is better than installed; uninstalled is better than disabled.

Serve the whole site over TLS, not just the login page. Session cookies sent over plain HTTP can be captured and replayed, and an admin session is worth capturing.

  • Redirect HTTP to HTTPS at the web server.
  • Set WEB_PATH to the https:// URL so generated links and e-mails match.
  • Consider HSTS once you are confident TLS will not be turned off.

The web-server user needs to write oc-content/ and its uploads/ and downloads/ subdirectories. It does not need to write anything else.

Terminal window
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod 640 config.php

config.php holds your database credentials. It should be readable by the web server and by nobody else.

oc-content/ is served over HTTP, and several things that can appear there should not be readable:

  • debug.log, queries.log, explain_queries.log — see debugging. Delete them when you are done and deny *.log in your server config.
  • Database dumps. Never leave a backup in the web root.

Turn OSC_DEBUG off in production. Displayed errors leak file paths, database structure and sometimes credentials to anyone who can trigger one.

  • Use a real admin username. Not admin, and not the site name.
  • Use a unique, long password, from a password manager.
  • One account per person, so the activity log attributes actions to someone.
  • Remove accounts when people leave.

For a site where the admin panel has no reason to be publicly reachable, put it behind an IP allowlist, a VPN or an identity proxy at the web-server layer.

Locked out? php oc-cli.php user:reset-password --user=<name> — see the CLI reference.

Failed logins are throttled per IP and per account by default — 20 attempts per IP and 10 per account in a 15-minute window.

Tune the limits in Settings → Spam and bots.

Turn on a CAPTCHA for publishing and registration before the site is public, add a posting delay, and require e-mail validation. See spam and abuse.

  • A dedicated user with privileges on one database. Never the MySQL root account.
  • A strong, unique password.
  • Not reachable from the internet — localhost or a private network only.

Uploaded images are user-controlled files. Make sure your web server will not execute anything in the uploads directory — deny PHP execution under oc-content/uploads/ at the server level. A polyglot file that is a valid image and valid PHP is a real technique.

If you have offloaded uploads to S3, the bucket should be public-read at most — never public-write.

Ransomware and a bad DELETE look the same from the restore side. Keep offsite, tested backups — see backups.

Follow the security policy, which sets out how to report privately and which versions are supported.

  • Running the current release; plugins and themes current
  • Unused plugins and themes removed
  • HTTPS enforced, WEB_PATH set to the https:// URL
  • config.php not world-readable; no 777 anywhere
  • OSC_DEBUG off; no log files or dumps in the web root
  • PHP execution denied under oc-content/uploads/
  • Admin username is not admin; one account per person
  • Real client IP configured if behind a proxy
  • CAPTCHA on publishing and registration
  • Database user scoped to one database, not reachable publicly
  • Offsite backups, and one of them restored successfully