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