WordPress PHP
`.php` under `wp-content/themes`, `plugins`, or `mu-plugins`
WordPress APIs first; escape output, sanitize input, prepare SQL, verify AJAX/REST.
-
BAD
query_posts( array( 'posts_per_page' => -1 ) );OK
pre_get_posts + $query->set( 'posts_per_page', 10 ) on main query onlyNever query_posts(); unbounded -1 is critical perf risk
-
BAD
$wpdb->query( "SELECT * FROM {$wpdb->posts} WHERE ID = " . $_GET["id"] );OK
$wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE ID = %d", absint( $_GET["id"] ) ) );Raw SQL with request data — SQLi
-
BAD
'posts_per_page' => $_POST['posts_per_page']OK
'posts_per_page' => min( 100, absint( $_POST['posts_per_page'] ?? 10 ) )User-controlled pagination without absint/cap
-
BAD
echo $_POST['title'];OK
echo esc_html( sanitize_text_field( wp_unslash( $_POST['title'] ) ) );Unescaped output — XSS
-
BAD
wp_ajax_* handler without check_ajax_referer() / capability checkOK
check_ajax_referer( 'action', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) wp_die();CSRF / missing auth on AJAX
-
BAD
session_start(); or setcookie() in theme templateOK
Avoid frontend sessions; use WP auth/cookies APIs only when requiredCache bypass / session fixation risk
-
BAD
WP_Query / get_posts inside foreach without static/cacheOK
Single query with post__in or batched meta lookupN+1 queries in loops
https://developer.wordpress.org/themes/advanced-topics/security/
https://developer.wordpress.org/reference/functions/query_posts/
https://developer.wordpress.org/reference/classes/wpdb/prepare/