211 lines
8.2 KiB
PHP
211 lines
8.2 KiB
PHP
<?php
|
|
// core/security.php
|
|
|
|
// 1. Session Security Configuration
|
|
// Must be called BEFORE session_start()
|
|
function configure_secure_session() {
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
ini_set('session.cookie_httponly', 1);
|
|
ini_set('session.cookie_samesite', 'Lax');
|
|
ini_set('session.use_only_cookies', 1);
|
|
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// 2. HTTP Security Headers
|
|
function send_security_headers() {
|
|
header("X-Frame-Options: SAMEORIGIN");
|
|
header("X-XSS-Protection: 1; mode=block");
|
|
header("X-Content-Type-Options: nosniff");
|
|
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
|
|
header("Referrer-Policy: strict-origin-when-cross-origin");
|
|
}
|
|
|
|
// 3. CSRF Protection
|
|
function generate_csrf_token() {
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
return $_SESSION['csrf_token'];
|
|
}
|
|
|
|
function verify_csrf_token($token) {
|
|
if (empty($_SESSION['csrf_token']) || empty($token)) {
|
|
return false;
|
|
}
|
|
return hash_equals($_SESSION['csrf_token'], $token);
|
|
}
|
|
|
|
// 4. Rate Limiting (Anti-Brute Force)
|
|
function check_rate_limit($conn, $ip) {
|
|
try {
|
|
// Create table if not exists (Auto-healing)
|
|
$sql_create = "CREATE TABLE IF NOT EXISTS sys_login_attempts (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
ip_address VARCHAR(45) NOT NULL,
|
|
username VARCHAR(255) NOT NULL,
|
|
attempt_time DATETIME NOT NULL
|
|
)";
|
|
$conn->query($sql_create);
|
|
|
|
// Clean up old attempts
|
|
$conn->query("DELETE FROM sys_login_attempts WHERE attempt_time <= (NOW() - INTERVAL 15 MINUTE)");
|
|
|
|
// Count recent failed attempts for this IP
|
|
$stmt = $conn->prepare("SELECT COUNT(*) as count FROM sys_login_attempts WHERE ip_address = ? AND attempt_time > (NOW() - INTERVAL 15 MINUTE)");
|
|
if ($stmt) {
|
|
$stmt->bind_param("s", $ip);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$row = $res->fetch_assoc();
|
|
$stmt->close();
|
|
|
|
if ($row && $row['count'] >= 5) {
|
|
return false; // Blocked
|
|
}
|
|
}
|
|
} catch (Throwable $e) {
|
|
error_log("check_rate_limit Error: " . $e->getMessage());
|
|
}
|
|
return true; // Allowed
|
|
}
|
|
|
|
function record_failed_login($conn, $ip, $username) {
|
|
try {
|
|
$stmt = $conn->prepare("INSERT INTO sys_login_attempts (ip_address, username, attempt_time) VALUES (?, ?, NOW())");
|
|
if ($stmt) {
|
|
$stmt->bind_param("ss", $ip, $username);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
|
|
// Check if exactly 5 to send notification
|
|
$stmt2 = $conn->prepare("SELECT COUNT(*) as count FROM sys_login_attempts WHERE ip_address = ? AND attempt_time > (NOW() - INTERVAL 15 MINUTE)");
|
|
if ($stmt2) {
|
|
$stmt2->bind_param("s", $ip);
|
|
$stmt2->execute();
|
|
$res = $stmt2->get_result();
|
|
$row = $res->fetch_assoc();
|
|
$stmt2->close();
|
|
|
|
if ($row && $row['count'] == 5) {
|
|
send_ban_notification($ip, $username);
|
|
}
|
|
}
|
|
}
|
|
} catch (Throwable $e) {
|
|
error_log("record_failed_login Error: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
function clear_failed_logins($conn, $ip) {
|
|
try {
|
|
$stmt = $conn->prepare("DELETE FROM sys_login_attempts WHERE ip_address = ?");
|
|
if ($stmt) {
|
|
$stmt->bind_param("s", $ip);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
}
|
|
} catch (Throwable $e) {
|
|
error_log("clear_failed_logins Error: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
function send_ban_notification($ip, $username) {
|
|
require_once(__DIR__."/settings.php");
|
|
$settings = get_app_settings();
|
|
|
|
$message = "⚠️ แจ้งเตือนความปลอดภัย\n";
|
|
$message .= "มีการระงับการใช้งาน IP Address (Ban IP)\n";
|
|
$message .= "IP: {$ip}\n";
|
|
$message .= "Username ที่พยายามเข้าสู่ระบบ: {$username}\n";
|
|
$message .= "เวลา: " . date("Y-m-d H:i:s");
|
|
|
|
// Line Notify
|
|
if (!empty($settings['line_notify_enable']) && $settings['line_notify_enable'] == '1' && !empty($settings['line_notify_token'])) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, "https://notify-api.line.me/api/notify");
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, "message=" . urlencode($message));
|
|
$headers = array('Content-type: application/x-www-form-urlencoded', 'Authorization: Bearer ' . $settings['line_notify_token']);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
}
|
|
|
|
// Telegram Notify
|
|
if (!empty($settings['telegram_notify_enable']) && $settings['telegram_notify_enable'] == '1' && !empty($settings['telegram_bot_token']) && !empty($settings['telegram_chat_id'])) {
|
|
$bot_token = $settings['telegram_bot_token'];
|
|
$chat_id = $settings['telegram_chat_id'];
|
|
$url = "https://api.telegram.org/bot{$bot_token}/sendMessage";
|
|
|
|
$post_fields = array(
|
|
'chat_id' => $chat_id,
|
|
'text' => $message
|
|
);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
}
|
|
}
|
|
|
|
function send_test_notification($target = 'all') {
|
|
require_once(__DIR__."/settings.php");
|
|
$settings = get_app_settings();
|
|
|
|
$message = "🔔 ทดสอบระบบแจ้งเตือน HOSxP Web Service\n";
|
|
$message .= "ระบบของคุณสามารถส่งการแจ้งเตือนได้ตามปกติ!\n";
|
|
$message .= "เวลา: " . date("Y-m-d H:i:s");
|
|
|
|
$results = [];
|
|
|
|
// Line Notify
|
|
if (($target === 'all' || $target === 'line') && !empty($settings['line_notify_enable']) && $settings['line_notify_enable'] == '1' && !empty($settings['line_notify_token'])) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, "https://notify-api.line.me/api/notify");
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, "message=" . urlencode($message));
|
|
$headers = array('Content-type: application/x-www-form-urlencoded', 'Authorization: Bearer ' . $settings['line_notify_token']);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
$res = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$results['line'] = ($http_code == 200);
|
|
}
|
|
|
|
// Telegram Notify
|
|
if (($target === 'all' || $target === 'telegram') && !empty($settings['telegram_notify_enable']) && $settings['telegram_notify_enable'] == '1' && !empty($settings['telegram_bot_token']) && !empty($settings['telegram_chat_id'])) {
|
|
$bot_token = $settings['telegram_bot_token'];
|
|
$chat_id = $settings['telegram_chat_id'];
|
|
$url = "https://api.telegram.org/bot{$bot_token}/sendMessage";
|
|
|
|
$post_fields = array(
|
|
'chat_id' => $chat_id,
|
|
'text' => $message
|
|
);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
$res = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$results['telegram'] = ($http_code == 200);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
?>
|