
WordPress SQL Injection: Protect and Clean Your Site
SQL injections remain one of the most dangerous threats to WordPress sites. By exploiting flaws in data validation, an attacker can read, modify, or delete your entire database. In 2026, SQLi attacks still rank in the OWASP top 3 web vulnerabilities. This guide explains how to detect, clean up, and prevent SQL injections on your WordPress site.
How to protect WordPress from SQL injection (8 etapes)
- 1
Scan for SQLi vulnerabilities — Use WPScan or Sucuri to detect exploitable injection points.
- 2
Audit plugin and theme code — Check that all database queries use prepared statements.
- 3
Sanitize all user inputs — Apply WordPress sanitization functions to every form field.
- 4
Use $wpdb prepared statements — Replace raw SQL queries with $wpdb->prepare() in custom code.
- 5
Deploy a web application firewall — Install a WAF like Sucuri or Cloudflare to block SQLi patterns.
- 6
Restrict database user privileges — Limit the WordPress DB user to only required permissions.
- 7
Clean compromised database tables — Remove injected content and rogue accounts from the database.
- 8
Monitor and log database queries — Enable query logging to detect suspicious activity early.
What Is SQL Injection
A SQL injection (SQLi) is an attack technique that involves inserting malicious SQL code into a query sent to the database. When a web application does not properly validate user input, the attacker can manipulate queries to access sensitive data.
How SQL Injection Works
Consider a concrete example. A WordPress search form executes this query:
SELECT * FROM wp_posts WHERE post_title LIKE '%search_term%';If the search field is not secured, an attacker can enter:
' OR '1'='1' --
The query then becomes:
SELECT * FROM wp_posts WHERE post_title LIKE '%' OR '1'='1' --%';This query returns all posts in the database because the condition '1'='1' is always true. The double dash -- comments out the rest of the query, canceling any additional restrictions.
Three Types of SQL Injections
Attackers use different variants depending on the context:
| Type | Description | Risk |
|---|---|---|
| Classic SQLi | The attacker sees results directly in the page | Massive data extraction |
| Blind SQLi | No visible output, the attacker infers information through true/false responses | Slower but equally dangerous |
| Union-based SQLi | Uses UNION SELECT to combine results with other tables | Access to unintended tables (wp_users, wp_options) |
There are also time-based blind SQLi attacks where the attacker uses functions like SLEEP() to infer information by measuring server response time.
How WordPress Is Targeted by SQL Injections
WordPress uses MySQL/MariaDB as its database and interacts with it through thousands of daily queries. Several attack vectors exist.
Unsecured Forms
Contact forms, search bars, and comment forms are prime targets. A plugin that builds its SQL queries by directly concatenating user input without sanitization is vulnerable:
// VULNERABLE CODE - Never do this
$query = "SELECT * FROM wp_users WHERE user_login = '" . $_POST['username'] . "'";
$results = $wpdb->query($query);This type of code allows an attacker to inject any SQL command through the username field.
URL Parameters
GET parameters in URLs are another common vector:
https://example.com/?id=1 UNION SELECT user_login,user_pass FROM wp_users--
Plugins that use URL parameters to filter content (galleries, portfolios, e-commerce) are particularly exposed.
WordPress REST API
Since WordPress 4.7, the REST API exposes endpoints that interact with the database. Vulnerabilities in plugins using the REST API have enabled massive SQL injections. In 2025, several popular plugins (over 100,000 installations) were affected by SQLi flaws through their REST endpoints.
XML-RPC and AJAX
XML-RPC requests (xmlrpc.php) and AJAX handlers (admin-ajax.php) are also injection vectors. Plugins that expose AJAX actions without proper nonce verification or data validation are vulnerable.
Detecting SQL Injection on WordPress
Early detection is crucial to limit damage. Here are the telltale signs and diagnostic methods.
Check Error Logs
Examine your PHP and MySQL error logs to spot abnormal queries:
# Search for SQLi attempts in Apache/Nginx logs
grep -i "union\|select\|concat\|information_schema" /var/log/apache2/access.log
# Search for SQL errors in PHP logs
grep -i "mysql\|sql syntax\|query" /var/log/php_errors.logSQL error messages visible on the front-end are a critical sign: they indicate that your site exposes information about its database structure.
Search for Suspicious Database Entries
Connect to phpMyAdmin or use WP-CLI to inspect critical tables:
-- Search for suspicious code in wp_options
SELECT option_name, option_value
FROM wp_options
WHERE option_value LIKE '%eval(%'
OR option_value LIKE '%base64_decode%'
OR option_value LIKE '%<script%'
OR option_value LIKE '%<?php%';
-- Check for suspicious transient options
SELECT option_name, LENGTH(option_value) as size
FROM wp_options
WHERE option_name LIKE '_transient_%'
AND LENGTH(option_value) > 10000
ORDER BY size DESC;Check for Unauthorized User Accounts
Attackers often create administrator accounts via SQL injection:
-- List all administrators
SELECT u.user_login, u.user_email, u.user_registered
FROM wp_users u
INNER JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND m.meta_value LIKE '%administrator%'
ORDER BY u.user_registered DESC;Any administrator account you do not recognize is a compromise indicator, one of the most common signs of a hacked WordPress site. Pay special attention to recently created accounts.
Use Scanning Tools
Several tools can detect SQLi vulnerabilities on your site:
- WPScan: WordPress vulnerability scanner (command-line)
- sqlmap: automated SQLi detection and exploitation tool (testing only)
- Wordfence / Sucuri: security plugins with real-time detection
- Acunetix / Burp Suite: professional web security scanners
# Scan vulnerabilities with WPScan
wpscan --url https://your-site.com --enumerate vp,vt
# Test a specific URL with sqlmap (only on your own sites)
sqlmap -u "https://your-site.com/?id=1" --batch --level=3Cleaning a WordPress Database After SQL Injection
If your site has been compromised by a SQL injection, follow these cleanup steps methodically.
Step 1: Back Up Before Any Intervention
Back up your entire database before making any changes:
# Full database export
wp db export backup-before-cleanup-$(date +%Y%m%d).sql
# Or via mysqldump
mysqldump -u username -p database_name > backup-before-cleanup.sqlKeep this backup in a safe location. It can serve as a reference if the cleanup causes issues.
Step 2: Clean the wp_options Table
The wp_options table is a prime target because it stores site configuration:
-- Identify infected options first (safer approach)
SELECT option_id, option_name, LEFT(option_value, 200) as preview
FROM wp_options
WHERE option_value LIKE '%eval(%'
OR option_value LIKE '%base64_decode%'
OR option_value LIKE '%<script%'
OR option_value LIKE '%document.write%';
-- Then delete by specific ID
DELETE FROM wp_options WHERE option_id IN (123, 456, 789);Step 3: Clean the wp_posts Table
Injections in wp_posts directly affect the visible content of your site:
-- Detect posts containing injected code
SELECT ID, post_title, post_type, post_status,
LEFT(post_content, 100) as preview
FROM wp_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%eval(%'
OR post_content LIKE '%base64_decode%'
OR post_content LIKE '%<iframe src=%'
OR post_content LIKE '%onload=%'
OR post_content LIKE '%document.cookie%';
-- Remove injected code from posts (example with a specific pattern)
UPDATE wp_posts
SET post_content = REPLACE(post_content, '<script src="https://malware-site.com/script.js"></script>', '')
WHERE post_content LIKE '%malware-site.com%';
-- Clean post metadata
SELECT post_id, meta_key, LEFT(meta_value, 200) as preview
FROM wp_postmeta
WHERE meta_value LIKE '%eval(%'
OR meta_value LIKE '%base64_decode%'
OR meta_value LIKE '%<script%';Step 4: Clean wp_users and wp_usermeta Tables
Remove user accounts created by the attacker:
-- Identify suspicious accounts
SELECT u.ID, u.user_login, u.user_email, u.user_registered,
m.meta_value as role
FROM wp_users u
INNER JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND (
u.user_email LIKE '%temp%'
OR u.user_email LIKE '%test%'
OR u.user_login LIKE '%admin%'
OR u.user_registered > '2026-01-01'
)
ORDER BY u.user_registered DESC;
-- Delete a suspicious account and its metadata
DELETE FROM wp_usermeta WHERE user_id = SUSPECT_ID;
DELETE FROM wp_users WHERE ID = SUSPECT_ID;Step 5: Check for Stored Procedures and Triggers
An advanced attacker may create triggers or stored procedures in MySQL:
-- List all triggers in the database
SHOW TRIGGERS;
-- List stored procedures
SHOW PROCEDURE STATUS WHERE Db = 'your_database_name';
-- List scheduled events
SHOW EVENTS;Delete anything you did not create yourself.
Step 6: Reset All Passwords
After cleanup, change all passwords:
# Change admin password via WP-CLI
wp user update admin --user_pass="NewComplexPassword123!"
# Regenerate WordPress salt keys
wp config shuffle-salts
# Change the database password
# (then update wp-config.php accordingly)For a complete cleanup guide covering files as well, check our WordPress cleanup guide in 10 steps.
Preventing SQL Injections on WordPress
Prevention is always more effective and less costly than cleanup. Here are the essential measures.
Use Prepared Statements
The most effective method against SQL injections is using prepared statements via the WordPress $wpdb class:
// SECURE METHOD with $wpdb->prepare()
global $wpdb;
// Query with an integer
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d",
$post_id
)
);
// Query with a string
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->users} WHERE user_login = %s",
$username
)
);
// Query with multiple parameters
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s LIMIT %d",
$post_type,
$status,
$limit
)
);The placeholders %s (string), %d (integer), and %f (float) ensure values are properly escaped before being inserted into the query.
Validate and Sanitize Input
WordPress provides native validation functions:
// Sanitize text input
$clean_input = sanitize_text_field($_POST['input']);
// Sanitize emails
$clean_email = sanitize_email($_POST['email']);
// Sanitize URLs
$clean_url = esc_url($_POST['url']);
// Validate integers
$clean_id = absint($_GET['id']);
// Escape output data
echo esc_html($variable);
echo esc_attr($attribute);Apply the defense in depth principle: validate input on both the client AND server side.
Configure a WAF (Web Application Firewall)
An application firewall filters malicious requests before they reach WordPress:
| WAF Solution | Type | Price | SQLi Protection |
|---|---|---|---|
| Cloudflare | Cloud | Free to 200 EUR/month | OWASP rules included |
| Sucuri | Cloud | 199 EUR/year | Advanced SQLi protection |
| Wordfence | Plugin | Free to 119 EUR/year | Firewall with SQLi rules |
| ModSecurity | Server | Free (open source) | Configurable OWASP rules |
For optimal protection, combine a cloud WAF (Cloudflare) with a security plugin (Wordfence).
Apply the Principle of Least Privilege
Configure database permissions restrictively:
-- Create a MySQL user with limited privileges
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'complex_password';
-- Grant only necessary privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON wordpress_db.* TO 'wp_user'@'localhost';
-- DO NOT grant FILE, PROCESS, SUPER, CREATE ROUTINE
-- These privileges enable advanced attacks
FLUSH PRIVILEGES;The WordPress user should never have FILE, PROCESS, SUPER, or GRANT OPTION privileges in production.
Disable SQL Error Display
SQL error messages reveal your database structure to attackers:
// In wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', true); // Log errors to a file
define('WP_DEBUG_DISPLAY', false); // Do not display errorsErrors are recorded in wp-content/debug.log without being visible to visitors.
Configure Security Headers
Add HTTP headers to strengthen overall security:
# In .htaccess
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
Header set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>Keep WordPress and Extensions Updated
Security updates fix known vulnerabilities. In 2026, statistics show that:
- 94% of compromised WordPress sites were running outdated plugins
- The average time between SQLi flaw discovery and exploitation is 72 hours
- The most targeted plugins are those with over 50,000 active installations
A WordPress maintenance contract automates updates and monitors for vulnerabilities continuously. Enable automatic updates for security patches:
// In wp-config.php - enable automatic minor updates
define('WP_AUTO_UPDATE_CORE', 'minor');
// Or via a filter for plugins
add_filter('auto_update_plugin', '__return_true');Continuously Monitor SQL Injection Attempts
Active monitoring lets you detect attacks before they succeed.
Set Up Query Monitoring
Configure logging for slow and suspicious queries:
-- Enable slow query log (in my.cnf)
-- slow_query_log = 1
-- slow_query_log_file = /var/log/mysql/slow.log
-- long_query_time = 2
-- Check current queries
SHOW PROCESSLIST;Configure Automated Alerts
Use monitoring tools to receive real-time alerts:
- Wordfence: email alerts on blocked injection attempts
- Fail2Ban: automatic banning of IPs attempting SQL injections
- OSSEC: server-level intrusion detection
# Example Fail2Ban rule for SQL injections
# In /etc/fail2ban/filter.d/wordpress-sqli.conf
[Definition]
failregex = ^<HOST> .*(union.*select|information_schema|concat\(|benchmark\().*$
ignoreregex =Conduct Regular Security Audits
Schedule regular security audits:
- Weekly: review security logs and user accounts
- Monthly: full scan with WPScan and plugin verification
- Quarterly: penetration testing including SQL injection tests
To learn more about the types of malware that can result from a successful SQL injection, check our guide to common WordPress malware in 2026. If your site is already compromised, our guide on infected WordPress files will help you identify modified files.
Conclusion: SQL Security Is an Ongoing Process
SQL injections on WordPress are a serious but preventable threat. By combining prepared statements, input validation, WAF, and continuous monitoring, you significantly reduce your attack surface.
Key takeaways:
- Always use
$wpdb->prepare()for SQL queries in your WordPress development - Deploy a WAF (Cloudflare + Wordfence) to block attempts before they reach your site
- Monitor your logs and set up alerts on injection patterns
- Keep everything updated: WordPress core, plugins, themes, and PHP
- Limit privileges for the WordPress MySQL user
If your site has already been targeted by a SQL injection and you need professional intervention, our team specializes in WordPress malware cleanup and can restore your site securely.
