【问题标题】:How to check if domain has SSL certificate or not?如何检查域是否有 SSL 证书?
【发布时间】:2015-02-25 15:44:18
【问题描述】:

是否可以获得详细信息,例如域(例如 www.example.com)是否已准备好 HTTPS?

我需要验证一些 URL,无论它们是否具有 SSL 证书。我知道,通过使用$_SERVER['HTTPS'],我们可以检查我们的服务器详细信息。但是我怎样才能为其他域实现相同的目标。

任何帮助将不胜感激。

【问题讨论】:

  • 尝试通过任何方法连接到https://www.example.com,比如curl。
  • @deceze 我猜是这样,但我怀疑这样可以吗?我的意思是这是一种正确的检测方法吗?
  • 真的没有其他办法了。您可以通过 SSL 连接,也可以不可以。唯一知道的方法就是尝试一下。
  • 请注意,有些网站会从 https 重定向到 http。
  • 谢谢@deceze,我会调查的。

标签: php ssl https


【解决方案1】:

最后,我的代码如下:

$stream = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
$read = fopen("https://www.example.com", "rb", false, $stream);
$cont = stream_context_get_params($read);
$var = ($cont["options"]["ssl"]["peer_certificate"]);
$result = (!is_null($var)) ? true : false;

如果为域启用了 HTTPS,var_dump($var) 会给出如下所示的输出

resource(4) of type (OpenSSL X.509) 

如果不存在,则返回NULL

我检查了几个域。它似乎工作正常。我希望它会对某人有所帮助。

【讨论】:

  • 这种方法的危险在于您不知道证书是否有效。许多服务器为某些域配置了 SSL(您使用自签名证书),如果没有进行域匹配,这些服务器通常会返回第一个可用证书(以支持不支持 SNI 的客户端)。
【解决方案2】:

此功能不仅会检查域是否有 SSL 证书,还会确认证书是否与请求的域匹配。

最重要的部分是函数openssl_x509_parse,它解析证书并将所有详细信息作为数组返回。

function has_ssl( $domain ) {
    $res = false;
    $stream = @stream_context_create( array( 'ssl' => array( 'capture_peer_cert' => true ) ) );
    $socket = @stream_socket_client( 'ssl://' . $domain . ':443', $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $stream );

    // If we got a ssl certificate we check here, if the certificate domain
    // matches the website domain.
    if ( $socket ) {
        $cont = stream_context_get_params( $socket );
        $cert_ressource = $cont['options']['ssl']['peer_certificate'];
        $cert = openssl_x509_parse( $cert_ressource );

        // Expected name has format "/CN=*.yourdomain.com"
        $namepart = explode( '=', $cert['name'] );

        // We want to correctly confirm the certificate even 
        // for subdomains like "www.yourdomain.com"
        if ( count( $namepart ) == 2 ) {
            $cert_domain = trim( $namepart[1], '*. ' );
            $check_domain = substr( $domain, -strlen( $cert_domain ) );
            $res = ($cert_domain == $check_domain);
        }
    }

    return $res;
}

【讨论】:

  • 解决了一些问题,由于某种原因 $namepart 总是有 4 个元素而不是 2 个。
  • 如果证书有多个替代名称则不起作用,例如在example.com上尝试
  • 我添加了这个以使其工作:if(!$res && strpos($cert['extensions']['subjectAltName']??'',"DNS:$domain") !== false) {return true;}(它不处理 altName 是通配符的情况)
  • 我们可以通过什么方式下载证书并根据 CA 列表对其进行验证?
【解决方案3】:

这是我在 github 某处找到的不同解决方案(无法再次找到 repo...)

这与接受的答案类似,但使用 fsockopen 测试我们是否可以在端口 443 上建立 SSL 连接。如果连接被拒绝,则域没有 ssl 证书。

function has_ssl( $domain ) {
    $ssl_check = @fsockopen( 'ssl://' . $domain, 443, $errno, $errstr, 30 );
    $res = !! $ssl_check;
    if ( $ssl_check ) { fclose( $ssl_check ); }
    return $res;
}

// Test it:
print_r( has_ssl('google.com') );

【讨论】:

  • 为什么不只是function has_ssl( $domain ) { return @fsockopen( 'ssl://' . $domain, 443, $errno, $errstr, 30 ); }
  • 马特,你是对的; fclose() 行不是 OP 要求的一部分,因为它对正确的功能响应没有贡献。但我认为再次关闭远程套接字以确保它已全部清理是一个好习惯:)
【解决方案4】:

没有办法检查证书是否安全或不使用 API 单独使用 PHP(绿色挂锁)

但是你可以使用这个,它使用一个 API:https://github.com/spatie/ssl-certificate

【讨论】:

    猜你喜欢
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 2021-09-26
    • 2015-08-14
    相关资源
    最近更新 更多