Files
HexaHost-Web-Prod/backend/api/ssl-check.php

160 lines
4.7 KiB
PHP

<?php
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
if (!checkApiRateLimit('ssl-check')) {
rejectApiRateLimit();
}
$domain = getValidatedDomainParam();
if ($domain === null) {
http_response_code(400);
echo json_encode(['error' => empty($_GET['domain']) ? 'Domain-Parameter fehlt' : 'Ungültiges Domain-Format']);
exit;
}
$startTime = microtime(true);
$sslData = checkSslCertificate($domain);
$queryTime = round((microtime(true) - $startTime) * 1000, 2);
echo json_encode([
'success' => $sslData['success'],
'domain' => $domain,
'query_time_ms' => $queryTime,
'timestamp' => date('Y-m-d H:i:s'),
'ssl' => $sslData
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
function checkSslCertificate(string $domain): array {
$result = [
'success' => false,
'has_ssl' => false,
'is_valid' => false,
'error' => null,
'certificate' => null
];
$context = stream_context_create([
'ssl' => [
'capture_peer_cert' => true,
'verify_peer' => false,
'verify_peer_name' => false,
]
]);
$socket = @stream_socket_client(
"ssl://{$domain}:443",
$errno,
$errstr,
10,
STREAM_CLIENT_CONNECT,
$context
);
if (!$socket) {
$httpSocket = @fsockopen($domain, 80, $errno, $errstr, 5);
if ($httpSocket) {
fclose($httpSocket);
$result['error'] = 'Kein SSL-Zertifikat auf Port 443 gefunden';
} else {
$result['error'] = "Verbindung fehlgeschlagen: {$errstr}";
}
return $result;
}
$result['has_ssl'] = true;
$result['success'] = true;
$params = stream_context_get_params($socket);
$cert = $params['options']['ssl']['peer_certificate'] ?? null;
if ($cert) {
$certInfo = openssl_x509_parse($cert);
if ($certInfo) {
$validFrom = $certInfo['validFrom_time_t'] ?? 0;
$validTo = $certInfo['validTo_time_t'] ?? 0;
$now = time();
$isExpired = $now > $validTo;
$isNotYetValid = $now < $validFrom;
$result['is_valid'] = !$isExpired && !$isNotYetValid;
$daysUntilExpiry = floor(($validTo - $now) / 86400);
$sans = [];
if (isset($certInfo['extensions']['subjectAltName'])) {
$sanStr = $certInfo['extensions']['subjectAltName'];
preg_match_all('/DNS:([^,\s]+)/', $sanStr, $matches);
$sans = $matches[1] ?? [];
}
$issuer = [];
if (isset($certInfo['issuer'])) {
if (isset($certInfo['issuer']['O'])) $issuer['organization'] = $certInfo['issuer']['O'];
if (isset($certInfo['issuer']['CN'])) $issuer['common_name'] = $certInfo['issuer']['CN'];
if (isset($certInfo['issuer']['C'])) $issuer['country'] = $certInfo['issuer']['C'];
}
$subject = [];
if (isset($certInfo['subject'])) {
if (isset($certInfo['subject']['CN'])) $subject['common_name'] = $certInfo['subject']['CN'];
if (isset($certInfo['subject']['O'])) $subject['organization'] = $certInfo['subject']['O'];
}
$result['certificate'] = [
'subject' => $subject,
'issuer' => $issuer,
'valid_from' => date('Y-m-d H:i:s', $validFrom),
'valid_to' => date('Y-m-d H:i:s', $validTo),
'days_until_expiry' => $daysUntilExpiry,
'is_expired' => $isExpired,
'serial_number' => $certInfo['serialNumberHex'] ?? null,
'signature_algorithm' => $certInfo['signatureTypeSN'] ?? null,
'san_domains' => $sans,
'version' => $certInfo['version'] ?? null,
];
if ($daysUntilExpiry <= 30 && $daysUntilExpiry > 0) {
$result['warning'] = "Zertifikat läuft in {$daysUntilExpiry} Tagen ab!";
} elseif ($isExpired) {
$result['error'] = 'Zertifikat ist abgelaufen!';
$result['is_valid'] = false;
}
}
}
fclose($socket);
return $result;
}