WordPress Data Validation, Sanitizing and Escaping
Most security problems in plugins and themes come from the same mistake: trusting the data from the user. In WordPress we follow one simple rule: validate or sanitize the input, escape the output. Also check who is allowed to do the action.
| Step | When | Examples |
|---|---|---|
| Validate | Check if the data is correct. Reject it if not | is_email(), is_numeric() |
| Sanitize | Clean the data when it comes in (before saving) | sanitize_text_field(), absint() |
| Escape | Make the data safe when it goes out (printing) | esc_html(), esc_attr(), esc_url() |
Sanitizing the input
echo sanitize_text_field( ' <b>Riya</b> Sharma <script>alert(1)</script> ' ) . "\n";
echo sanitize_email( 'riya@@example.com' ) . "\n";
echo sanitize_title( 'My First Post!' ) . "\n";
echo absint( '-25abc' ) . "\n";
echo sanitize_file_name( 'my photo (1).JPG' ) . "\n";
echo wp_kses_post( '<p>Hello <a href="https://example.com" onclick="steal()">link</a><script>bad()</script></p>' );
Riya Sharma
riya@example.com
my-first-post
25
my-photo-1.JPG
<p>Hello <a href="https://example.com">link</a>bad()</p>
wp_kses_post() keeps the safe HTML tags (like those allowed in a post) and removes the dangerous ones.
Validating
var_dump( is_email( 'riya@example.com' ) );
var_dump( is_email( 'not-an-email' ) );
var_dump( in_array( 'blue', array( 'red', 'green', 'blue' ), true ) );
string(16) "riya@example.com"
bool(false)
bool(true)
Escaping the output
$name = 'Riya "<b>Sharma</b>"';
echo esc_html( $name ) . "\n";
echo '<input value="' . esc_attr( $name ) . '">' . "\n";
echo esc_url( 'javascript:alert(1)' ) . "|\n";
echo esc_url( 'https://example.com/a b?x=1&y=2' ) . "\n"; // spaces and & are encoded
echo wp_kses( '<a href="/x" class="y">Safe</a>', array( 'a' => array( 'href' => true ) ) );
Riya "<b>Sharma</b>"
<input value="Riya "<b>Sharma</b>"">
|
https://example.com/a%20b?x=1&y=2
<a href="/x">Safe</a>
| Function | Use for |
|---|---|
| esc_html() | Text inside HTML tags |
| esc_attr() | Values of HTML attributes |
| esc_url() | Links and image addresses |
| esc_js() | Text inside inline JavaScript |
| wp_kses_post() | HTML content you want to allow in part |
Nonces: protect from forged requests
A nonce (“number used once”) is a short token that proves a form or link was made by your own site, for a given user and action. It protects against CSRF attacks.
wp_set_current_user( 1 );
$nonce = wp_create_nonce( 'delete_book_5' );
echo 'Nonce length: ' . strlen( $nonce ) . "\n";
echo 'Right action: ' . var_export( (bool) wp_verify_nonce( $nonce, 'delete_book_5' ), true ) . "\n";
echo 'Wrong action: ' . var_export( (bool) wp_verify_nonce( $nonce, 'delete_book_6' ), true );
Nonce length: 10
Right action: true
Wrong action: false
In a form use wp_nonce_field( 'action_name', 'field_name' ), and in the handler check it with check_admin_referer() or wp_verify_nonce(). For links, use wp_nonce_url().
Capability checks
A nonce only proves the request is from your site. It does not prove that the user is allowed. Always check the capability too:
wp_set_current_user( 2 ); // user 2 does not exist here, so no capabilities
var_dump( current_user_can( 'manage_options' ) );
wp_set_current_user( 1 ); // the administrator
var_dump( current_user_can( 'manage_options' ) );
bool(false)
bool(true)
A safe form handler
function tc_handle_form() {
// 1. Who is allowed?
if ( ! current_user_can( 'edit_posts' ) ) {
wp_die( 'Not allowed', 403 );
}
// 2. Is the request real?
check_admin_referer( 'tc_save_form', 'tc_nonce' );
// 3. Sanitize the input
$title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '';
$age = isset( $_POST['age'] ) ? absint( $_POST['age'] ) : 0;
// 4. Validate
if ( $title === '' || $age < 18 ) {
wp_die( 'Invalid data', 400 );
}
// ... save the data ...
}
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |