function tls_fallback_scsv($host, $ip, $port) { global $timeout; if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:_-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { // ipv6 openssl tools are broken. (https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest) return false; } $result = []; $protocols = ssl_conn_protocols($host, $ip, $port); if (count(array_filter($protocols)) > 1) { $result['protocol_count'] = count(array_filter($protocols)); $fallback_test = shell_exec("echo | timeout {$timeout} openssl s_client -servername \"" . escapeshellcmd($host) . "\" -connect " . escapeshellcmd($ip) . ":" . escapeshellcmd($port) . " -fallback_scsv -no_tls1_2 2>&1 >/dev/null"); if (stripos($fallback_test, "alert inappropriate fallback") !== false) { $result['tls_fallback_scsv_support'] = 1; } } else { $result['protocol_count'] = 1; } return $result; }
function ssl_conn_metadata_json($host, $ip, $port, $read_stream, $chain_data = null) { $result = array(); global $random_blurp; global $current_folder; $context = stream_context_get_params($read_stream); $context_meta = stream_context_get_options($read_stream)['ssl']['session_meta']; $cert_data = openssl_x509_parse($context["options"]["ssl"]["peer_certificate"])[0]; if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $result["warning"][] = "You are testing an IPv6 host. Due to <a href=\"https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest\">bugs</a> in OpenSSL's command line tools the results will be inaccurate. Known incorrect are OCSP Stapling, TLS_FALLBACK_SCSV and SSL Compression results, others may also be incorrect."; } $result["checked_hostname"] = $host; //chain if (isset($context_meta)) { if (isset($chain_data)) { $chain_length = count($chain_data); $certificate_chain = array(); if ($chain_length <= 10) { for ($i = 0; $i < $chain_length; $i++) { if (openssl_x509_parse($chain_data[$i])['issuer']['CN'] && openssl_x509_parse($chain_data[$i])['subject']['CN']) { $result["chain"][$i]["name"] = openssl_x509_parse($chain_data[$i])['subject']['CN']; $result["chain"][$i]["issuer"] = openssl_x509_parse($chain_data[$i])['issuer']['CN']; $export_pem = ""; openssl_x509_export($chain_data[$i], $export_pem); array_push($certificate_chain, $export_pem); if (openssl_x509_parse($chain_data[$i])['issuer']['CN'] == openssl_x509_parse($chain_data[$i + 1])['subject']['CN']) { continue; } else { if ($i != $chain_length - 1) { $result["chain"][$i]["error"] = "Issuer does not match the next certificate CN. Chain order is probably wrong."; $result["warning"][] = "Issuer does not match the next certificate CN. Chain order is probably wrong."; } } } } } // chain validation file_put_contents('/tmp/verify_cert.' . $random_blurp . '.pem', implode("\n", array_reverse($certificate_chain)) . PHP_EOL, FILE_APPEND); $verify_output = 0; $verify_exit_code = 0; $verify_exec = exec(escapeshellcmd('openssl verify -verbose -purpose any -CAfile ' . getcwd() . '/cacert.pem /tmp/verify_cert.' . $random_blurp . '.pem') . "| grep -v OK", $verify_output, $verify_exit_code); if ($verify_exit_code != 1) { $result["validation"]["status"] = "failed"; $result["validation"]["error"] = "Error: Validating certificate chain failed: " . str_replace('/tmp/verify_cert.' . $random_blurp . '.pem: ', '', implode("\n", $verify_output)); $result["warning"][] = "Validating certificate chain failed. Probably non-trusted root/self signed certificate, or the chain order is wrong."; } else { $result["validation"]["status"] = "success"; } unlink('/tmp/verify_cert.' . $random_blurp . '.pem'); } // hostname ip port $result["ip"] = $ip; if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $addr = inet_pton(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip)); $unpack = unpack('H*hex', $addr); $hex = $unpack['hex']; $arpa = implode('.', array_reverse(str_split($hex))) . '.ip6.arpa'; if (!empty(dns_get_record($arpa, DNS_PTR)[0]["target"])) { $result["hostname"] = dns_get_record($arpa, DNS_PTR)[0]["target"]; } else { $result["hostname"] = "{$host} (No PTR available)."; } } elseif (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if (!empty(gethostbyaddr(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip)))) { $result["hostname"] = gethostbyaddr(preg_replace('/[^A-Za-z0-9\\.\\:-]/', '', $ip)); } else { $result["hostname"] = "{$host} (No PTR available)."; } } else { $result["hostname"] = "{$host} (No PTR available)."; } $result["port"] = $port; //heartbleed $result['heartbleed'] = test_heartbleed($ip, $port); if ($result['heartbleed'] == "vulnerable") { $result["warning"][] = 'Vulnerable to the Heartbleed bug. Please update your OpenSSL ASAP!'; } // compression $compression = conn_compression($host, $ip, $port); if ($compression == false) { $result["compression"] = false; } else { if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:_-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { // ipv6 openssl tools are broken. (https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest) $result["warning"][] = 'SSL compression not tested because of <a href="https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest">bugs</a> in the OpenSSL tools and IPv6.'; } else { $result["compression"] = true; $result["warning"][] = 'SSL compression enabled. Please disable to prevent attacks like CRIME.'; } } // protocols $result["protocols"] = array_reverse(ssl_conn_protocols($host, $ip, $port)); foreach ($result["protocols"] as $key => $value) { if ($value == true) { if ($key == "sslv2") { $result["warning"][] = 'SSLv2 supported. Please disable ASAP and upgrade to a newer protocol like TLSv1.2.'; } if ($key == "sslv3") { $result["warning"][] = 'SSLv3 supported. Please disable and upgrade to a newer protocol like TLSv1.2.'; } } else { if ($key == "tlsv1.2") { $result["warning"][] = 'TLSv1.2 unsupported. Please enable TLSv1.2.'; } } } // ciphersuites if ($_GET['ciphersuites'] == 1) { $ciphersuites_to_test = array('ECDHE-RSA-AES256-GCM-SHA384', 'ECDHE-ECDSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES256-SHA384', 'ECDHE-ECDSA-AES256-SHA384', 'ECDHE-RSA-AES256-SHA', 'ECDHE-ECDSA-AES256-SHA', 'SRP-DSS-AES-256-CBC-SHA', 'SRP-RSA-AES-256-CBC-SHA', 'SRP-AES-256-CBC-SHA', 'DH-DSS-AES256-GCM-SHA384', 'DHE-DSS-AES256-GCM-SHA384', 'DH-RSA-AES256-GCM-SHA384', 'DHE-RSA-AES256-GCM-SHA384', 'DHE-RSA-AES256-SHA256', 'DHE-DSS-AES256-SHA256', 'DH-RSA-AES256-SHA256', 'DH-DSS-AES256-SHA256', 'DHE-RSA-AES256-SHA', 'DHE-DSS-AES256-SHA', 'DH-RSA-AES256-SHA', 'DH-DSS-AES256-SHA', 'DHE-RSA-CAMELLIA256-SHA', 'DHE-DSS-CAMELLIA256-SHA', 'DH-RSA-CAMELLIA256-SHA', 'DH-DSS-CAMELLIA256-SHA', 'ECDH-RSA-AES256-GCM-SHA384', 'ECDH-ECDSA-AES256-GCM-SHA384', 'ECDH-RSA-AES256-SHA384', 'ECDH-ECDSA-AES256-SHA384', 'ECDH-RSA-AES256-SHA', 'ECDH-ECDSA-AES256-SHA', 'AES256-GCM-SHA384', 'AES256-SHA256', 'AES256-SHA', 'CAMELLIA256-SHA', 'PSK-AES256-CBC-SHA', 'ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-ECDSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES128-SHA256', 'ECDHE-ECDSA-AES128-SHA256', 'ECDHE-RSA-AES128-SHA', 'ECDHE-ECDSA-AES128-SHA', 'SRP-DSS-AES-128-CBC-SHA', 'SRP-RSA-AES-128-CBC-SHA', 'SRP-AES-128-CBC-SHA', 'DH-DSS-AES128-GCM-SHA256', 'DHE-DSS-AES128-GCM-SHA256', 'DH-RSA-AES128-GCM-SHA256', 'DHE-RSA-AES128-GCM-SHA256', 'DHE-RSA-AES128-SHA256', 'DHE-DSS-AES128-SHA256', 'DH-RSA-AES128-SHA256', 'DH-DSS-AES128-SHA256', 'DHE-RSA-AES128-SHA', 'DHE-DSS-AES128-SHA', 'DH-RSA-AES128-SHA', 'DH-DSS-AES128-SHA', 'DHE-RSA-SEED-SHA', 'DHE-DSS-SEED-SHA', 'DH-RSA-SEED-SHA', 'DH-DSS-SEED-SHA', 'DHE-RSA-CAMELLIA128-SHA', 'DHE-DSS-CAMELLIA128-SHA', 'DH-RSA-CAMELLIA128-SHA', 'DH-DSS-CAMELLIA128-SHA', 'ECDH-RSA-AES128-GCM-SHA256', 'ECDH-ECDSA-AES128-GCM-SHA256', 'ECDH-RSA-AES128-SHA256', 'ECDH-ECDSA-AES128-SHA256', 'ECDH-RSA-AES128-SHA', 'ECDH-ECDSA-AES128-SHA', 'AES128-GCM-SHA256', 'AES128-SHA256', 'AES128-SHA', 'SEED-SHA', 'CAMELLIA128-SHA', 'IDEA-CBC-SHA', 'PSK-AES128-CBC-SHA', 'ECDHE-RSA-RC4-SHA', 'ECDHE-ECDSA-RC4-SHA', 'ECDH-RSA-RC4-SHA', 'ECDH-ECDSA-RC4-SHA', 'RC4-SHA', 'RC4-MD5', 'PSK-RC4-SHA', 'ECDHE-RSA-DES-CBC3-SHA', 'ECDHE-ECDSA-DES-CBC3-SHA', 'SRP-DSS-3DES-EDE-CBC-SHA', 'SRP-RSA-3DES-EDE-CBC-SHA', 'SRP-3DES-EDE-CBC-SHA', 'EDH-RSA-DES-CBC3-SHA', 'EDH-DSS-DES-CBC3-SHA', 'DH-RSA-DES-CBC3-SHA', 'DH-DSS-DES-CBC3-SHA', 'ECDH-RSA-DES-CBC3-SHA', 'ECDH-ECDSA-DES-CBC3-SHA', 'DES-CBC3-SHA', 'PSK-3DES-EDE-CBC-SHA', 'EDH-RSA-DES-CBC-SHA', 'EDH-DSS-DES-CBC-SHA', 'DH-RSA-DES-CBC-SHA', 'DH-DSS-DES-CBC-SHA', 'DES-CBC-SHA', 'EXP-EDH-RSA-DES-CBC-SHA', 'EXP-EDH-DSS-DES-CBC-SHA', 'EXP-DH-RSA-DES-CBC-SHA', 'EXP-DH-DSS-DES-CBC-SHA', 'EXP-DES-CBC-SHA', 'EXP-RC2-CBC-MD5', 'EXP-RC4-MD5', 'ECDHE-RSA-NULL-SHA', 'ECDHE-ECDSA-NULL-SHA', 'AECDH-NULL-SHA', 'ECDH-RSA-NULL-SHA', 'ECDH-ECDSA-NULL-SHA', 'NULL-SHA256', 'NULL-SHA', 'NULL-MD5'); $tested_ciphersuites = ssl_conn_ciphersuites($host, $ip, $port, $ciphersuites_to_test); $result["supported_ciphersuites"] = array(); foreach ($tested_ciphersuites as $key => $value) { if ($value == true) { $result["supported_ciphersuites"][] = $key; } } } else { $result["used_ciphersuite"]["name"] = $context_meta['cipher_name']; $result["used_ciphersuite"]["bits"] = $context_meta['cipher_bits']; } // tls_fallback_scsv $fallback = tls_fallback_scsv($host, $ip, $port); if ($fallback['protocol_count'] == 1) { $result["tls_fallback_scsv"] = "Only 1 protocol enabled, fallback not possible, TLS_FALLBACK_SCSV not required."; } else { if ($fallback['tls_fallback_scsv_support'] == 1) { $result["tls_fallback_scsv"] = "supported"; } else { if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:_-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { // ipv6 openssl tools are broken. (https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest) $result["warning"][] = 'TLS_FALLBACK_SCSV not tested because of <a href="https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest">bugs</a> in the OpenSSL tools and IPv6.'; } else { $result["tls_fallback_scsv"] = "unsupported"; $result["warning"][] = "TLS_FALLBACK_SCSV unsupported. Please upgrade OpenSSL to enable. This offers downgrade attack protection."; } } } //hsts $headers = server_http_headers($host, $ip, $port); if ($headers["strict-transport-security"]) { if (is_array($headers["strict-transport-security"])) { $result["strict_sransport-security"] = substr($headers["strict-transport-security"][0], 0, 50); } else { $result["strict_transport_security"] = substr($headers["strict-transport-security"], 0, 50); } } else { $result["strict_transport_security"] = 'not set'; $result["warning"][] = "HTTP Strict Transport Security not set."; } //hpkp if ($headers["public-key-pins"]) { if (is_array($headers["public-key-pins"])) { $result["public_key_pins"] = substr($headers["public-key-pins"][0], 0, 255); } else { $result["public_key_pins"] = substr($headers["public-key-pins"], 0, 255); } } else { $result["public_key_pins"] = 'not set'; } if ($headers["public-key-pins-report-only"]) { if (is_array($headers["public-key-pins-report-only"])) { $result["public_key_pins_report_only"] = substr($headers["public-key-pins-report-only"][0], 0, 255); } else { $result["public_key_pins_report_only"] = substr($headers["public-key-pins-report-only"], 0, 255); } } // ocsp stapling $stapling = ocsp_stapling($host, $ip, $port); if ($stapling["working"] == 1) { $result["ocsp_stapling"] = $stapling; } else { if (filter_var(preg_replace('/[^A-Za-z0-9\\.\\:_-]/', '', $ip), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { // ipv6 openssl tools are broken. (https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest) $result["warning"][] = 'OCSP Stapling not tested because of <a href="https://rt.openssl.org/Ticket/Display.html?id=1365&user=guest&pass=guest">bugs</a> in the OpenSSL tools and IPv6.'; } else { $result["ocsp_stapling"] = "not set"; $result["warning"][] = "OCSP Stapling not enabled."; } } $result["openssl_version"] = shell_exec("openssl version"); $result["datetime_rfc2822"] = shell_exec("date --rfc-2822"); } return $result; }