10

I have a site that uses PHP session for authentication. There is one directory that I would like to restrict access to that does not use any PHP, it's just full of static content.

I just don't know how to restrict access without every request going through a PHP script. Is there some way to have Apache check the session credentials and restrict access like Basic Auth?

7 Answers 7

6

Unless you've changed the settings, PHP session data is stored in a variation on its own serialize() format in a temporary directory, and it's not easy to get at that without using PHP itself.

unfortuantly, you appear to want the speed of static served files while dynamically authorising each request, which are not really compatible goals. You might comprimise by having a super-light PHP script which you then use mod_rewrite to rewrite requests to files within it to, which passes though things that are fine. Super simple example:

.htaccess:

 RewriteEngine On
 RewriteMap auth prg:auth.php
 RewriteRule (.*) ${auth:$1}

auth.php:

#!/usr/bin/php
 <?PHP
 set_time_limit(0); # This program needs to run forever. 
 $stdin = fopen("php://stdin","r"); # Keeps reading from standard in
 while (true) {
        $line = trim(fgets($stdin));
        if (isset($_SESSION['USER_LOGGED_IN'])) {
                echo $line\n";
        } else {
                echo "authfailed.html\n";
        }
 }

notably, that PHP script is running forever, so you'll need to restart apache if you change it, I think.

This is all untested, but that's roughly the direction I think you'd have to go in.

References:

3
  • I like this approach, but I am wondering if a RewriteMap script has access to anything other than stdin? Does it have the context of cookies etc?
    – Cogsy
    Commented Mar 25, 2010 at 6:27
  • Actually, yes, you do have a point. You can send in the value of cookies using mod_rewrite's environement variables syntax (So I think the syntax would become something like ${auth:$1 $COOKIES} and you'd have to split the line of stdin by arguments, by this method you could pick up the session contents.
    – Aquarion
    Commented Mar 25, 2010 at 16:04
  • What if you set a cookie by login with a constant auth id (lots of bit length), and you check it by directory access? Not the best solution, but it can work on admin pages... (By file share pages it doesn't work because users can share the cookie...)
    – inf3rno
    Commented Feb 16, 2012 at 3:20
2

My plan to solve this problem is now to

  1. Redirect the request to PHP

    RewriteEngine on
    RewriteRule ([0-9a-z-_]+)$ authenticateUser.php?&file=$1 [L]
    
  2. Authenticate the user in PHP (all other authentication methods are maybe too weak or require writing into files all the time)

    if ( User::hasPermission() && isSane( $filePath ) ) {
        // 
        header( 'X-Sendfile: ' . $filePath );
    }
    
  3. Use Apache's mod_xsendfile (docs, github)

1
1

If you had a particular cookie you can expect, then you can test for its absence with mod_rewrite and give a 403 Forbidden.

RewriteCond %{HTTP_COOKIE} !LoggedIn=true
RewriteRule .* - [F,L]

But, if anyone knew that they needed a cookie set with "LoggedIn=true", then they could easily get around your "protection".

A PHP session is specific to PHP. Apache has no way of using any info in a PHP session. You would have to have some authentication module specifically for doing the session verification.

What I've seen most people do is have a PHP script handle the serving of the static content in that it gets the request, verifies the session, reads in the file and sends the content out with the appropriate MIME info.

1
  • Or you can modify the .htaccess file by every login, logout and gc...
    – inf3rno
    Commented Feb 16, 2012 at 3:24
1

The conventional solution for that problem is to redirect every call on that folder to a php file, which checks the user permissions, and after that it reads the file, and send it to the output stream or redirects the user to the "no permission" site. For example...

Another tricky way to protect you files is to generate a token from the session_id, and a static salt (and optianally from the static file path), and check it by the file accessing. So you have to regenerate that token in you htaccess file. I don't know whether it's possible only with .htaccess, or you have to use php for that. I found a similar solution here. I'm 99% convinced, that the md5 is not a built in mod rewrite feature.

1
  • 1
    I love the idea from the Blogspot link to use RewriteMap to a prg://… shell script resource which does all the actual MD5 encryption and verification. I need to test this and make sure it works on my setup, but it looks like it should work with vanilla Apache + the mod_rewrite module.
    – thirdender
    Commented Aug 31, 2015 at 12:43
0

Yes to Basic Auth if you're using mod_php. http://php.net/manual/en/features.http-auth.php

1
  • Authentication is already using PHP Sessions. I'm really looking for a way to use the session cookie.
    – Cogsy
    Commented Mar 24, 2010 at 3:13
0

Expanding on @Aquarion answer:

httpd.conf:

RewriteMap session "prg:/xampp/php/php-win.exe -c /xampp/php/php.ini /xampp/htdocs/domain/src/session-daemon.php"

<Directory "/xampp/htdocs/domain/public/private">
    RewriteCond %{HTTP_COOKIE} PHPSESSID=([^;]+)
    RewriteCond ${session:%1} !=1
    RewriteRule ^ - [END,R=403]
</Directory>

note that per apache documentation: https://httpd.apache.org/docs/current/rewrite/rewritemap.html

While you cannot declare a map in per-directory context (.htaccess files or blocks) it is possible to use this map in per-directory context.

session-daemon.php:

<?php
set_time_limit(0); // this program needs to run forever

$idLength = ini_get('session.sid_length');

$startOptions = [
    'read_and_close' => '1',
    'use_cookies' => '0',
    'cache_limiter' => ''
];

// trim remove new line char
while ($id = trim(fgets(STDIN, $idLength + 2))) {
    if ($id) {
        $logged = false;

        session_id($id); // set the session id for the current session
        if (session_start($startOptions)) {
            if (isset($_SESSION['logged']) && $_SESSION['logged'] === true) {
                $logged = true;
            }
        }

        fputs(STDOUT, $logged ? '1' : '0');
    }
    fputs(STDOUT, "\n"); // end processing for this line with linebreak
    flush(); // flush sends the result to STDOUT, this is very important, no buffering
}

so we use session_id() to manualy set the id passed from apache https://www.php.net/manual/en/function.session-id.php

0

This is a very old question but popped to the top of the stack today. Oddly there's lots of upvoted answers without ANY of them addressing the requirement "without every request going through a PHP script".

This is exactly the problem mod_auth_memcookie was written to address.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .