Web Development

How to Prevent SQL Injection in WordPress

SQL Injection is the #1 way hackers breach WordPress databases. Learn how to sanitize inputs and use prepared statements to lock them out.

Imagine a hacker entering a password that isn't a password, but a command that deletes your entire database. That is SQL Injection (SQLi).

Despite being one of the oldest web vulnerabilities, it remains the most dangerous. In WordPress, it usually happens when a developer unknowingly passes unsafe user data directly into a database query.

Here is how to code responsibly and prevent SQLi.


🛑 The Mistake: Direct Variable Insertion

The most common error is inserting a variable directly into a SQL string.

DO NOT DO THIS:

// ❌ UNSAFE
$id = $_GET['id'];
$results = $wpdb->get_results( "SELECT * FROM $wpdb->posts WHERE ID = $id" );

If the user sets ?id=1 OR 1=1, the query becomes SELECT * FROM wp_posts WHERE ID = 1 OR 1=1. Since 1=1 is always true, the database returns every single post, potentially exposing private data.


✅ The Fix: Prepare Your Queries

WordPress provides a built-in method called $wpdb->prepare(). It functions like printf() in C or sprintf() in PHP. It separates the SQL query from the data, ensuring the data is treated as text, not executable code.

DO THIS:

// ✅ SAFE
$id = $_GET['id'];
$results = $wpdb->get_results( 
    $wpdb->prepare( 
        "SELECT * FROM $wpdb->posts WHERE ID = %d", 
        $id 
    ) 
);

Placeholders:

  • %d = Integer (numbers)
  • %s = String (text)
  • %f = Float (decimals)

🛑 Mistake: Trusting $_POST or $_GET data

Never assume input is safe. Even if it's from a dropdown menu, a hacker can intercept the request and change the value.

Sanitize First, Validate Second

WordPress has helper functions to clean data before you even use it.

  • sanitize_text_field(): Removes HTML tags and checks string safety.
  • sanitize_email(): Ensures it's a valid email format.
  • absint(): Forces a value to be a non-negative integer.

Example:

$user_email = sanitize_email( $_POST['email'] );
$age = absint( $_POST['age'] );

4. The LIKE Statement Trap

This is a specific vulnerability that $wpdb->prepare() alone doesn't fix.

If you are searching for a name:

$name = 'admin'; // User input
$wpdb->prepare( "SELECT * FROM wp_users WHERE user_login LIKE %s", '%' . $name . '%' );

If the user enters %, the query becomes LIKE '%%%', returning ALL users.

The Fix: Use $wpdb->esc_like() before preparing.

$name = $wpdb->esc_like( $user_input ); // Escapes % and _
$query = $wpdb->prepare( "SELECT * FROM wp_users WHERE user_login LIKE %s", '%' . $name . '%' );

5. Cast Your Variables (The Ultimate Safety Net)

If you know an input must be a number, force it to be one.

$id = (int) $_GET['id'];
$query = "SELECT * FROM wp_posts WHERE ID = $id";

Because $id is mathematically guaranteed to be an integer now, SQL injection is impossible, even without prepare() (though you should still use prepare for consistency!).


6. Don't Hardcode Table Prefixes

You might be using wp_posts now, but what if you migrate the site and the prefix changes to db123_posts for security?

Bad: SELECT * FROM wp_posts

Good: SELECT * FROM {$wpdb->prefix}posts

This makes your code portable and compatible with all WordPress installs.


7. When prepare() isn't enough: esc_sql()

Sometimes you have a dynamic order query that prepare() can't handle because you can't put column names in placeholders.

Scenario: Ordering by a user-chosen column.

$orderby = $_GET['sort']; // Could be 'post_title' OR '1=1; DROP TABLE wp_users'
$safe_col = esc_sql( $orderby );
$query = "SELECT * FROM {$wpdb->posts} ORDER BY $safe_col";

Note: For ORDER BY, it's even safer to whitelist allowed columns using in_array().


Summary

Security is a mindset, not a feature.

  1. Never trust user input.
  2. Always use $wpdb->prepare().
  3. Sanitize all incoming data.

By following these three rules, you eliminate 99% of SQL injection risks in your custom WordPress development.

Written by

Gokila Manickam

Senior WebCoder

Builds WordPress and front-end work at FUEiNT. Writes most of the groundwork on this blog — URLs, markup, page speed and the questions clients ask before a build starts.

All 67 articles by Gokila Manickam
Next stepWeb Development

Want this done on your own site?

Tell us what you are trying to do in two sentences. You will get a straight answer, and if it is not work for us we will say so.

Back to the blog
ServiceWordPress developmentWe build, fix and maintain WordPress sites — themes, plugins and the parts other people gave up on.Open

Bring us the one everyone called impossible.

Messy, undocumented, half-migrated and business-critical is our favourite kind of brief. Write two sentences. You will get a straight answer and a way forward.

WhatsAppMessage us on WhatsApp
Visit12, Sri Vigneshwara Nagar, Amman Kovil
Saravanampatti, Coimbatore, TN, India — 641035

தெய்வத்தான் ஆகா தெனினும் முயற்சிதன்மெய்வருத்தக் கூலி தரும்.