117 lines
3.5 KiB
PHP
117 lines
3.5 KiB
PHP
|
<?php
|
||
|
|
||
|
class Security {
|
||
|
private static $instance = null;
|
||
|
private $rate_limit_file;
|
||
|
private static $encryptionKey = 'your-secure-encryption-key-here';
|
||
|
|
||
|
private function __construct() {
|
||
|
$this->rate_limit_file = __DIR__ . '/../cache/rate_limits.json';
|
||
|
if (!file_exists($this->rate_limit_file)) {
|
||
|
if (!is_dir(dirname($this->rate_limit_file))) {
|
||
|
mkdir(dirname($this->rate_limit_file), 0755, true);
|
||
|
}
|
||
|
file_put_contents($this->rate_limit_file, '{}');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static function getInstance() {
|
||
|
if (self::$instance === null) {
|
||
|
self::$instance = new self();
|
||
|
}
|
||
|
return self::$instance;
|
||
|
}
|
||
|
|
||
|
public static function sanitizeInput($data) {
|
||
|
if (is_array($data)) {
|
||
|
return array_map([self::class, 'sanitizeInput'], $data);
|
||
|
}
|
||
|
return htmlspecialchars(strip_tags(trim($data)), ENT_QUOTES, 'UTF-8');
|
||
|
}
|
||
|
|
||
|
public static function validateRecaptcha($response) {
|
||
|
$url = 'https://www.google.com/recaptcha/api/siteverify';
|
||
|
$data = [
|
||
|
'secret' => RECAPTCHA_SECRET_KEY,
|
||
|
'response' => $response
|
||
|
];
|
||
|
|
||
|
$options = [
|
||
|
'http' => [
|
||
|
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||
|
'method' => 'POST',
|
||
|
'content' => http_build_query($data)
|
||
|
]
|
||
|
];
|
||
|
|
||
|
$context = stream_context_create($options);
|
||
|
$result = file_get_contents($url, false, $context);
|
||
|
$result_json = json_decode($result, true);
|
||
|
|
||
|
return isset($result_json['success']) && $result_json['success'] === true;
|
||
|
}
|
||
|
|
||
|
public function checkRateLimit($ip, $endpoint, $limit = 60, $window = 3600) {
|
||
|
$limits = json_decode(file_get_contents($this->rate_limit_file), true);
|
||
|
$now = time();
|
||
|
$key = $ip . ':' . $endpoint;
|
||
|
|
||
|
if (!isset($limits[$key])) {
|
||
|
$limits[$key] = ['count' => 0, 'window_start' => $now];
|
||
|
}
|
||
|
|
||
|
if ($now - $limits[$key]['window_start'] > $window) {
|
||
|
$limits[$key] = ['count' => 0, 'window_start' => $now];
|
||
|
}
|
||
|
|
||
|
$limits[$key]['count']++;
|
||
|
file_put_contents($this->rate_limit_file, json_encode($limits));
|
||
|
|
||
|
return $limits[$key]['count'] <= $limit;
|
||
|
}
|
||
|
|
||
|
public static function generateCSRFToken() {
|
||
|
if (!isset($_SESSION['csrf_token'])) {
|
||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||
|
}
|
||
|
return $_SESSION['csrf_token'];
|
||
|
}
|
||
|
|
||
|
public static function verifyCSRFToken($token) {
|
||
|
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||
|
}
|
||
|
|
||
|
public static function encrypt($data) {
|
||
|
$iv = random_bytes(16);
|
||
|
$encrypted = openssl_encrypt(
|
||
|
$data,
|
||
|
'AES-256-CBC',
|
||
|
self::$encryptionKey,
|
||
|
0,
|
||
|
$iv
|
||
|
);
|
||
|
return base64_encode($iv . $encrypted);
|
||
|
}
|
||
|
|
||
|
public static function decrypt($data) {
|
||
|
$data = base64_decode($data);
|
||
|
$iv = substr($data, 0, 16);
|
||
|
$encrypted = substr($data, 16);
|
||
|
return openssl_decrypt(
|
||
|
$encrypted,
|
||
|
'AES-256-CBC',
|
||
|
self::$encryptionKey,
|
||
|
0,
|
||
|
$iv
|
||
|
);
|
||
|
}
|
||
|
|
||
|
public static function hashPassword($password) {
|
||
|
return password_hash($password, PASSWORD_DEFAULT);
|
||
|
}
|
||
|
|
||
|
public static function verifyPassword($password, $hash) {
|
||
|
return password_verify($password, $hash);
|
||
|
}
|
||
|
}
|