171 lines
No EOL
4.3 KiB
PHP
171 lines
No EOL
4.3 KiB
PHP
<?php
|
|
/**
|
|
* Secure Website Monitoring Script
|
|
*
|
|
* Monitors websites and logs their response times and status codes
|
|
* Recommended to run via cron job
|
|
*/
|
|
|
|
// Strict error reporting
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 0);
|
|
ini_set('log_errors', 1);
|
|
|
|
// Validate and include configuration
|
|
$configPath = __DIR__ . '/configuration.php';
|
|
if (!file_exists($configPath)) {
|
|
error_log('Configuration file not found');
|
|
exit(1);
|
|
}
|
|
require_once $configPath;
|
|
|
|
// Validate PATH constant
|
|
if (!defined('PATH') || !is_dir(PATH)) {
|
|
error_log('Invalid monitoring directory');
|
|
exit(1);
|
|
}
|
|
|
|
// Secure monitors file path
|
|
$monitorsFile = PATH . '/monitors.json';
|
|
if (!file_exists($monitorsFile)) {
|
|
error_log('Monitors configuration file not found');
|
|
exit(1);
|
|
}
|
|
|
|
// Read and validate monitors
|
|
$monitorsJson = file_get_contents($monitorsFile);
|
|
$monitors = json_decode($monitorsJson, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE || !is_array($monitors)) {
|
|
error_log('Invalid monitors JSON');
|
|
exit(1);
|
|
}
|
|
|
|
// Maximum number of historical records to keep
|
|
const MAX_HISTORY_RECORDS = 60;
|
|
|
|
// Maximum execution time
|
|
set_time_limit(30);
|
|
|
|
/**
|
|
* Validate and sanitize URL
|
|
*
|
|
* @param string $url URL to validate
|
|
* @return string|false Sanitized URL or false if invalid
|
|
*/
|
|
function validateUrl($url) {
|
|
// Trim and validate URL
|
|
$url = trim($url);
|
|
|
|
// Check if URL is valid
|
|
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
|
return false;
|
|
}
|
|
|
|
// Additional URL schemes validation (optional)
|
|
$allowedSchemes = ['http', 'https'];
|
|
$urlParts = parse_url($url);
|
|
|
|
if (!isset($urlParts['scheme']) || !in_array($urlParts['scheme'], $allowedSchemes)) {
|
|
return false;
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Safely fetch website response
|
|
*
|
|
* @param string $url URL to monitor
|
|
* @return array Response data
|
|
*/
|
|
function fetchWebsiteResponse($url) {
|
|
$response_data = [
|
|
'timestamp' => time(),
|
|
'error' => null,
|
|
'time' => null,
|
|
'response' => null
|
|
];
|
|
|
|
// Validate URL
|
|
$sanitizedUrl = validateUrl($url);
|
|
if (!$sanitizedUrl) {
|
|
$response_data['error'] = 'Invalid URL';
|
|
return $response_data;
|
|
}
|
|
|
|
// Initialize cURL with safe options
|
|
$curl = curl_init();
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_URL => $sanitizedUrl,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HEADER => false,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_MAXREDIRS => 3,
|
|
CURLOPT_TIMEOUT => 10,
|
|
CURLOPT_CONNECTTIMEOUT => 5,
|
|
CURLOPT_SSL_VERIFYPEER => true,
|
|
CURLOPT_SSL_VERIFYHOST => 2
|
|
]);
|
|
|
|
// Execute request
|
|
$response = curl_exec($curl);
|
|
|
|
// Check for cURL errors
|
|
if ($response === false) {
|
|
$response_data['error'] = curl_error($curl);
|
|
} else {
|
|
// Get connection info
|
|
$info = curl_getinfo($curl);
|
|
$response_data['time'] = round($info['total_time_us'] / 1000, 2);
|
|
$response_data['response'] = $info['http_code'];
|
|
}
|
|
|
|
// Close cURL resource
|
|
curl_close($curl);
|
|
|
|
return $response_data;
|
|
}
|
|
|
|
/**
|
|
* Safely write monitor data
|
|
*
|
|
* @param string $name Monitor name
|
|
* @param array $newData New response data
|
|
*/
|
|
function writeMonitorData($name, $newData) {
|
|
// Validate monitor name (prevent path traversal)
|
|
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) {
|
|
error_log("Invalid monitor name: $name");
|
|
return;
|
|
}
|
|
|
|
$monitorFile = PATH . '/monitors/' . $name;
|
|
|
|
// Read existing data
|
|
$data = [];
|
|
if (file_exists($monitorFile)) {
|
|
$fileContent = file_get_contents($monitorFile);
|
|
$data = json_decode($fileContent, true) ?: [];
|
|
}
|
|
|
|
// Merge and limit historical data
|
|
$data[] = $newData;
|
|
$data = array_slice($data, -MAX_HISTORY_RECORDS);
|
|
|
|
// Securely write file
|
|
$jsonData = json_encode($data, JSON_PRETTY_PRINT);
|
|
if (file_put_contents($monitorFile, $jsonData) === false) {
|
|
error_log("Failed to write monitor data for: $name");
|
|
}
|
|
}
|
|
|
|
// Main monitoring loop
|
|
foreach ($monitors as $name => $url) {
|
|
try {
|
|
$responseData = fetchWebsiteResponse($url);
|
|
writeMonitorData($name, $responseData);
|
|
} catch (Exception $e) {
|
|
error_log("Monitoring error for $name: " . $e->getMessage());
|
|
}
|
|
} |