7
votes

I am trying to protect the ~/public_html/dev directory using http auth basic, but to make that secure I want to run it over ssl.

The middle section of the below .htaccess file switches to https if the request URI begins with /dev and works.

The last section of the file works as well but does not work properly with the https redirect.

I basically want to be able to type http://www.example.com/dev/some_sub_dir/ and be redirected to https://www.example.com/dev/some_sub_dir/ and prompted for the http auth username and password.

What currently happens is if I go to http://www.example.com/dev/some_sub_dir/ I get prompted for a username and password over port 80, and then immediately get prompted again over port 443. So my credentials are being sent twice, once in the clear, and once encrypted. Making the whole https url rewrite a little pointless.

The reason for doing this is so that I won't be able to accidentally submit my user/pass over http; https will always be used to access the /dev directory.

The .htaccess is in the ~/public_html/dev directory.

# Rewrite Rules for example.com
RewriteEngine On
RewriteBase /

# force /dev over https
RewriteCond %{HTTPS} !on
RewriteCond %{REQUEST_URI} ^/dev
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

# do auth
AuthType Basic
AuthName "dev"
AuthUserFile /home/matt/public_html/dev/.htpasswd
Require valid-user
9

9 Answers

11
votes

There is a relatively popular hack to force HTTPS before doing Basic Authentication. I first saw it here:

http://blog.jozjan.net/2008/02/htaccess-redirect-to-ssl-https-before.html

It involves using a custom error document to handle whatever happens after the HTTPS check fails.

For example, I have a single page I need to force HTTPS on, so I did this in an .htaccess file:

<FilesMatch "secure-page.php">
    SSLRequireSSL
    ErrorDocument 403 https://www.example.com/secure-page.php
    AuthType Basic
    AuthName "Secure Page"
    AuthUserFile /var/www/whatever/.htpasswdFile
    Require valid-user
</FilesMatch>

Which translates to:

if the requested page is 'secure-page.php' - if not HTTPS, then redirect to a custom 'error page' - the 'error page' is actually just the HTTPS version of the page - on the second request, since the HTTPS check now passes, perform Basic Auth :)

You can extend this concept to a directory or other use cases - your custom 'error page' could be a php page that redirects to the correct HTTPS URL, or a CGI script like in the link above...

4
votes

I ran into the same problem and finally found an ugly solution, but it works. Put the rewrite rule in a Directory directive in httpd.conf or one of your conf.d files (i.e., in the "Main" server configuration). Then, put the Auth* and Require lines in a Directory directive inside the <VirtualHost _default_:443> container in ssl.conf (or wherever your SSL VirtualHost is defined).

For me, this means creating a file /etc/httpd/conf.d/test.conf with:

<Directory "/var/www/html/test">
        #
        # force HTTPS
        #
        RewriteEngine On
        RewriteCond %{HTTPS} off
        RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</Directory>

...and then adding the following inside /etc/httpd/conf.d/ssl.conf just above the </VirtualHost> tag:

<Directory "/var/www/html/test">
        #
        # require authentication
        #
        AuthType Basic
        AuthName "Please Log In"
        AuthUserFile /var/www/auth/passwords
        Require valid-user
</Directory>

Doing this makes Apache apply the RewriteRule to all requests, and the auth requirements only to requests in the 443 VirtualHost.

4
votes

Building on siliconrockstar's answer, I am adding a php script that would work in the case where you want to force SSL on all the files, not just the one-file case shown by siliconrockstar. Here again it works in conjunction with the htaccess file.

The htaccess to protect to whole directory:

    SSLRequireSSL
    ErrorDocument 403 /yourphp.php
    AuthType Basic
    AuthName "Secure Page"
    AuthUserFile /some_path_above_the_html_root/.htpasswdFile
    Require valid-user

The php called by the htaccess (the path given for the php in this sample htaccess is the root of your site), which forces https on the url you called:

<?php
$path = "https://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
if ( $_SERVER['SERVER_PORT'] == 80) {
    header("Status: 302 Moved\n");
    header("Location: ".$path."\n\n");
}
else {
    header( "Content-type: text/html\n\n");
    echo "How did you get here???";
}
?>

If your site doesn't have an SSL certificate, you'll have to install one. If this is your only use for it, you can install a self-signed certificate. On a cPanel VPS with your site on a dedicated IP, that takes moments to do: in WHM, visit

One. Generate an SSL Certificate and Signing Request

then

Two. Install an SSL Certificate on a Domain

2
votes

Protecting content with basic authentication will never work securely over HTTP.

Once the user has entered their username and password, it is sent unencrypted for every page view to that site - its not just sent the time the user gets prompted.

You have to treat requests over HTTP as un-authenticated, and do all logged in stuff over HTTPS.

A lot of websites have used HTTPS for the login - using forms and cookies, rather than basic auth - and then go to HTTP afterwards. This means that their 'you are logged in' cookie gets sent unencrypted. Every valuable target has been hacked because of this, and gmail is now switching to full HTTPS and others will follow.

You don't have the same scaling issues that others have had that has kept them away from the computationally more expensive HTTPS. If your homepage supports HTTPS access, use it throughout.

1
votes

If you place the rewrite rules in the main config, outside any or or similar, rewriting will be done before authentication.

See Rewrite Tech

0
votes

Does it work to put your authentication section in a <Location> or <LocationMatch> tag using the protocol as the term?

0
votes

I get around it this way. Just allow Non-SSL since it will be redirected then require auth once on SSL...

SetEnvIf %{SERVER_PORT} ^80$ IS_NON_SSL

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

AuthUserFile /.htpasswd
AuthName "Enter your Username and Password:"
AuthType Basic
require valid-user
Allow from env=IS_NON_SSL
0
votes

I know this is an old question but I had trouble with a simple ht.access redirect. Following many other problems and answers I finally put together this htaccess which works as intended.

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

AuthName "Private Server"
AuthUserFile /var/www/.htpassword
AuthType Basic
require valid-user
Order allow,deny
Allow from env=!HTTPS
Satisfy Any

To note the

Order allow,deny

Which is what was missing from many other answers I have seen as it would allow people in directly when using https. The other section that was missing from my tests was the following:

Satisfy Any

This following snippet is what allows non SSL clients in with for the redirect. The HTTPS env var is set from the mod_ssl for ssl clients.

Allow from env=!HTTPS
0
votes

I have two answers to the question, one with a 2010 mindset, and one with a post-2012 mindset.

To answer this question as if it was 2010 when it was asked: use two different VirtualHost configurations.

This solution is a little outside the scope of the question as the implication is "I only have access to modify .htaccess!" but a viable solution in many cases is to request the administrator du jour to simply setup two different VirtualHost configurations.

  • The non-SSL VirtualHost is not configured to access anything on the filesystem, and merely returns 301 redirects to https://... locations for all requests.

  • The SSL listening VirtualHost is then free to implement Basic Authentication without worry that the standard HTTP listener will give away the goods.

However, with my post-2018 hat on, and noting that this question was asked before Apache 2.4 (early 2012?), it's time for an update. Apache 2.4 introduced <If condition> checks, which makes this much easier and more direct:

  • First, do not IfModule the RewriteEngine. As Apache is listening on both port 80 and 443 (in the standard setup), AND we want to enforce SSL, we want the server to break if the Rewrite module is otherwise disengaged.

    RewriteEngine On 
    
  • Second, simply enough, perform an immediate and permanent (301) redirect if the request is not secure. Note the R(edirect) and L(ast) which work together to ensure that non-encrypted requests are redirected and can't be utilized to gain non-authenticated access. (For the OCD, non-copy-paste minded, note the lack of space between != and on. That is intentional.)

    RewriteCond %{HTTPS} !=on 
    RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,QSA,L,NC]
    
  • Finally, use that same variable with an <If-condition> check to ensure that we only require the password for TLS request.

    <If "%{HTTPS} == 'on'">
            AuthName "Password please!" 
            AuthType Basic 
            AuthUserFile /path/to/htpasswdfile
            AuthGroupFile /dev/null 
            require valid-user
    </If>
    

And altogether:

# .htaccess

RewriteEngine On

RewriteCond %{HTTPS} !=on
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,QSA,L,NC]

<If "%{HTTPS} == 'on'">
        AuthName "Password please!"
        AuthType Basic
        AuthUserFile /path/to/htpasswdfile
        AuthGroupFile /dev/null
        require valid-user
</If>