WordPress

Prepared statement

Prepared statement is a query written with placeholders so values are inserted separately from the SQL text. In WordPress, $wpdb->prepare() builds one by escaping each value for its declared type.

How it is measured

The form is $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_status = %s", $author_id, 'publish' ). Placeholders are %d for integers, %s for strings, %f for floats, and %i for identifiers since WordPress 6.2. WordPress does not use MySQL's server-side prepared statements here; it escapes and formats the values itself before sending the query.

To audit code, grep a plugin for $wpdb->query, get_results, and get_var calls that contain a variable directly inside the SQL string. Any such call without prepare() deserves a look.

Worked example

A plugin's search feature runs $wpdb->get_results( "SELECT * FROM wp_leads WHERE email = '$email'" ). A tester enters x' OR '1'='1 and the page returns all 8,400 rows of the leads table.

The fix is one line: prepare( "... WHERE email = %s", $email ). The same test string now matches nothing, because the quote is escaped and the whole string is compared as the email.

How it differs

A prepared statement separates data from the SQL, while wpdb is the object that runs the query. Using $wpdb does not make code safe by itself, because $wpdb->query( $raw ) will run whatever it is given. SQL injection is the attack that prepare() is meant to prevent.

Common errors

Putting quotes around %s in the format string, which doubles them. Building the query with sprintf and then calling prepare on the result. Passing a table name through %s instead of a trusted prefix. Using LIKE with a user string without esc_like. Passing a single array and forgetting the arguments.

In practice

Search your custom code and any plugin you maintain for queries with variables in the SQL string. Wrap each one with prepare and add a test with a quote character in the input. For core data, prefer WP_Query or get_posts, which handle escaping for you.

See also

wpdb, SQL injection

Sources

Count this on a real site.

Watch my website