The WordPress plugin All-In-One Sticky Floating Contact Form was found to have a vulnerability involving authorization bypasses. When you develop plugins for WordPress, it’s important to ensure that the user making a request to a function has the right permissions. Developers for the All-In-One Sticky plugin didn’t validate authorization, so anyone could send a command to the form’s function to make changes to data, specifically all contacts stored in the plugin database table could be deleted.
In WordPress, most site owners use the admin role or the contributor role, Admins have full access, but Contributors can only manage their own content. Contributors also can’t publish anything until it is reviewed by an editor or an admin. For small WordPress sites, it’s common to only have a single administrator. If you are the sole owner of a WordPress site and don’t have contributors, you don’t have to worry about this vulnerability. This vulnerability affects WordPress sites with other users configured on the backend WordPress dashboard.
This article will use the All-In-One Sticky Floating Contact Form vulnerability as an example of how you introduce security bugs into code when you don’t validate that the user has permissions to run a function. The vulnerability was found and reported in CVE-2025-14428. Luckily, WordPress has an internal API function to quickly validate permissions. We’ll show you how to use it and what happens when you don’t.
Vulnerable WordPress Function my_sticky_elements_bulks
All-In-One Sticky Floating Contact Form has a function named my_sticky_elements_bulks that deletes all contacts in the plugin table. The following function code is from the vulnerable version 2.3.3. Any version earlier than 2.3.3 is also vulnerable to an authorization bypass.
Here is the vulnerable function code snippet:
public function my_sticky_elements_bulks(){
global $wpdb;</span> <span style="font-weight: 400;">
check_ajax_referer( 'mystickyelements', 'wpnonce' );
if( isset($_POST['wpnonce']) ){
$bulks = isset($_POST['bulks']) ? $_POST['bulks'] : array();
foreach( $bulks as $key => $bulk ){
$ID = sanitize_text_field($bulk);
$table = $wpdb->prefix . 'mystickyelement_contact_lists';
$ID = self::sanitize_options($ID, "sql");
$delete_sql = $wpdb->prepare("DELETE FROM {$table} WHERE id = %d",$ID);
$delete = $wpdb->query($delete_sql);
}
}
wp_die();
}
To give you a brief explanation of what this code does, it takes no parameters and deletes all contacts from a table named mystickyelement_contact_lists. No authorization checks are in the function, so anyone with a WordPress account on the site could run it.
You might notice that the check_ajax_referer function does some kind of authorization validation. This function checks to make sure that the user exists on the site and isn’t a single call to the ajax function to break data. Essentially, it ensures that a script kiddie on the internet can’t invoke the function. The check_ajax_referer function takes two parameters: the WordPress plugin name and the ‘wpnonce’ string value. Wpnonce stands for “WordPress number used once.” The function returns a value that verifies a valid user is making a call to the Ajax function. This function is necessary as well, but it does not check for authorization. It only validates that it’s a legitimate site call, so it stops attacks like Cross-Site Request Forgery (CSRF).
The rest of the my_sticky_elements_bulks function builds the SQL query to delete all contacts. To sum up the function execution process, it validates that the user is a legitimate site user, builds a SQL DELETE query, and then executes it.
New Function Code That Validates User Authorization
Developers for All-In-One Sticky Floating Contact Form released a patch for the security bug in version 2.3.4. Here is the plugin’s new function code:
public function my_sticky_elements_bulks(){
global $wpdb;
if ( ! current_user_can( 'manage_options' ) ) {
wp_die(0);
}
check_ajax_referer( 'mystickyelements', 'wpnonce' );
if( isset($_POST['wpnonce']) ){
$bulks = isset($_POST['bulks']) ? $_POST['bulks'] : array();
foreach( $bulks as $key => $bulk ){
$ID = sanitize_text_field($bulk);
$table = $wpdb->prefix . 'mystickyelement_contact_lists';
$ID = self::sanitize_options($ID, "sql");
$delete_sql = $wpdb->prepare("DELETE FROM {$table} WHERE id = %d",$ID);
$delete = $wpdb->query($delete_sql);
}
}
wp_die();
}
Notice the function now has a new if statement:
if ( ! current_user_can( 'manage_options' ) ) {
wp_die(0);
}
This current_user_can function checks that the currently authenticated user has the manage_options permission. WordPress has several built-in permissions, and manage_options is one of them. This permission gives a user access to change any setting from the WordPress site dashboard. Out-of-the-box, this permission is only given to the site administrator, so only administrators can now delete all contacts.
Secure Programming Lessons Learned
If you have the All-In-One Sticky Floating Contact Form installed on your WordPres site, you should upgrade to the latest version (2.3.4 or newer). If you’re a developer coding WordPress plugins, make sure you include validation checks for all your functions. Validate that a user is authenticated if it’s a feature that only a site user can execute, and validate permissions if it’s a change in settings or data.
Incorporate current_user_can to validate user permissions, and use check_ajax_referer to validate that a user is valid to execute the function.
