Implements security helper and CSRF middleware
parent
be77376d85
commit
9c9a306f55
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
// Security helper, to be used with all the forms in the app
|
||||
|
||||
class SecurityHelper {
|
||||
private static $instance = null;
|
||||
private $session;
|
||||
|
||||
private function __construct() {
|
||||
// Don't start a new session, just reference the existing one
|
||||
$this->session = &$_SESSION;
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new SecurityHelper();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
// Generate CSRF token
|
||||
public function generateCsrfToken() {
|
||||
if (empty($this->session['csrf_token'])) {
|
||||
$this->session['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $this->session['csrf_token'];
|
||||
}
|
||||
|
||||
// Verify CSRF token
|
||||
public function verifyCsrfToken($token) {
|
||||
if (empty($this->session['csrf_token']) || empty($token)) {
|
||||
return false;
|
||||
}
|
||||
return hash_equals($this->session['csrf_token'], $token);
|
||||
}
|
||||
|
||||
// Sanitize string input
|
||||
public function sanitizeString($input) {
|
||||
if (is_string($input)) {
|
||||
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Validate email
|
||||
public function validateEmail($email) {
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
|
||||
}
|
||||
|
||||
// Validate integer
|
||||
public function validateInt($input) {
|
||||
return filter_var($input, FILTER_VALIDATE_INT) !== false;
|
||||
}
|
||||
|
||||
// Validate URL
|
||||
public function validateUrl($url) {
|
||||
return filter_var($url, FILTER_VALIDATE_URL) !== false;
|
||||
}
|
||||
|
||||
// Sanitize array of inputs
|
||||
public function sanitizeArray($array, $allowedKeys = []) {
|
||||
$sanitized = [];
|
||||
foreach ($array as $key => $value) {
|
||||
if (empty($allowedKeys) || in_array($key, $allowedKeys)) {
|
||||
if (is_array($value)) {
|
||||
$sanitized[$key] = $this->sanitizeArray($value);
|
||||
} else {
|
||||
$sanitized[$key] = $this->sanitizeString($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
// Validate form data based on rules
|
||||
public function validateFormData($data, $rules) {
|
||||
$errors = [];
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (!isset($data[$field]) && $rule['required']) {
|
||||
$errors[$field] = "Field is required";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($data[$field])) {
|
||||
$value = $data[$field];
|
||||
switch ($rule['type']) {
|
||||
case 'email':
|
||||
if (!$this->validateEmail($value)) {
|
||||
$errors[$field] = "Invalid email format";
|
||||
}
|
||||
break;
|
||||
case 'integer':
|
||||
if (!$this->validateInt($value)) {
|
||||
$errors[$field] = "Must be a valid integer";
|
||||
}
|
||||
break;
|
||||
case 'url':
|
||||
if (!$this->validateUrl($value)) {
|
||||
$errors[$field] = "Invalid URL format";
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
if (isset($rule['min']) && strlen($value) < $rule['min']) {
|
||||
$errors[$field] = "Minimum length is {$rule['min']} characters";
|
||||
}
|
||||
if (isset($rule['max']) && strlen($value) > $rule['max']) {
|
||||
$errors[$field] = "Maximum length is {$rule['max']} characters";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../helpers/securityhelper.php';
|
||||
|
||||
function verifyCsrfToken() {
|
||||
$security = SecurityHelper::getInstance();
|
||||
|
||||
// Skip CSRF check for GET requests
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip CSRF check for initial login attempt
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||||
isset($_GET['page']) && $_GET['page'] === 'login' &&
|
||||
!isset($_SESSION['username'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check CSRF token for all other POST requests
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$token = $_POST['csrf_token'] ?? '';
|
||||
if (!$security->verifyCsrfToken($token)) {
|
||||
// Log CSRF attempt
|
||||
error_log("CSRF attempt detected from IP: " . $_SERVER['REMOTE_ADDR']);
|
||||
$logObject->insertLog(0, "CSRF attempt detected from IP: " . $_SERVER['REMOTE_ADDR'], 'system');
|
||||
|
||||
// Return error message
|
||||
http_response_code(403);
|
||||
die('Invalid CSRF token. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
$token = SecurityHelper::getInstance()->generateCsrfToken();
|
||||
?>
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>" />
|
Loading…
Reference in New Issue