PW Security Helpers
A plugin that help you toggle common security hardening features
by Tyrus Christiana · github.com/hawkspook/wordpress-security-helper · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/hawkspook/wordpress-security-helper/archive/refs/heads/main.zipPW Security Helpers is a lightweight WordPress plugin that adds opt-in hardening controls to common attack surfaces and delivers optional HTTP security headers. The plugin is designed to be safe-by-default, requiring administrators to explicitly enable each mitigation so it plays well with a wide variety of site configurations.
Features
- Toggle REST API user enumeration protections that block ALL access to
/wp-json/wp/v2/users. - Prevent classic
/?author=1probing for author archives. - Disable the legacy
xmlrpc.phpendpoint when it is not required. - Restrict the REST API to authenticated users only.
- NEW: Hide WordPress Version - Remove version information from meta tags, feeds, and block readme files.
- NEW: Hide Plugin/Theme Versions - Remove version query strings from CSS/JS files.
- Enable and configure a suite of HTTP security headers including:
Strict-Transport-SecurityContent-Security-PolicyX-Frame-OptionsX-Content-Type-OptionsReferrer-PolicyPermissions-Policy
- NEW: CSP Builder - Automated Content Security Policy generator:
- Scan multiple pages to discover all resources (scripts, styles, images, fonts, etc.)
- Visual interface to select which resources to allow without writing CSP code
- Automatic nonce generation for inline scripts and styles
- Report-Only mode for safe testing before enforcement
- Real-time policy preview with copy-to-clipboard functionality
- Tabbed settings page for easy navigation between core protections, security headers, and CSP builder.
Requirements
- WordPress 6.4 or newer (tested up to the latest stable release at the time of writing).
- Administrator capability to manage site settings.
Installation
- Copy the plugin directory into your WordPress installation under
wp-content/plugins/pw-security-helpers. - Log in to the WordPress admin dashboard and activate PW Security Helpers from the Plugins screen.
Usage
Core Protections & Security Headers
- Navigate to
Settings → PW Security Helpers. - Use the Core Protections tab to enable REST and XML-RPC hardening features as needed.
- Switch to the Security Headers tab to enable the headers appropriate for your site. Adjust directive values before enabling to avoid breaking legitimate functionality.
- Save changes after updating each tab.
CSP Builder (Content Security Policy Generator)
The CSP Builder provides a no-code solution for creating Content Security Policies:
- Navigate to
Settings → PW Security Helpers → CSP Builder. - Add URLs to scan:
- Enter the URLs of pages you want to scan (homepage, key pages, posts, etc.)
- Click "Add URL" for each page
- Click "Scan Now" to analyze all added pages
- Review discovered resources:
- The scanner will find all scripts, styles, images, fonts, and other resources used on your pages
- Resources are organized by CSP directive type (script-src, style-src, etc.)
- Use "Select All" / "Deselect All" buttons for quick management
- Configure inline code handling:
- If inline scripts or styles are detected, enable "Generate nonces" for secure handling
- Nonces are automatically added to enqueued scripts and styles
- Choose deployment mode:
- Report-Only (recommended): Logs violations without blocking them - perfect for testing
- Enforce: Actively blocks policy violations
- Review and deploy:
- Preview the generated policy in real-time
- Check "Use this generated policy" to activate it
- The generated policy will override any manual CSP settings in the Security Headers tab
- Monitor and refine:
- Start in Report-Only mode and monitor your site
- Adjust selections if needed
- Switch to Enforce mode when confident
Notes on Security Headers
Strict-Transport-Securityis automatically skipped on non-HTTPS requests to avoid trapping users on HTTP-only environments.- Use caution when enabling
Content-Security-PolicyorPermissions-Policy; incorrect directives can prevent required scripts or browser features from functioning. - The plugin does not automatically add preload entries for HSTS. Submit your domain to hstspreload.org when you are confident the configuration is correct.
- Tip: Use the CSP Builder tab instead of manually configuring Content-Security-Policy - it's much easier and less error-prone.
Notes on Version Hiding
IMPORTANT: Version hiding is "security through obscurity" - it does NOT fix vulnerabilities!
What it does:
- Slows down automated reconnaissance by hiding version information
- Makes it slightly harder for attackers to identify vulnerable versions
- Removes WordPress version from HTML meta tags and RSS feeds
- Blocks access to
readme.htmlandlicense.txtfiles - Removes
?ver=X.X.Xquery strings from CSS/JS files
What it does NOT do:
- Fix security vulnerabilities in WordPress or plugins
- Prevent determined attackers from fingerprinting your site
- Replace the need for regular security updates
- Protect against zero-day exploits
Best practices:
- Always keep WordPress and plugins updated - This is your primary defense
- Use version hiding as ONE layer in a defense-in-depth strategy
- Combine with other security measures (firewall, login protection, etc.)
- Be aware that removing version query strings may affect browser caching
- Determined attackers can still fingerprint WordPress through file structure, REST API, and other methods
Testing after enabling:
- Check your site's HTML source for
<meta name="generator"> - Visit your RSS feed and verify no version information
- Try accessing
/readme.html(should return 403 Forbidden) - Check browser console for any CSS/JS loading errors
Development
- Source code is contained in
pw-security-helpers.php. - Options are stored in the single
pwsh_optionsoption entry for easy export. - Coding standards follow modern WordPress best practices and sanitization patterns.
Support & Contributions
This plugin is maintained as a helper for personal projects. Bug reports and improvement ideas are welcome; please open an issue or submit a pull request if you have suggestions.
Changelog
Version 2.5.2
- ENHANCEMENT: Improved debugging and visibility for inline code handling
- Added comprehensive console logging during scan (shows inline script/style collection)
- Added debug output in admin UI when WP_DEBUG is enabled (shows database contents)
- Inline code handling section now always visible after scan (even if no inline content detected)
- Better error messages when iframe scanning fails (CORS issues)
- Console shows summary: "Total inline scripts collected: X, Total inline styles collected: X"
- Debug box shows: inline script/style counts, selected methods, and raw data structure
- ENHANCEMENT: Improved user feedback
- Inline handling section shows message if no inline code detected during scan
- Helps users understand if scanning worked or if inline code was missed
- Technical details:
- Console logs show character count for each collected inline script
- Warns if iframe document cannot be accessed (CORS)
- Server logs show processing and hash generation when WP_DEBUG_LOG is enabled
Version 2.5.1
- BUG FIX: Fixed SHA-256 hash generation not working
- Client-side scanner now properly collects inline script/style content (not just detection)
- Changed from boolean detection to content arrays that capture actual code
- Server properly receives inline content from AJAX and generates SHA-256 hashes
- Hash generation now works correctly:
base64_encode(hash('sha256', $content, true)) - Inline method settings (unsafe-inline/sha256/nonce) now save correctly
- Fixed data structure mismatch between client and server
- ENHANCEMENT: Style attribute compatibility handling
- Detects when inline style attributes (
style="...") are present - Automatically includes 'unsafe-inline' for styles when attributes detected (SHA-256 doesn't work for attributes)
- Shows informative notice explaining the limitation
- Detects when inline style attributes (
- Technical details:
extractResourcesFromDocument()now returns{resources, inlineScripts, inlineStyles}scanUrlsSequentially()properly merges inline content arraysajax_save_scan_results()processes inline content and generates hashes- Special marker
__STYLE_ATTRIBUTES_DETECTED__tracks style attribute usage
Version 2.5.0
- MAJOR FEATURE: SHA-256 hash support for inline scripts and styles
- New option to use SHA-256 hashes instead of 'unsafe-inline' for CSP
- More secure than 'unsafe-inline' - only allows specific detected scripts/styles
- No code changes required (unlike nonces)
- Automatically generates SHA-256 hash for each inline script/style during scan
- New radio button UI to choose handling method:
- 'unsafe-inline' (least secure, maximum compatibility)
- SHA-256 hashes (more secure, no code changes needed)
- Nonces (most secure, requires code modification)
- Displays hash table showing all detected inline scripts with preview and hash
- Copy-to-clipboard button for each hash
- Hashes automatically added to CSP when SHA-256 method is selected
- 'unsafe-inline' automatically filtered out when using SHA-256
- Separate method selection for scripts and styles
- Backwards compatible - defaults to 'unsafe-inline' for existing installations
- Technical implementation:
- Server-side scanner now stores full inline script/style content (not just 100 char preview)
- Uses PHP's hash('sha256', $content, true) and base64_encode() for hash generation
- Inline content stored as array with 'preview' and 'hash' keys
- CSP generation updated to add 'sha256-{hash}' entries for each inline script/style
- Form submission saves selected method preferences
Version 2.4.4
- ENHANCEMENT: Improved CSP preview formatting
- CSP preview now displays in readable multi-line format
- Added "Content-Security-Policy:" header label at top
- Each directive on its own indented line
- Fixed escaped quote characters in copied/displayed policy
- Better word wrapping for long URLs (uses word-wrap instead of word-break)
- Makes it easier to read and copy CSP policies
Version 2.4.3
- ENHANCEMENT: Improved security header test accuracy
- Tests now check if header value is configured, not just if checkbox is enabled
- Shows warning "Enabled but no value configured" when header is enabled but missing required value
- Only shows fail status when header has value configured but isn't being sent
- Provides clearer guidance on what action to take for each scenario
- Prevents false failures for headers like Permissions-Policy that require configuration
Version 2.4.2
- ENHANCEMENT: Security Headers tests now show individual results
- Broke down single "Security Headers" test into 5 separate tests
- Each header now shows its own pass/fail/warning status:
- Strict-Transport-Security (HSTS)
- X-Frame-Options
- X-Content-Type-Options
- Referrer-Policy
- Permissions-Policy
- Makes it easier to identify exactly which headers are working
- Each test shows the actual header value when present
Version 2.4.1
- BUG FIX: Fixed Test & Verify tab JavaScript errors
- Corrected data attribute selectors (data-test-id → data-test)
- Fixed section selection logic to use proper data-group attributes
- Updated HTML structure to match JavaScript expectations (test-icon → test-status, test-result → test-message)
- Added missing test-details spans to all test items
- Added CSS styling for test details display
- Tests now run and display results correctly
Version 2.4.0
- NEW FEATURE: Test & Verify Tab
- Added dedicated "Test & Verify" tab to settings page for diagnostic testing
- Run real-time tests to verify security settings are actually working
- Tests all protection categories: Core Protections, Version Hiding, Security Headers, and CSP
- Visual pass/fail/warning indicators with detailed results
- Select which test groups to run (all or selective testing)
- Tests include:
- REST API user enumeration blocking
- Author enumeration protection
- XML-RPC access control
- REST API authentication requirements
- WordPress version hiding effectiveness
- Readme/license file blocking
- Version string removal from assets
- Security headers presence and configuration
- CSP header deployment mode detection
- Server-side testing using actual HTTP requests to verify protections work in real-world scenarios
- Helps administrators confirm their security configuration is effective
- No guesswork - see exactly which protections are active and working
Version 2.1.0
- NEW FEATURE: Hide WordPress Version
- Removes WordPress version from HTML meta generator tags
- Removes version from RSS/Atom feeds
- Blocks access to
/readme.htmland/license.txtfiles (returns 403) - Helps slow down automated reconnaissance
- NEW FEATURE: Hide Plugin/Theme Versions
- Removes version query strings (
?ver=X.X.X) from all CSS/JS files - Applies to both WordPress core and plugin/theme assets
- Warning: May affect browser caching behavior
- Removes version query strings (
- Enhancement: Both features added to Core Protections tab with status indicators
- Important: These features provide "security through obscurity" - NOT real security
- Does not fix vulnerabilities
- Should be used WITH other security measures, not instead of them
- Regular security updates remain critical
Version 2.3.1
- NEW FEATURE: Auto-detection and support for Web Workers
- Added
worker-srcCSP directive support - Automatically includes
'self' blob:for worker-src to allow Web Worker creation - Fixes CSP violations when sites use Web Workers (common in modern WordPress plugins/themes)
- Scanner automatically detects and configures worker-src
- Added informative UI message explaining worker-src and blob: protocol usage
- Resolves errors like: "Creating a worker from 'blob:...' violates CSP directive"
- Added
- Technical details:
- worker-src controls where Web Workers, Service Workers, and Shared Workers can load from
- blob: protocol is standard for creating workers in modern JavaScript
- Falls back to script-src if worker-src is not explicitly set
- Safe and commonly required for WordPress sites with background processing
Version 2.3.0
- MAJOR IMPROVEMENT: Client-side scanning for complete resource detection
- Scanner now loads pages in the browser to catch ALL resources (not just initial HTML)
- Detects JavaScript-loaded resources, AJAX calls, lazy-loaded images, and dynamic content
- Waits 3 seconds after page load to catch late-loading resources
- Scans fully-rendered DOM to see exactly what the browser loaded
- Much more accurate CSP generation for modern WordPress sites with dynamic content
- Each URL is loaded in a hidden iframe, scanned, then removed
- Status updates show progress: "Scanning 1 of 3: https://example.com"
- Technical details:
- Previous server-side scanning only saw initial HTML via wp_remote_get()
- New client-side scanning executes JavaScript and waits for full page load
- Extracts resources from the actual loaded DOM, not just source HTML
- Handles protocol-relative URLs, data: URIs, blob: URLs, and relative paths correctly
Version 2.2.0
- MAJOR IMPROVEMENT: CSP Builder now automatically handles WordPress inline content
- Scanner automatically detects inline scripts and styles
- Automatically adds
'unsafe-inline'to script-src and style-src when inline content is found - Automatically adds
data:to font-src for data URI fonts (common in WordPress themes) - All discovered resources are now auto-selected after scanning for easier setup
- Added informative warning explaining why
'unsafe-inline'is needed for WordPress - CSP policies now work out-of-the-box without manual configuration
- Note: While
'unsafe-inline'reduces CSP security, it's required for most WordPress sites. The CSP still provides significant protection by controlling external resource domains.
Version 2.1.3
- ENHANCEMENT: Improved header sending reliability
- Added
template_redirecthook in addition tosend_headersfor better compatibility with various hosting environments - Added comprehensive debug logging to help troubleshoot header issues
- Logs show which headers are being sent and when
- Check
wp-content/debug.log(when WP_DEBUG_LOG is enabled) to verify headers are being sent
- Added
Version 2.1.2
- BUG FIX: Fixed Content-Security-Policy headers not being sent
- CSP Builder's "Use this generated policy" now works independently without requiring Security Headers tab to be enabled
- Manual CSP in Security Headers tab continues to work as before
- Simplified internal logic to remove redundant checks
- Headers are now properly sent when either CSP Builder or manual CSP is enabled
Version 2.1.1
- SECURITY ENHANCEMENT: CSP Builder now generates more secure policies by default
- Always adds
default-src 'none'as the first directive for explicit allow-listing - Automatically filters out
data:URIs fromframe-src(XSS risk) - Sets
object-src 'none'by default to block legacy plugins - Added security warning in UI when dangerous resources are detected
- Policy generator now follows security best practices for Content Security Policy
- Always adds
Version 2.0.6
- SECURITY FIX: REST API User Enumeration blocking now properly blocks ALL access to
/wp-json/wp/v2/users- Previous version only blocked filtered queries (
?search=,?context=, etc.) - Now blocks the entire users endpoint for unauthenticated users
- Logged-in users can still access individual user data (
/users/123) - This closes a significant information disclosure vulnerability
- Previous version only blocked filtered queries (
Version 2.0.5
- Enhancement: Added "Current Status" display to Core Protections tab
- Shows which protections are ACTIVE (green) or Inactive (gray) in real-time
- Helps verify settings are properly saved and active
Version 2.0.4
- CRITICAL BUG FIX: Fixed root cause of URL scanning failure
- WordPress Settings API sanitize callback was rejecting AJAX updates
- Implemented
_ajax_bypassmarker to allow AJAX operations to update database - All AJAX handlers now properly save data without being blocked by sanitization
- URLs now persist correctly in the database
- Scanning now works as expected
- Note: This was a fundamental architecture issue where WordPress's
update_option()would return false when the sanitize callback returned unchanged data
Version 2.0.3
- Bug Fix: Added protection against sanitize_options being called without active_tab marker
- Enhancement: Added comprehensive server-side logging for debugging URL save/scan issues
- Enhancement: Improved client-side console logging for better troubleshooting
- Enhancement: Better error messages that show debug information in console
Version 2.0.2
- Bug Fix: Fixed URL scanning issue where scanned URLs weren't being properly saved or retrieved
- Enhancement: Improved AJAX handlers to ensure CSP Builder data structure is properly initialized
- Enhancement: Added better error messages and debugging information for scan failures
- Enhancement: Improved UI feedback when adding URLs (shows "Adding..." and "Added!" messages)
- Enhancement: Better error handling with console logging for troubleshooting
Version 2.0.1
- New Feature: CSP (Content Security Policy) Builder
- Visual interface to scan pages and discover resources
- No-code CSP generation with checkbox selection
- Automatic nonce generation for inline scripts and styles
- Report-Only mode for safe testing
- Real-time policy preview and copy-to-clipboard
- Bug Fix: Resolved settings persistence issue where saving one tab could overwrite settings from other tabs
- Enhancement: Added tab-specific form handling to ensure all settings are preserved correctly
Version 1.1.0
- Initial release with core protections and security headers
License
GPL-2.0-or-later. See the plugin header for license details.