16 May 2025

WordPress Plugin Security Review: FV Gravatar Cache

Before we start using a new WordPress plugin on our website, we do a security review of it, which led to us doing one for FV Gravatar Cache.

If you want a security review of plugins you use, when you become a paying customer of our service, you can start suggesting and voting on plugins to get security reviews from us. For those already using the service that haven’t already suggested and voted for plugins to receive a review, you can start doing that here. You can use our tool for doing limited automated security checks of plugins to see if plugins you are using have possible issues that would make them good candidates to get a review. You can also order a review of a plugin separately from our main service.

The review was done on version 0.4.8 of FV Gravatar Cache. We checked for the following issues during it as part of our standard review:

  • Insecure file upload handling (this is the cause of the most exploited type of vulnerability, arbitrary file upload)
  • Deserialization of untrusted data
  • Security issues with functions accessible through WordPress’ AJAX functionality (those have and continued to be a common source of disclosed vulnerabilities)
  • Security issues with functions accessible through WordPress’ REST API (those have started to be a source of disclosed vulnerabilities)
  • Persistent cross-site scripting (XSS) vulnerabilities in the frontend portions of the plugin and in the admin portions accessible to users with the Author role or below
  • Cross-site request forgery (CSRF) vulnerabilities in the admin portion of the plugin
  • SQL injection vulnerabilities (the code that handles requests to the database)
  • Reflected cross-site scripting (XSS) vulnerabilities
  • Security issues with functions accessible through any of the plugin’s shortcodes
  • Security issues with functions accessible through any of the plugin’s blocks
  • Security issues with functions accessible through the admin_action action
  • Security issues with functions accessible through the admin_init action
  • Security issues with functions accessible through the admin_post action
  • Security issues with import/export functionality
  • Security issues with usage of the is_admin() function
  • Security issues with usage of the add_option(), delete_option(), and update_option() functions
  • Security issues with usage of the update_user_meta() and wp_update_user() functions
  • Security with usage of determine_current_user filter
  • Security issues with usage of the wp_set_current_user(), wp_set_auth_cookie() and wc_set_customer_auth_cookie() functions
  • Security issues with usage of the reset_password() and wp_set_password() functions
  • Security issues with usage of the extract() function
  • Lack of IP address validation
  • Proper usage of sanitize_callback when using register_setting() to register settings
  • Existence of register_uninstall_hook or uninstall.php file that removes any WordPress options and database tables added by the plugin
  • CSV injection
  • Host header injection vulnerabilities
  • Lack of protection against unintended direct access of PHP files
  • Insecure and unwarranted requests to third-party websites
  • Any additional possible issues identified by our Plugin Security Checker

Results

We found one relatively serious vulnerability and several minor ones. We also found places where security could be improved.

We notified the developer of the results on April 14 and offered free help to address the issues. We received a response two days later that they were going to address the issues, though there still seemed to be some confusion on their part about a fundamental handling of security of WordPress plugins. On April 29, they informed us that a new version was available on the GitHub project for the plugin. There were a couple of issues that hadn’t been properly addressed, which we notified them of.

Since then nothing more has happened and the new version hasn’t been submitted to the WordPress Plugin Directory. It has been a month without a fix being released, so in line with our disclosure policy, we are releasing the results.

Authenticated Information Disclosure Vulnerability Leading to Email Address Disclosure

The relatively serious vulnerability allows anyone logged in to WordPress to see the email addresses of everyone that had commented on the website. That occurred through the function load_gravatar_list() in the plugin’s only .php file, which is AJAX accessible to anyone logged in to WordPress:

43
add_action( 'wp_ajax_load_gravatar_list', array( $this, 'load_gravatar_list' ) );

The code didn’t include any security check to limit access before providing that information:

870
871
872
  function load_gravatar_list() {
    global $wpdb;
    $options = get_option('fv_gravatar_cache');

That was improperly fixed by adding a nonce check to the beginning of the function:

909
910
911
912
  function load_gravatar_list() {
    if ( ! wp_verify_nonce( $_POST['nonce'], 'fv_gravatar_cache_load_list' ) ) {
      echo 'Invalid nonce';
      exit;

A nonce check is used to prevent cross-site request forgery (CSRF), which isn’t relevant to what happens with the code that runs after that. There should be a capability check using current_user_can() to limit who can access that. While a nonce check normally provides the equivalent of a capability check, that isn’t always the case, and the WordPress documentation warns against using it that way:

Nonces should never be relied on for authentication, authorization, or access control. Protect your functions using current_user_can(), and always assume nonces can be compromised.

Cross-Site Request Forgery (CSRF)

The plugin registers the function OptionsHead() to run during admin_init, which makes it accessible to even those not logged in to WordPress:

22
add_action( 'admin_init', array( &$this, 'OptionsHead' ) );

The functionality of that is only meant to be accessible by those logged in to WordPress with the manage_options capability, but there was no capability check before the function’s code ran:

607
608
609
610
  function OptionsHead() {
      if(stripos($_SERVER['REQUEST_URI'],'/options-general.php?page=fv-gravatar-cache')!==FALSE) {
          $options = get_option('fv_gravatar_cache');
          if(isset($_POST['fv_gravatar_cache_save'])) {

That was corrected:

637
638
639
  function OptionsHead() {
      if(stripos($_SERVER['REQUEST_URI'],'/options-general.php?page=fv-gravatar-cache')!==FALSE) {
        if ( current_user_can( 'manage_options' ) ) {

Most of the code in that had a nonce check that would limit an attacker’s ability to access the functionality, but one piece, which deleted a WordPress option, lacked that:

640
641
642
          if( isset( $_GET['dismiss_directory_change_notice'] ) ) {
            delete_option( 'fv_gravatar_cache_directory_changed' );
          }

That was corrected by adding a nonce check, which is intended to prevent CSRF:

672
673
674
          if( isset( $_GET['fv_gravatar_cache_dismiss_directory_change_notice'] ) ) {
            if ( wp_verify_nonce( $_GET['fv_gravatar_cache_dismiss_directory_change_notice'], 'fv_gravatar_cache_dismiss_directory_change_notice2' ) ) {
              delete_option( 'fv_gravatar_cache_directory_changed' );

Authenticated Setting Change Vulnerability

The plugin’s settings page was accessible to users with the edit_pages capability, which made it accessible to users with the Editor role in addition to Administrators:

818
add_options_page('FV Gravatar Cache', 'FV Gravatar Cache', 'edit_pages', 'fv-gravatar-cache', array( &$this, 'OptionsManage' ) );

The appropriate capability to check for with settings pages is manage_options, so only Administrators have access. That has been corrected:

857
add_options_page('FV Gravatar Cache', 'FV Gravatar Cache', 'manage_options', 'fv-gravatar-cache', array( &$this, 'OptionsManage' ) );

Before that change, combined with the lack of capability check mentioned in the previous section, Editors could change the plugin’s settings.

Weak Gravatar Hashing

The plugin was using MD5 hashing for the commenter’s email address. Gravatar now supports the better for privacy SHA-256. WordPress changed to that in WordPress 6.8. The plugin was updated to supported that as well.

maybe_unserialize()

The plugin is using the maybe_unserialize() function, which is known to be insecure and a Core Committer of WordPress said plugins shouldn’t be using it.

That wasn’t addressed.

Lack of Proper Uninstallation

The plugin doesn’t implement one of WordPress’ methods for properly handling uninstalling the plugin.

The new version added an uninstallation function, but that runs when the plugin is deactivated instead of when the plugin is uninstalled and requires a setting to be set to run.

Lack of Protection Against Direct Access to PHP Files

The plugin’s .php file did not have code at the beginning of the file to restrict direct access to it. We didn’t see anything that could be exploited in the file without the restriction in place, but adding the code would make things more secure. That was addressed by adding this code:

11
12
13
if ( ! defined( 'ABSPATH' ) ) {
  exit;
}

Leave a Reply

Your email address will not be published.