Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Prevent SQL injection in WordPress by avoiding hand-written SQL whenever a native WordPress API can perform the operation. When custom SQL is necessary, normalize input to the type you expect, put every data value in a matching $wpdb->prepare() placeholder, and build identifiers or SQL keywords only from fixed allowlists.

esc_sql() is not a substitute for prepared statements. WordPress also requires version-aware code: the %i identifier placeholder was added in WordPress 6.2.0, while older supported versions require an allowlist for table and column names.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What SQL injection means in a WordPress site

SQL injection occurs when request data is allowed to alter the SQL statement rather than being treated solely as data. A vulnerable plugin, theme, AJAX handler, REST endpoint, or administrative tool may concatenate a query string with values supplied through a URL, form, cookie, or API request. The resulting statement can return unintended rows or modify data, depending on the account and database privileges used by the site.

WordPress does not make custom SQL automatically safe. The $wpdb abstraction gives plugins and themes a consistent database interface, but the developer still has to escape every value correctly and keep untrusted text out of SQL structure. WordPress’s database guidance states: “All data in SQL queries must be SQL-escaped before the SQL query is executed to prevent SQL injection attacks.”

#1 Best Overall
Sale
Compressed Air Duster,130000RPM Super Power Cordless Air Duster,Rechargeable Brushless Blower,3 Gear Adjustable Blower with LED Light,Dust Remover for Computer/House/Outdoor/Car
  • Powerful Turbo Fan:CAGIWIRU Electric Air Duster have reached speed up to 130000RPM,Can efficient remove dust and debris.With LED light and Three Adjustable Speed to meet with different cleaning tasks.
  • With Attached USB C Fasting Charging cable.Charing 3 hours,Enjoy up to 240 minutes of use on the lowest setting, For third speed can use for 25mins.with four charging options to meet with your needs.
  • Wide Application:Upgraded attached 5 different nozzles,making it suitable for a variety of scenes, such as car,pet, pc, keyboards, and other electronic devices. It also serves for office and home clean duster.
  • Handy Design & Portable:Cordless air duster electric nozzle is Light weight and small size, design and comfortable to hold and space saving, convenient to carry around. You can put it in your room or inside the car without taking up space.
  • Warranty & Support: Our Electric air duster comes with 5 years warranty and 24/7 customer service. During this period, if you have any questions,pls kindly feel free to contact us.

The practical rule is simple: let WordPress construct the query when possible; otherwise, prepare it immediately before execution and separate values from the SQL grammar.

1. Use a native WordPress API first

The WordPress Theme Handbook gives the right starting point: “The first rule for hardening your theme against SQL Injection is: when there’s a WordPress function, use it.” Native APIs accept structured arguments and keep query construction inside code that WordPress maintains. They are preferable to reproducing that behavior with a custom SELECT, INSERT, UPDATE, or DELETE statement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Before writing SQL, check whether the operation can be expressed with an established WordPress API, such as a query object or a function for posts, metadata, users, terms, or options. This also reduces the amount of table-specific code that must be reviewed when WordPress changes.

  • Use the native API when it supports the records, filters, ordering, and pagination you need.
  • Use $wpdb only for a genuinely custom query or a table that the native API does not expose.
  • Do not switch to custom SQL merely to avoid learning the API’s argument names.

2. Put every SQL value in a typed placeholder

For custom SQL, call $wpdb->prepare() and pass values as separate arguments. Placeholders must remain unquoted; prepare() applies the appropriate quoting and escaping for the placeholder type.

<?php
global $wpdb;

$user_id = absint( $_GET['user_id'] ?? 0 );
$status  = sanitize_key( $_GET['status'] ?? 'publish' );

$sql = $wpdb->prepare(
    "SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_status = %s",
    $user_id,
    $status
);

$ids = $wpdb->get_col( $sql );

In this example, $wpdb->posts is a WordPress-provided table reference, while the request values are passed through placeholders. The query is prepared before get_col() executes it.

Placeholder Use it for Important constraint
%s String values Pass the value as an argument; do not put quotes around the placeholder.
%d Integer values Normalize or cast the input to an integer before building the query.
%f Floating-point values Use only where a decimal number is actually expected.
%i Identifiers such as table or field names Available in WordPress 6.2.0 and later; it is not interchangeable with %s.
%% A literal percent sign in the SQL text Use this when the percent character is part of the query itself rather than a placeholder.

Never concatenate a request value into a value position and then assume that a later call to prepare() will repair it. The value must be represented by a placeholder from the start.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
EVEO® Screen Cleaner Spray Kit (Streak-Free) TV Screen Cleaner for Smart TV, Lcd, LEd, Oled | Computer Screen Cleaner Spray and Wipe Microfiber Cloth - TV, Monitor, Laptop, iPad, Car - 6.7oz 1 Pack
  • Screen Cleaner is Your Screen’s New Bestfriend! - EVEO is proud to present a screen cleaner that’s perfectly suited for your TV. Meet your new screen cleaner, computer screen cleaner, laptop cleaner, iPad cleaner, Macbook,tv cleaner or any other electronic device. Our screen cleaner spray and wipe along with the included plush microfiber cloth, easily removes dust, fingerprints & other smudges on the screen’s surface. Clean effectively for a streak-free screen
  • Gentle on your Screens - Our screen cleaner spray and wipes is compatible to be used even for the most sensitive LCD, LED, CRT, and OLED screens in proper use. Be at ease knowing that this product is compatible with all the major brands: LG, SONY, Samsung, Sharp, iPad, iPhone, Android screens, Kindle, PC monitors - we’ve got you covered! Can be used as LCD screen cleaner, tv cleaner, smart tv screen cleaner, and more
  • Premium Super Soft Plush Microfiber Cloth - Included in this efficient screen cleaning kit. This screen cleaner spray and wipe has our signature plush microfiber cloth specially designed to comply with screens and monitors and leave them streak-free. Has been shown to effectively absorbs stubborn stains, zero streaks, and no fiber shedding. Always use this together with our screen cleaner for the best results
  • Easy-to-Use Screen Cleaner Kit + microfiber cleaning cloth - The EVEO Screen Cleaning Kit will easily be your new favorite tool when it comes to taking care of the things you love most, quick and easy solution for a streak-free screen. Simply screw on the sprayer gun on the screen cleaner bottle. Spray the cleaning liquid onto the microfiber cloth, then wipe the screen until desired cleanliness & shine is achieved.
  • Quality Screen Care Kit Worth Purchasing - Enjoy a convenient Screen Cleaner anytime, anywhere you need it. Your satisfaction is our top priority. We stand behind the quality of our products and that’s why, you shouldn’t be worrying at all. We manufacture the Best Screen Cleaner. and can be used for laptop cleaning kit, laptop screen cleaner, tv screen cleaner for smart tv. MacBook screen cleaner, monitor cleaner or screen cleaner spray for electronic devices with microfiber cleaning cloth

3. Normalize input before constructing the query

Preparation protects the SQL boundary; input normalization makes sure the value has the shape your application expects. Normalize as soon as the request enters your code, then pass the normalized value to prepare().

  • Use absint() for a non-negative integer such as a post or user ID.
  • Use a key-oriented normalization function such as sanitize_key() for a constrained token such as a status code.
  • Do not rely on a browser control, hidden field, or JavaScript validation to enforce a type.
  • Decide what an empty, missing, or invalid value means before querying: reject it, apply a documented default, or return no results.

Normalization is not a replacement for placeholders. A value that looks numeric or contains only letters still belongs in the matching placeholder.

4. Build a safe LIKE search

LIKE has a second escaping concern: the percent and underscore characters have wildcard meaning in the pattern. Escape the user’s search text with $wpdb->esc_like(), add the complete wildcard string to the argument, and then prepare the query.

<?php
global $wpdb;

$search_term = (string) ( $_GET['q'] ?? '' );
$needle      = '%' . $wpdb->esc_like( $search_term ) . '%';

$sql = $wpdb->prepare(
    "SELECT ID FROM {$wpdb->posts} WHERE post_title LIKE %s",
    $needle
);

$ids = $wpdb->get_col( $sql );

The wildcard characters are part of the argument, not part of the SQL string. Do not write LIKE '%s'; placeholders are intended to stand alone, without surrounding quotes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Handle dynamic table and column names correctly

Values and identifiers are different categories. A post title is a value, but a table name, column name, sort direction, operator, and SQL keyword change the structure of the statement. A value placeholder such as %s cannot safely represent an identifier.

WordPress 6.2.0 and later: use %i

WordPress documents the %i placeholder for identifiers beginning with version 6.2.0. Use it when the site’s supported WordPress range includes that behavior.

Rank #3
BladeHawks RGB Gaming Mouse Pad Extra Large Extended 31.5x12 Inch-Black
  • 【Large size】:Measuring 800 x 300 mm/ 31. 5×12 inches, which is wider and taller than others. Large mouse pad can perfectly fit your keyboard and mouse of any size, and there is also ample space for peripherals such as phones, tablets, etc.
  • 【LED backlight mouse pad】:Blade Hawks RGB mouse pad provides 7 static modes and 7 dynamic modes will give you the options you are looking for. Power-off memory function remembers the last lighting mode you selected. The super glow fiber Chroma will give you a colorful gaming setup that you want.
  • 【Anti-slip rubber base】:Made from Natural rubber, this gaming mousepad will firmly grip your desktop, allowing you to enjoy the ultimate precision in the games you are most passionate about. It also comes with a texture that enhances this ability even further.
  • 【Smooth and waterproof surface】: Micro-textured cloth surface, for extremely precise mouse control and a smooth movement. When liquid splashes on the gaming mouse pad, it forms water droplets and slides down. Will not delay your work or gameplay.
  • 【Plug and play】: This mouse and keyboard pad is powered by USB, plug and play, no driver required. One button control light modes. Press one time to change the lighting mode, press twice to change the brightness, press and hold about 3 seconds turn ON/OFF.
<?php
global $wpdb;

$column = 'post_date';
$sql    = $wpdb->prepare(
    "SELECT ID FROM {$wpdb->posts} ORDER BY %i DESC",
    $column
);

$ids = $wpdb->get_col( $sql );

The identifier still should come from an application-controlled choice or an allowlist. %i provides identifier handling; it does not make an arbitrary request value an acceptable column.

When older WordPress versions must be supported

If your plugin or theme supports a version before 6.2.0, map the user’s choice to a fixed set of known names and interpolate only the mapped result. Never insert the raw request string.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
global $wpdb;

$columns = array(
    'newest' => 'post_date',
    'title'  => 'post_title',
);

$key    = sanitize_key( $_GET['sort'] ?? 'newest' );
$column = $columns[ $key ] ?? $columns['newest'];

// Both values are selected from fixed application code.
$sql = $wpdb->prepare(
    "SELECT ID FROM {$wpdb->posts} ORDER BY {$column} DESC"
);

This older-version pattern is safe only because $column can contain one of the literal names defined by the application. An allowlist is not the same as checking whether a string merely “looks like” a column name.

6. Treat ORDER BY, directions, and other SQL structure as allowlisted code

Sort direction is a SQL keyword, not a value. Do not pass ASC or DESC through %s and do not concatenate the request value. Map a normalized key to a literal keyword, and append that mapped value only after the choice has been constrained.

<?php
global $wpdb;

$columns = array(
    'date'  => 'post_date',
    'title' => 'post_title',
);
$directions = array(
    'asc'  => 'ASC',
    'desc' => 'DESC',
);

$sort_key      = sanitize_key( $_GET['sort'] ?? 'date' );
$direction_key = sanitize_key( $_GET['direction'] ?? 'desc' );
$column        = $columns[ $sort_key ] ?? $columns['date'];
$direction     = $directions[ $direction_key ] ?? $directions['desc'];

$sql = $wpdb->prepare(
    "SELECT ID FROM {$wpdb->posts} ORDER BY %i {$direction}",
    $column
);

On WordPress versions that do not support %i, use the same fixed mappings for both the column and direction, then interpolate those mapped literals. Apply the same rule to table names, operators, grouping fragments, and optional clauses: user input may select from a finite set, but it must not write the SQL grammar.

7. Why esc_sql() is not enough

esc_sql() has a limited purpose. Its reference says that $wpdb->prepare() is generally preferred and warns that esc_sql() does not escape unquoted numeric values, field names, or SQL keywords.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Vaydeer Wrist Rest for Keyboard and Mouse, Computer Ergonomic Wrist Support Pad, Soft Memory Foam Arm Cushion for Desk, Palm Hand Office Laptop Typing
  • 【Softer and More Comfortable】Vaydeer wrist rest has unique diamond pattern, which is the combination of softness and aesthetics. The materials of wrist rest are improved into higher quality memory foam and covered with silky smooth lycra. The computer wrist rest makes you as comfortable and cushiony as like rest your wrists on clouds.
  • 【Ergonomic Wrist Saver】The wrist rests for keyboard and mouse comes with a 17.32×3.15×0.83 inch keyboard wrist pad and a 5.94×3.15×0.83 inch mouse wrist support. Based on ergonomic design, the unique concave shape is the perfect fit for your wrist joints. The wrist rest pad fits most computer keyboards and laptops, improve hand and wrist posture, release your wrist and arm stress.
  • 【Non-Slip Rubber Bottom】Featuring an anti-skid silicone base on the bottom, this wrist keyboard support stays firmly in place on your desk, preventing the padding from sliding around, ensuring stable and consistent wrist support during extended computer sessions.
  • 【Better Experience & Pain Relief】Our keyboard arm rest is beneficial to alleviate the soreness caused by direct contact and friction between your arm and a hard desk surface, reducing the risk of wrist fatigue or carpal tunnel. The soft texture of memory foam can evenly distribute the pressure around your wrists and provide good support with just enough give.
  • 【Helpful in Multiple Scenarios】Whether you're working, studying, writing, typing, gaming, this keyboard and mouse rest combo is an essential accessory to add comfort and support to your hands and wrists. It’s also a great gift for men, women, family, friend, coworker, gamer, teacher, etc.

That limitation matters when developers use one escaping function for every part of a query. A field name or sort direction is not safely handled merely because it passed through esc_sql(). Numeric text that is concatenated without quotes is another gap. Keep value data in typed placeholders, cast or normalize numeric input, and allowlist every identifier or keyword.

If an existing code path uses esc_sql(), review the entire statement rather than replacing one function mechanically. Check where the value is placed, whether it is quoted, whether the SQL fragment is actually an identifier or keyword, and whether the query can be rewritten with prepare().

8. Prepare immediately before execution

WordPress coding standards state: “Escaping should be done as close to the time of the query as possible, preferably by using $wpdb->prepare().” Keep the final SQL construction next to the call that executes it. This makes it easier to verify that every interpolated piece is trusted and that the placeholders still match the arguments.

Avoid storing an unprepared query in a variable, passing it through several helpers, and preparing only part of it later. Such indirection makes concatenation easy to miss during review. A clearer sequence is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Read the request or function arguments.
  2. Normalize values and select allowlisted structural options.
  3. Call $wpdb->prepare() with the complete SQL and all value arguments.
  4. Execute the returned statement immediately with the appropriate $wpdb method.

Review every interpolation in the SQL string. A WordPress table property or a literal fragment selected from a fixed map may be valid; raw request text is not.

9. Version support can change the safe implementation

Declare the WordPress versions your plugin or theme supports and choose the implementation accordingly. WordPress documents an unsafe-query issue affecting version 4.8.2 and earlier, and it documents the %i placeholder as added in 6.2.0.

Best Value
Sale
Under Desk Cable Management Tray No Drill, Metal Mesh Cable Management Under Desk with Clamp Mount, 2 Hole Cable Tray with Wire Management and Cord Organizer for Home Office Standing Desk
  • 【No Drill Mounted】Cable management tray can be assembled in as little as 3 minutes or less-simply. Side clips easily clampe the edge of desk, pass through the holes on the sides, and secure excess cables with baskets. Desk cable management supports inward or outward installation to meet your needs in different scenarios, and provides you with great convenience when collecting and organizing wires.
  • 【Rubber Pad Protects Desk】Wire organizer under desk built-in soft rubber pads on the clamp protect your desk from scratches, so you will not damage your furniture or office.
  • 【Sturdy and Beautiful】Cord management under desk is made of high-quality carbon steel and holds about 15 lbs. The wire basket has 2 holes on each side of the tray to enable your cables to pass freely. Cable baskets are suitable for desk thickness use from 0.4" to 2.4".
  • 【Space Saving & Keep Organized】Desk cable management tray holds all your power cords, outlet strips, USB hubs and computer transformers hidden from your feet, and the data cables will not enter the vacuum cleaner to keep your desk clean and tidy. Desk wire organizer is also suitable for use in the kitchen and outdoors.
  • 【Reduce Safety Risks】Cable tray under desk keeps all plugs away from your kids, no more worry about tripping. Using 2 holes wire tray saves extra space, helps you easily cabling and wire protection. The mesh design is not easy to accumulate dust, but avoids safety accidents caused by messy wires.
Support situation Implementation consequence
WordPress 6.2.0 or newer is guaranteed Use %i for dynamic identifiers, while still restricting choices to known names.
A release before 6.2.0 is in the supported range Use fixed allowlists for table and column names; do not rely on %i.
Version 4.8.2 or earlier may run the code Account for WordPress’s documented unsafe-query issue and set a clear minimum version or compatibility path.
Multiple major versions are supported Test each path and ensure the fallback never interpolates untrusted identifiers.

Do not describe a query as safe without stating the version assumption behind its identifier handling. A pattern that depends on %i needs a minimum-version requirement or a guarded fallback.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

10. Compare the safe and unsafe design choices

Decision Safer approach Risky approach
Data access Use a native WordPress API when it performs the required operation. Re-create core behavior with hand-written SQL without a need.
Value handling Normalize input and pass it to a matching %s, %d, or %f placeholder. Concatenate request text into the SQL string.
Identifiers Use %i on WordPress 6.2.0+ or a fixed allowlist on older versions. Use a request parameter directly as a table or column name.
Sort direction Map a key to the literal ASC or DESC. Insert the supplied direction or pass it as a string value.
Pattern matching Call esc_like(), add wildcards to the argument, then prepare. Put wildcards and untrusted text directly inside a quoted SQL fragment.
Escaping timing Prepare immediately before execution and inspect every interpolation. Escape in an unrelated helper and assume the final query is covered.

11. A code-review checklist for plugins and themes

  1. Search for $wpdb calls and list every custom query.
  2. Confirm that a native WordPress API cannot perform each operation.
  3. Mark every value that originates outside the function: request parameters, cookies, form fields, REST data, and values returned from other integrations.
  4. Verify that each value is normalized to its expected type before query construction.
  5. Verify that every value appears in a matching %s, %d, or %f placeholder.
  6. Check that placeholders are unquoted and that literal percent signs use %%.
  7. For LIKE, confirm that esc_like() runs before prepare() and that the full wildcard pattern is an argument.
  8. Identify every dynamic table or column name. Use %i only where the supported WordPress version provides it; otherwise use a fixed allowlist.
  9. Identify dynamic keywords and operators, especially ORDER BY and sort direction. Replace them with mapped literals.
  10. Look for esc_sql() used as a universal solution. Rework numeric, identifier, and keyword fragments instead of assuming they are escaped.
  11. Check that preparation occurs immediately before the corresponding execution call.
  12. Run the review against the oldest WordPress version in the support range, including the documented 4.8.2-and-earlier unsafe-query concern.

12. Common mistakes and their fixes

Quoting a placeholder

Problem: writing WHERE post_status = '%s'. Fix: write WHERE post_status = %s and pass the status as an argument. WordPress handles the quoting.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Using the wrong placeholder category

Problem: treating a column name as %s, or treating a sort direction as a value. Fix: use %i for identifiers on 6.2.0+, use an allowlist for older versions, and map keywords to literals.

Escaping a numeric value but leaving it unquoted

Problem: concatenating a supposedly numeric request value into SQL after calling esc_sql(). Fix: normalize it and pass it through %d or %f.

Putting the wildcard in the SQL text

Problem: combining request text with '%...%' inside the query. Fix: call esc_like(), construct the complete pattern in PHP, and pass it to %s.

Trusting a sanitized key as a column name

Problem: assuming that a key containing only letters is automatically a valid and safe SQL identifier. Fix: use the key to select a value from a finite map, then use %i or the legacy allowlist pattern.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Preparing too early

Problem: preparing a partial query and appending request-controlled fragments later. Fix: finish normalization and allowlist selection first, then prepare the complete statement immediately before execution.

13. A practical implementation sequence

For a new custom query, use this sequence as a repeatable development pattern:

  1. Write down the exact records and filters the feature needs.
  2. Check native WordPress APIs before choosing SQL.
  3. Define the accepted types and defaults for every input.
  4. Define finite maps for every selectable identifier, operator, or keyword.
  5. Normalize request values and reject or default invalid choices.
  6. Write the SQL with unquoted typed placeholders for all values.
  7. Escape LIKE text with esc_like() before preparing.
  8. Use %i only when the minimum supported WordPress version is 6.2.0 or later; otherwise interpolate only names from a fixed allowlist.
  9. Call $wpdb->prepare() immediately before the matching execution method.
  10. Review the final query for every concatenation and test the oldest supported WordPress version.

This sequence keeps the SQL structure under application control while allowing legitimate user-selected filters and sorting.

14. What to retain

  • WordPress APIs are safer than custom SQL when they cover the operation.
  • $wpdb->prepare() parameterizes values and applies the matching type placeholder.
  • %i is for identifiers, not ordinary string values.
  • esc_sql() has a limited scope and does not secure unquoted numbers, field names, or SQL keywords.
  • Dynamic SQL structure must come from a fixed allowlist.
  • WordPress version support matters: the documented older unsafe-query issue and the 6.2.0 arrival of %i affect implementation choices.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.