Skip to main content

Security & Hardening

warning

Most WordPress vulnerabilities come from custom code. Treat child themes like production codebases with reviews and rollback.

Security & Hardening Explained​

Security areaWhat to doExample
Output escapingEscape anything dynamicesc_html(), esc_url()
Input sanitizationSanitize user inputsanitize_text_field()
NoncesProtect state-changing actionswp_nonce_field()
Secrets hygieneNever commit keysNo .env in theme repo
LogsUse server + WP logs/usr/local/lsws/logs/error.log

Why It Matters​

  • Child themes often add shortcodes and hooks that output content.
  • That output is a common XSS vector if unescaped.
  • You also need safe rollback and audit trails.

How It Works​

Secure WordPress code follows a simple rule: sanitize input, escape output, and validate permissions before doing anything destructive.

Practical Walkthrough​

Step 1: Audit Your Child Theme for Risky Patterns​

audit-child-theme-security.sh
cd /var/www/html/wp-content/themes/generatepress-child
grep -R "echo \$\|\$_GET\|\$_POST\|eval\(" -n . | head -n 80 || true

Step 2: Check File Permissions​

check-child-theme-perms.sh
cd /var/www/html
stat wp-content/themes/generatepress-child
ls -lah wp-content/themes/generatepress-child | head -n 40

Step 3: Know Where Logs Are​

check-common-log-locations.sh
ls -lah /usr/local/lsws/logs 2>/dev/null | head -n 40 || true
ls -lah wp-content 2>/dev/null | head -n 40 || true

Step 4: Add a Basic WP Hardening Constant (Optional)​

In wp-config.php (server-managed):

wp-config.php
define( 'DISALLOW_FILE_EDIT', true );

Optional (if your stack allows it):

wp-config.php
define( 'DISALLOW_FILE_MODS', false );

Practical Examples​

Example 1: A Safe Shortcode​

wp-content/themes/generatepress-child/functions.php
<?php

add_shortcode( 'safe_notice', function( $atts ) {
$atts = shortcode_atts( array( 'text' => '' ), $atts );
return '<div class="notice">' . esc_html( (string) $atts['text'] ) . '</div>';
} );

Example 2: Avoid Secrets in Theme Code​

Bad:

  • API keys in functions.php
  • private URLs in templates

Good:

  • Use environment variables (server-level)
  • Use WP config constants (not committed)

Example 3: Nonce Pattern (Admin/Form Work)​

If you add forms or admin actions, use nonces and capability checks.

Example 4: Escape URLs​

wp-content/themes/generatepress-child/inc/hooks.php
<?php

add_action( 'generate_footer', function() {
echo '<a href="' . esc_url( home_url( '/' ) ) . '">Home</a>';
} );

Best Practices​

PracticeWhy
Escape output by defaultPrevents XSS
Sanitize all inputPrevents injection
Review code changesReduces risk
Keep secrets out of reposPrevents leaks
Patch quicklyReduce exposure time
Keep a staging environmentTest updates safely
Disable file editing in adminReduces attack surface
Review third-party scriptsCommon compromise vector

Troubleshooting​

SymptomCauseFix
Unexpected markup injectionUnescaped inputAudit shortcodes and hook callbacks
White screen after changeFatal errorCheck error logs and roll back
Security plugin flags themeSuspicious patternsRemove risky functions and refactor
Random redirectsCompromised plugin/themeDisable plugins on staging and audit logs

Hands-On​

  1. Search your child theme for $_GET and $_POST usage.
  2. Replace one unsafe echo with esc_html().
  3. Add a short note in README about secrets policy.
  4. Add DISALLOW_FILE_EDIT on staging and confirm wp-admin cannot edit files.
  5. Add one new shortcode and ensure it escapes output.

Quick Reference​

security-cheatsheet.sh
cd /var/www/html/wp-content/themes/generatepress-child
grep -R "\$_GET\|\$_POST\|eval\(" -n . | head

What's Next​