Merge branch 'main' into develop
This commit is contained in:
164
backend/api/ssl-check.php
Normal file
164
backend/api/ssl-check.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* HexaDNS - SSL Certificate Check API
|
||||
*
|
||||
* Prüft SSL-Zertifikat-Informationen einer Domain
|
||||
*
|
||||
* Verwendung: GET /api/ssl-check.php?domain=example.com
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||
|
||||
if (empty($domain)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Protokoll und Pfad entfernen
|
||||
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
|
||||
$domain = explode('/', $domain)[0];
|
||||
$domain = explode(':', $domain)[0]; // Port entfernen
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => '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);
|
||||
|
||||
/**
|
||||
* Prüft SSL-Zertifikat einer Domain
|
||||
*/
|
||||
function checkSslCertificate(string $domain): array {
|
||||
$result = [
|
||||
'success' => false,
|
||||
'has_ssl' => false,
|
||||
'is_valid' => false,
|
||||
'error' => null,
|
||||
'certificate' => null
|
||||
];
|
||||
|
||||
// Stream Context für SSL
|
||||
$context = stream_context_create([
|
||||
'ssl' => [
|
||||
'capture_peer_cert' => true,
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
]
|
||||
]);
|
||||
|
||||
// Verbindung herstellen
|
||||
$socket = @stream_socket_client(
|
||||
"ssl://{$domain}:443",
|
||||
$errno,
|
||||
$errstr,
|
||||
10, // Timeout
|
||||
STREAM_CLIENT_CONNECT,
|
||||
$context
|
||||
);
|
||||
|
||||
if (!$socket) {
|
||||
// Versuche ohne SSL (um zu prüfen ob Server erreichbar)
|
||||
$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;
|
||||
|
||||
// Zertifikat extrahieren
|
||||
$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;
|
||||
|
||||
// Tage bis Ablauf
|
||||
$daysUntilExpiry = floor(($validTo - $now) / 86400);
|
||||
|
||||
// Subject Alternative Names (SANs)
|
||||
$sans = [];
|
||||
if (isset($certInfo['extensions']['subjectAltName'])) {
|
||||
$sanStr = $certInfo['extensions']['subjectAltName'];
|
||||
preg_match_all('/DNS:([^,\s]+)/', $sanStr, $matches);
|
||||
$sans = $matches[1] ?? [];
|
||||
}
|
||||
|
||||
// Issuer aufbereiten
|
||||
$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 aufbereiten
|
||||
$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,
|
||||
];
|
||||
|
||||
// Warnung wenn bald ablaufend
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user