Add security logging, domain validation, and output helpers
- Add authentication failure logging to AuthMiddleware (session expiry, access denied, unauthenticated access attempts) - Add UrlHelper for secure URL generation with host validation against configurable ALLOWED_HOSTS whitelist - Add OutputHelper with consistent XSS-safe escaping functions (h, attr, json, url, css, truncate, date, cssClass) - Add validation to AuditLogModel query parameters (pagination limits, date format validation, action/entity type validation, IP sanitization) - Add APP_DOMAIN and ALLOWED_HOSTS configuration options Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,39 @@ class AuthMiddleware {
|
||||
$this->userModel = new UserModel($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log security event for authentication failures
|
||||
*
|
||||
* @param string $event Event type (e.g., 'auth_required', 'access_denied', 'session_expired')
|
||||
* @param array $context Additional context data
|
||||
*/
|
||||
private function logSecurityEvent(string $event, array $context = []): void {
|
||||
$logData = [
|
||||
'event' => $event,
|
||||
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
||||
'forwarded_for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
|
||||
'request_uri' => $_SERVER['REQUEST_URI'] ?? 'unknown',
|
||||
'request_method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown',
|
||||
'timestamp' => date('c')
|
||||
];
|
||||
|
||||
// Merge additional context
|
||||
$logData = array_merge($logData, $context);
|
||||
|
||||
// Remove null values for cleaner logs
|
||||
$logData = array_filter($logData, fn($v) => $v !== null);
|
||||
|
||||
// Format log message
|
||||
$message = sprintf(
|
||||
"[SECURITY] %s: %s",
|
||||
strtoupper($event),
|
||||
json_encode($logData, JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
|
||||
error_log($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate user from Authelia forward auth headers
|
||||
*
|
||||
@@ -37,6 +70,13 @@ class AuthMiddleware {
|
||||
if (isset($_SESSION['user']) && isset($_SESSION['user']['user_id'])) {
|
||||
// Verify session hasn't expired (5 hour timeout)
|
||||
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 18000)) {
|
||||
// Log session expiration
|
||||
$this->logSecurityEvent('session_expired', [
|
||||
'username' => $_SESSION['user']['username'] ?? 'unknown',
|
||||
'user_id' => $_SESSION['user']['user_id'] ?? null,
|
||||
'session_age_seconds' => time() - $_SESSION['last_activity']
|
||||
]);
|
||||
|
||||
// Session expired, clear it
|
||||
session_unset();
|
||||
session_destroy();
|
||||
@@ -123,6 +163,11 @@ class AuthMiddleware {
|
||||
* Redirect to Authelia login
|
||||
*/
|
||||
private function redirectToAuth() {
|
||||
// Log unauthenticated access attempt
|
||||
$this->logSecurityEvent('auth_required', [
|
||||
'reason' => 'no_auth_headers'
|
||||
]);
|
||||
|
||||
// Redirect to the auth endpoint (Authelia will handle the redirect back)
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
echo '<!DOCTYPE html>
|
||||
@@ -187,6 +232,14 @@ class AuthMiddleware {
|
||||
* @param string $groups User groups
|
||||
*/
|
||||
private function showAccessDenied($username, $groups) {
|
||||
// Log access denied event with user details
|
||||
$this->logSecurityEvent('access_denied', [
|
||||
'username' => $username,
|
||||
'groups' => $groups ?: 'none',
|
||||
'required_groups' => 'admin,employee',
|
||||
'reason' => 'insufficient_group_membership'
|
||||
]);
|
||||
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
echo '<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
Reference in New Issue
Block a user