When working with mixed PHP and static content sites, we often face authentication inconsistencies. PHP sessions provide excellent authentication mechanisms, but they don't natively extend to static directories. The traditional approach of putting PHP gatekeepers in every directory isn't optimal for static content.
We can leverage Apache's rewrite capabilities to check PHP session validity before serving static content. This solution maintains performance while ensuring security:
# In your .htaccess file
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/protected-directory/ [NC]
RewriteCond %{HTTP_COOKIE} !PHPSESSID=([a-zA-Z0-9]+) [NC]
RewriteRule ^ - [R=403,L]
RewriteCond %{REQUEST_URI} ^/protected-directory/ [NC]
RewriteCond %{HTTP_COOKIE} PHPSESSID=([a-zA-Z0-9]+) [NC]
RewriteCond /path/to/php/session/sess_%1 !-f
RewriteRule ^ - [R=403,L]
For more robust validation, create a small PHP validator script:
// session_validator.php
session_start();
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
header('HTTP/1.0 403 Forbidden');
exit;
}
Then modify your Apache configuration:
# httpd.conf or virtual host configuration
<Directory "/var/www/protected-directory">
AuthType Basic
AuthName "Restricted Area"
AuthBasicProvider file
AuthUserFile /dev/null
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^.*$ - [env=ALLOW_ACCESS:1]
<If "!env('ALLOW_ACCESS')">
Require expr "(%{HTTP_COOKIE} =~ /PHPSESSID=([^;]+)/ && -f '/path/to/sessions/sess_%{MATCH_1}' && file_get_contents('/path/to/sessions/sess_%{MATCH_1}') =~ /authenticated\|b:1/)"
</If>
</Directory>
For high-traffic sites, consider caching session validation results:
# Alternative using environment variables
RewriteMap sessiondb "dbm:/path/to/sessiondb.map"
RewriteCond %{HTTP_COOKIE} PHPSESSID=([a-zA-Z0-9]+) [NC]
RewriteCond ${sessiondb:%1} !^1$
RewriteRule ^protected-directory/ - [F]
Add additional layers of protection for sensitive static content:
# Additional .htaccess protections
<FilesMatch "\.(pdf|docx?|xlsx?)$">
ForceType application/octet-stream
Header set Content-Disposition "attachment"
</FilesMatch>
Options -Indexes
When working with mixed-content websites (PHP-driven authentication + static files), we often face this dilemma: how to protect static directories without:
- Converting every static file into PHP scripts
- Using separate authentication systems
- Compromising performance
The solution lies in making Apache aware of PHP session status. Here's a complete implementation:
# .htaccess in protected directory
<FilesMatch ".*">
AuthType Basic
AuthName "Restricted Area"
AuthBasicProvider external
AuthExternal php-session-auth
Require valid-user
</FilesMatch>
We need a custom Apache module or CGI script to verify PHP sessions. Here's a Python implementation:
#!/usr/bin/env python3
import os
import sys
import phpserialize
def check_session(session_id):
# Path to your PHP session files
session_file = f'/var/lib/php/sessions/sess_{session_id}'
if os.path.exists(session_file):
with open(session_file, 'r') as f:
data = phpserialize.loads(f.read())
return b'logged_in' in data and data[b'logged_in']
return False
if __name__ == '__main__':
session_cookie = os.getenv('HTTP_COOKIE', '')
if 'PHPSESSID' in session_cookie:
session_id = session_cookie.split('PHPSESSID=')[1].split(';')[0]
if check_session(session_id):
print("Authorized")
sys.exit(0)
print("Unauthorized")
sys.exit(1)
For high-traffic sites, consider these optimizations:
- Cache session validation results
- Use mod_authnz_external with persistent helpers
- Implement session validation at CDN level if possible
If external authentication seems complex, create a lightweight PHP gateway:
<?php
session_start();
if (!isset($_SESSION['logged_in'])) {
header('HTTP/1.0 403 Forbidden');
exit;
}
$file = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'];
if (file_exists($file)) {
header('Content-Type: ' . mime_content_type($file));
readfile($file);
} else {
header('HTTP/1.0 404 Not Found');
}
- Always regenerate session IDs after login
- Set proper session cookie attributes (Secure, HttpOnly, SameSite)
- Implement session expiration
- Consider adding CSRF protection for sensitive operations