Bypass password protected posts via GET variable

I like to bypass the password form of protected posts with a GET variable with following requirements:

  • No changes to the core
  • No clear exposure of the password (hashing)
  • No security leaks

No changes to the core

This is a no brainer as the functionality should be in a plugin and working out of the box. I found some methods which changes the core file.

No clear exposure of the password

Instead of http://example.com/post?pwd=mypassword the URL should be something like http://example.com/post?pwd=%25P%25BaxfPUloy8YdOFi2F9F2xAcCg8OLGP

No possible security leaks

Of course this should not get “hacked” somehow. They are only protected posts but nevertheless this is a no go.

What I need:

A way to get the hash appended to the URL:

$url = add_query_arg(array(
    'pwd' => get_my_hash();
), get_permalink($post_id));

and on the page template the code:

if(post_password_required()){
    if(isset($_GET['pwd'])){
        if(check_pwd_with_hash($_GET['pwd'])){
        //unlock content
        }
    }
}

the_content();

There’s no way to hook into the post_password_required() method or simple set the global $post->post_password to an empty string.

1
1

The check for a post password happens (for some reason) inside get_the_content(), which seems legit, but actually mixes together two separate concerns. You were right with the statement that there is no way to work around get_the_content() displaying the password form. Here’s what the check for the password does in core in detail:

if ( empty( $post->post_password ) )
     return false;

if ( ! isset( $_COOKIE['wp-postpass_' . COOKIEHASH] ) )
     return true;

require_once ABSPATH . WPINC . '/class-phpass.php';
$hasher = new PasswordHash( 8, true );
$hash = wp_unslash( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] );
if ( 0 !== strpos( $hash, '$P$B' ) )
     return true;
return ! $hasher->CheckPassword( $post->post_password, $hash );

As you can see, the password gets check against a Cookie stored at the users machine.

To circumvent the post password, you will actually have to give the user explicit permission by setting this cookie. Core does it the following way:

setcookie(
    'wp-postpass_' . COOKIEHASH,
    $hasher->HashPassword( wp_unslash( $_POST['post_password'] ) ),
    $expire,
    COOKIEPATH
);

when you look at the_post_password(), which is used to retrieve the (plain text password), then you will find that it does nothing aside from escaping the argument for the users safety:

esc_attr( $post->post_password )

Now you can combine this and set the Cookie before you display the content in your template:

// Your custom check for the `$_GET` content
// …also check if there in fact is a password
// …and if the user is a repeated visitor, do not set the Cookie again
if (
        isset( $_GET['circumvent'] ) 
        and 'disable-pass' === $_GET['circumvent'] 
        and isset( $post->post_password )
        and ! isset( 'wp-postpass_'.COOKIEHASH )
    ) {
    // Finally we use the plain text password to set the Cookie
    // as if the user would have entered it into the password form
    setcookie(
        'wp-postpass_'.COOKIEHASH,
        $hasher->HashPassword( wp_unslash( esc_attr( $post->post_password ) ) ),
        $expire,
        COOKIEPATH
    );
}
// Now display the content:
the_content();

Additional information about Post Passwords in this article I wrote as much of this concept is known very little and you might want to account for some of those details.

Leave a Comment