my alt

Secure WordPress REST API POST Requests Without Authentication Plugins

Discover how to enhance the security of WordPress REST API POST requests using a shared secret key and SHA256 encoding, bypassing the need for authentication plugins.

WordPress REST API provides a flexible and efficient way to interact with your WordPress site. However, securing these APIs is crucial. While there are plugins available for this purpose, in this article, we’ll explore a different approach that uses a shared secret key and the hash_hmac() function with SHA256 encoding. This method not only verifies the sender but also the content of the POST requests, effectively preventing Man-In-The-Middle attacks.

Advantages Over Authentication Plugins

While authentication plugins are a common way to secure WordPress REST API, our approach has several advantages:

  • Less Overhead: Plugins often come with features that you may not need, adding unnecessary overhead to your site. Our approach is lightweight and only includes what you need.
  • Greater Control: With plugins, you’re at the mercy of the plugin developer. If they stop maintaining the plugin or introduce a bug in a new update, it could affect your site. Our approach gives you full control over your API security.
  • Better Performance: Plugins can slow down your site, especially if they’re poorly coded. Our approach has minimal impact on performance.
  • Increased Security: Plugins can be a security risk if they have vulnerabilities. Our approach uses a shared secret key and SHA256 encoding, which is a secure method of verifying the sender and the content of the requests.

Step 1: Registering the REST Route

First, we need to register a new REST route that will handle our POST requests. We’ll use the register_rest_route() function for this purpose. Keep in mind that all code we use in this article is intended to live in a larger PHP Class, thus, when using this in the real world, you should consider using a Class and not copy-pasting the code shared. Here’s an example:

$args = array(
    'methods'             => 'POST',
    'callback'            => array( $this, 'custom_post_route_api' ),
    'permission_callback' => array( $this, 'permission_check' ),
);
register_rest_route( 'custom/v1', '/post-received/', $args );

In this code, we’re registering a new route /post-received/ under the namespace custom/v1. The custom_post_route_api method will handle the POST requests, and the permission_check method will verify the requests before processing them.

Step 2: Verifying the Requests

Next, we’ll implement the permission_check method to verify the requests. This method will retrieve a signature from the request headers and compare it with a computed signature.

public function permission_check( $req ) {
    $signature = str_replace( 'sha256=', '', $req->get_header( 'custom_signature' ) );
    return $this->compute_signature( $req, $signature );
}

The compute_signature method will compute a signature using the shared secret key and the request body. If the computed signature matches the signature from the request headers, the method will return true; otherwise, it will return false.

private function compute_signature( $req, $signature ) {
    if ( false === $signature || empty( $signature ) ) {
        return false;
    }

    $shared_key = get_option('shared_key'); // get shared key from WP Settings.
    $method = $req->get_method();
    $body = $req->get_body(); // get POSTed body.
    $verified_signature = hash_hmac( 'sha256', $body, $shared_key );

    if ( 'POST' === $method && $signature === $verified_signature ) {
        return true;
    } else {
        return false;
    }
}

Step 3: Processing the Requests

Finally, we’ll implement the custom_post_route_api method to process the verified requests. This method will perform the desired action and return a response.

public function custom_post_route_api( WP_REST_Request $req ) {
    $route = 'post-received';
    $response = array(
        'status'  => 'failure',
        'message' => 'Post was not processed.',
    );

    if ( 'post-received' !== $route ) {
        $response = new WP_Error( '401', esc_html__( 'Invalid Request Route', 'pp' ), array( 'status' => 401 ) );
    }

    if ( ! is_wp_error( $response ) ) {
        $body = json_decode( $req->get_body() );
        // Perform your desired action here.
    }

    return rest_ensure_response( $response );
}

Conclusion

This approach provides a secure and efficient way to handle WordPress REST API POST requests without using any authentication plugins. It’s easier because it doesn’t require any additional dependencies, and it’s safer because it verifies both the sender and the content of the requests. However, it’s important to keep your shared secret key secure and update it regularly to maintain the security of your API.