3

I've set up custom flows on my Wordpress blog for login, signup, and commenting. I would like to password protect the standard login flow so only I can use it, and block access entirely to the standard comment flow.

My instinct is to use .htaccess to pw protect access to wp-login.php and wp-signup.php. Is this sufficient or are there other methods of accessing this page? e.g. index.php?action=login, etc.

For comments, I'm not sure. Is it best to hook into the comment action and kill the process? Or will .htaccess work for that too?

If .htaccess is a viable solution for one or both of these scenarios, example code would be very much appreciated, as I am a novice here.

Edit: I am using the following WP functions in my flows, so these must be able to run:

  • wp_set_auth_cookie
  • wp_create_user
  • wp_signon
  • wp_insert_comment
2
  • Concerning the comments-did you try disabling commenting in the settings?
    – kraftner
    Apr 24, 2014 at 20:54
  • @kraftner that would cause comments_open() to always return false, which is not desired behavior. I need to be able to enable and disable comments on certain articles using the default wordpress options. Apr 28, 2014 at 15:39

3 Answers 3

1
+50

All standard login / registration action are done using 'wp-login.php' file.

The standard comments are saved using 'wp-comments-post.php' file.

Blocking this 2 files you block standard flow for comments, login, registration and password recover.

Very early in its bootstrap WordPress save a global variable $pagenow that contain the name of the file being loaded. You can use that variable to block the flow.

Blocking comments will be very simple, because you want block it for all.

First let's write a function that ends the request, send an http header 403 and outuput a message. That can be done using wp_die

function restrict_standard_flow( $not_allowed = 'Action' ) {
  $args = array( 'response' => 403 ); // set response to 403
  $msg = $not_allowed . ' not allowed';
  if ( restrict_used_method() === 'GET' ) {
    $args['back_link'] = TRUE; // show a back link if request send via GET
  };
  wp_die( $msg, $msg, $args );
}

restrict_used_method() is a function that return the current method used:

function restrict_used_method() {
  $method = filter_input( INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING );
  return strtoupper( $method ) === 'GET' ? 'GET' : 'POST';
}

This function prevent unexpected behavior if methods are not POST or GET.

At this point, we can use $pagenow variable to end the request if it is 'wp-comments-post.php' and run a function if it is 'wp-login.php'.

add_action( 'plugins_loaded', function() {
  global $pagenow;
  if ( $pagenow === 'wp-login.php' ) {
    restrict_standard_login_flow();
  } elseif ( $pagenow === 'wp-comments-post.php' ) {
    restrict_standard_flow( 'Standard comment flow' );
  }
});

So the standard comment flow is blocked, we have to write restrict_standard_login_flow() function.

I think that an easy way to implement password protection is to allow inserting the password as url variable. In that case we have to assure that a password in the url is also included in login form, to pass it when it's submitted.

We can do this using 'login_form' action hooks:

add_action( 'login_form', 'embed_pwd_in_form' );

function embed_pwd_in_form() {
  if ( ! is_super_admin() && defined('MY_SECRET') && MY_SECRET ) {
    $type = restrict_used_method() === 'GET' ? INPUT_GET : INPUT_POST;
    $pwd = filter_input( $type, 'mypwd', FILTER_SANITIZE_STRING );
    if ( ! empty( $pwd ) ) {
      echo "<input type='hidden' name='mypwd' value='" . md5($pwd). "' />";
      wp_nonce_field( 'pwd_nonce', 'pwd_n' );
    }
  }
}

So we look at url and if the url variable 'mypwd' put it md5-ed in a hidden field, along another hidden nonce field.

The password that you will put in the url, must be compared to something... a simple way is use a constant:

Open your wp-config.php and just before

/* That's all, stop editing! Happy blogging. */

put

define( 'MY_SECRET', 'your-password-here' );

Of course, replace 'your-password-here' with your real password.

Now we can write the restrict_standard_login_flow() function that compare the password in the url to the constant and also check the nonce if current http method is POST:

function restrict_standard_login_flow() {
  // if the current user is not already logged as super admin
  // and a constant 'MY_SECRET' is defined
  if ( ! is_super_admin() && defined('MY_SECRET') && MY_SECRET ) {
    $type = INPUT_GET;
    $not_allowed = FALSE;
    if ( restrict_used_method() === 'POST' ) {
      // sent via form, check a nonce
      $type = INPUT_POST;
      $nonce = filter_input( $type, 'pwd_n', FILTER_SANITIZE_STRING );
      if ( empty($nonce) || ! wp_verify_nonce( $nonce, 'pwd_nonce' ) ) {
        $not_allowed = 'Nonce';
      }
    }
    if ( $not_allowed === FALSE ) { // nonce is valid, check password
      $pwd = filter_input( $type, 'mypwd', FILTER_SANITIZE_STRING );
      // if the password is sent via url, md5 it, like in form
      if ( restrict_used_method() === 'GET' ) $pwd = md5($pwd);
      // if password is not valid
      if ( $pwd === md5(MY_SECRET)  ) {
        $not_allowed = 'Standard login flow';
      }
    }
    if ( $not_allowed === FALSE ) { // if not allowed exit
      restrict_standard_flow( $not_allowed );
    }
  }
}

Please see inline comments for additional details.

Now, to use standard login flow you have to use an url like

http://www.example.com/wp-login.php?mypwd=your-password-here

If you don't set the password the request will be refused.

Please note that code here allow only login form, lost/reset password form are not allowed, so or you extend the code to allow them or you use your custom flow for those tasks.

As alternative, never forget the password ;)

Registeri form is also not allowed, because you want only you are able to use standard flow, and you are already registered.

2
  • Thank you for the thorough answer. The comment solution is exactly what I am looking for; however I do not like the idea of using a GET url parameter for the password because that will surely be saved in plain text in browser history. This is not secure. I will continue to do more research on htaccess solutions before accepting the answer, but I have awarded you the bounty in any case. Apr 28, 2014 at 15:34
  • Sure the password on the url is absolutely not safe, but if someone get it, he/she has no access to your backend, but simply will see a page where the real password must me set. Protect a single url via apache server files is doable, but require high privileges to configure server. Tip for your research, look for ".htpasswd" not ".htaccess". Maybe start from here. @rkarbowski
    – gmazzap
    Apr 28, 2014 at 18:03
0

1) Setting->general to remove Anyone can register option check mark

2) Setting->discussion check Users must be registered and logged in to comment

If two condition any person not register and not comment on your post.

Just add code in function.php and your admin dashboard access only admin.

function block_dashboard() {
    $file = basename($_SERVER['PHP_SELF']);
    if (is_user_logged_in() && is_admin() && !current_user_can('edit_posts') && $file != 'admin-ajax.php'){
        wp_redirect( home_url() );
        exit();
    }
}
add_action('init', 'block_dashboard');
1
  • Ravi, please add an explanation to your code. Just dumping code as an answer is rendered useless. Apr 26, 2014 at 5:51
0

This is a pure .htaccess solution. For a solution using purely WordPress functions, see G. M.'s answer below.

As G. M. pointed out in their answer,

All standard login / registration action are done using 'wp-login.php' file. The standard comments are saved using 'wp-comments-post.php' file.

Thus, we can effectively manage comments and login/registration flows using htaccess to handle requests to these files.

Comments

Thus, if we want to block access to the standard comments flow, we need to block access to wp-comments-post.php. Here's how we can do that with .htaccess:

<Files "wp-comments-post.php">
  Order Allow,Deny
  Deny from all
</Files>

Deny from all blocks access from anyone. Order Allow,Deny tells apache override any default Allow statement with this Deny statement. If you want to read more about that, look here.

Now, if a user attempts to visit wp-comments-post.php, their browser's default 403 error page will be displayed.

Login/Registration

To restrict access to the standard login flow to one or more users, we'll use .htpasswd basic authentication.

Start by generating credentials using a website like this. Your output will look something like myusername:$apr1$Qp4aF23x$mK3KLb17lL2vbJmtWUmQ5. (the password will look encoded like that but you'll still use the plain text password you provided to login.) Save this output to a file called .htpasswd in your WordPress root directory (should be the same dir as your .htaccess file.)

Now, add the following to your .htaccess file:

<Files "wp-login.php">
  Allow from all
  AuthType Basic
  AuthName "This area is restricted, fool." 
  AuthUserFile /var/www/absolute/path/to/.htpasswd
  Require user myusername
</Files>

Your browser will now prompt all users for a username and password when they attempt to visit wp-login.php.

Note for those developing in multiple environments: if the server path to your installation changes between local and production (e.g. /var/www and /app/public) you'll need to make sure AuthUserFile is pointing at the correct file in each environment. One way to accomplish this is to adjust the above code by removing the AuthUserFile line and adding this:

<IfDefine localdev>
  AuthUserFile /var/www/absolute/path/to/.htpasswd
</IfDefine>
<IfDefine !localdev>
  AuthUserFile /app/public/absolute/path/to/.htpasswd
</IfDefine>

This code looks for an environment variable called localdev - conditionally setting the location of AuthUserFile whether it finds it or does not. You'll have to define localdev when you start apache on your local server e.g. apachectl -k start -Dlocaldev

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.