I made a custom post loop which shows posts based on attributes in a shortcode. Users can load additional posts by clicking a button which triggers a new query and echo’s html. I validate each value with filter_var() or preg_match().
Is this enough from a security perspective, or should I do additional checks? Sorry if this is an obvious question, I’m a bit out of my depth.
Validation:
– page number: positive int
– types: podcast, event, post and `,`
– tags/categories: words and `-`, `_`, `+`, `,`
– items: int 1-20
“`php
function load_more() {
if ( ! filter_var($_POST[‘page_number’], FILTER_VALIDATE_INT, [‘options’ => [‘min_range’ => 1]]) ) {
echo ‘Invalid page number.’;
exit;
}
if ( ! preg_match(‘#^(podcast|event|post|,)+$#’, $_POST[‘types’]) ) {
echo ‘Invalid types.’;
exit;
}
if ( ! preg_match(‘#^[\w\-_\+\,]*$#’, $_POST[‘tags’]) ) {
echo ‘Invalid tags.’;
exit;
}
if ( ! preg_match(‘#^[\w\-_\+\,]*$#’, $_POST[‘categories’]) ) {
echo ‘Invalid categories.’;
exit;
}
if (! filter_var($_POST[‘items’], FILTER_VALIDATE_INT, [‘options’ => [‘min_range’ => 1 , ‘max_range’ => 20]]) ) {
echo ‘Invalid number of items.’;
exit;
}
// WP_QEURY ETC here
}
add_action(‘wp_ajax_load_more’, ‘load_more’);
add_action(‘wp_ajax_nopriv_load_more’, ‘load_more’);
“`

When using AJAX in WordPress, you should always use a wp_nonce. The official documentation can be found here:
It’s also always recommended to use tha native WordPress functions when possible – it’s PHP but adapted for the context of WordPress infrastructure.
Check this to find out more about data validation, sanitization and escaping:
Where is the `nonce` check?
Always NONCE!
I wrote about securing WordPress AJAX endpoints here: [https://patchstack.com/articles/patchstack-weekly-week-19-secure-ajax-endpoints/])
The advice given here already is a great match, adding a nonce is one thing you are missing.
In my article I mention authorization checks which are not applicable for your endpoint (since no authorization is needed.) But I do share how you can test/attack your own endpoint using a browser or OWASP ZAP.
Hopefully this is helpful, and best of luck on your project!