1

我正在使用 OpenSSL 通过 HTTPS 连接到我的一台服务器。但是我似乎无法让服务器验证在客户端工作。据我了解,不验证证书会使我容易受到中间人攻击,但证书验证基本上是在证书中寻找 IP 地址和域名来匹配。(我说了很多错误的事情只是为了得到一些完整的详细回复:))

所以如果它是我的服务器,我知道它的域名和 IP 地址,而且我正在使用 SSL,我应该担心吗?再说一遍,中间的人不能解密我的ssl数据,插入恶意代码,重新加密,然后转发我的服务器证书吗?

最后,如果 MITM 攻击是一个问题,如果我首先使用另一个库检查证书以进行验证,然后使用 OpenSSL 而不进行验证怎么办?

还有其他可能发生的攻击吗?

4

2 回答 2

3

但是证书验证基本上是在证书里面找ip地址和域名来匹配

证书将公钥绑定到实体(个人或组织等身份)。绑定通过权威机构的签名进行。验证确保签名存在,并且提供证书的实体就是他们所说的那个人。

您识别对等方的方式是通过 DNS 名称。如果 DNS 被破坏,或者主机名检查被忽略,那么系统就会崩溃。

因此,您需要信任证书颁发机构DNS。DNS 不提供真实性保证(或者更准确地说,客户端不使用安全机制),因此您应该将 DNS 视为不受信任的输入。


但是我似乎无法让服务器验证在客户端工作。

使用 OpenSSL,您需要在客户端中做三件事。首先,您需要确保服务器提供证书。其次,您需要验证链。第三,您需要执行主机名匹配,因为 OpenSSL 不将其作为链验证的一部分。

服务器证书

您需要验证服务器是否有证书,因为某些协议和密码套件不需要证书。你可以这样做:

X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); }
if(NULL == cert) handleFailure();

链式验证

如果您有自定义验证回调,这适用,但它适用于 OpenSSL 内置的标准验证和自定义验证回调。要从链验证中获取验证结果,请执行:

long res = SSL_get_verify_result(ssl);
if(!(X509_V_OK == res)) handleFailure();

主机名验证

1.0.2 之前的 OpenSSL验证主机名。您必须从服务器的证书中提取主机名,并确保它是您访问的站点。如果你想借用代码,看看libcurl和源文件中的验证过程ssluse.c

如果要根据规范执行手动验证,请参阅 RFC 6125,在传输层安全性 (TLS) 的上下文中使用 X.509 (PKIX) 证书在 Internet 公钥基础设施中基于域的应用程序服务身份的表示和验证

为了完整起见,以下是获取Subject Alternate Names证书 (SAN) 中存在的 DNS 名称的方法。您可以X509*从像SSL_get_peer_certificate.

void print_san_name(X509* const cert)
{
    GENERAL_NAMES* names = NULL;
    unsigned char* utf8 = NULL;
    
    do
    {
        if(!cert) break; /* failed */
        
        names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0 );
        if(!names) break;
        
        int i = 0, count = sk_GENERAL_NAME_num(names);
        if(!count) break; /* failed */
        
        for( i = 0; i < count; ++i )
        {
            GENERAL_NAME* entry = sk_GENERAL_NAME_value(names, i);
            if(!entry) continue;
            
            if(GEN_DNS == entry->type)
            {
                int len1 = 0, len2 = -1;
                
                len1 = ASN1_STRING_to_UTF8(&utf8, entry->d.dNSName);
                if(!utf8) continue;
                len2 = (int)strlen((const char*)utf8);
                
                /* If there's a problem with string lengths, then     */
                /* we skip the candidate and move on to the next.     */
                /* Another policy would be to fail since it probably  */
                /* indicates the client is under attack.              */
                if(len1 != len2) {
                    fprintf(stderr, "Strlen and ASN1_STRING size do not match
                            "(embedded null?): %d vs %d\n", len2, len1);

                    /* Potential problem with the DNS name. Skip it */
                    /* TODO: test against IDNs                      */
                    OPENSSL_free(utf8), utf8 = NULL;

                    continue;
                }
                
                /* Perform matching here */
                fprintf(stdout, "  SAN: %s\n", utf8);
                
                OPENSSL_free(utf8), utf8 = NULL;
            }
            else
            {
                fprintf(stderr, "  Unknown GENERAL_NAME type: %d\n", entry->type);
            }
        }
        
    } while (0);
    
    if(names)
        GENERAL_NAMES_free(names);
    
    if(utf8)
        OPENSSL_free(utf8);    
}

在TLS Client的 OpenSSL wiki 上有一个示例程序。它涵盖了服务器证书和链验证。您必须提供主机名验证的代码。


据我了解,不验证证书会使我容易受到中间人攻击

如果您不执行验证,您不妨保存循环并选择匿名方案,如ADH.


所以如果它是我的服务器,我知道它的域名和 IP 地址,而且我正在使用 SSL,我应该担心吗?...如果 MITM 攻击是一个问题,如果我首先使用另一个库检查证书以进行验证,然后使用 OpenSSL 而不进行验证怎么办?... 是否还有其他可能发生的攻击?

它有很多,而且没有单一的答案。由于您正在构建先验了解服务器的客户端,因此请考虑使用固定方案,以便您可以放弃信任 CA 和 DNS。例如,参见 OWASP 的Certificate and Public Key Pinning

另外,请阅读 Peter Guttman 的工程安全。本书的很大一部分讨论了 PKI、SSL、TLS 和人类行为中的系统缺陷;以及如何改善您的安全状况。

于 2014-02-24T00:00:09.627 回答
1

Https基本上有两个验证:

i) 验证服务器/客户端提供的证书对彼此的信任存储。为此,您必须为服务器/客户端实例实施密钥库/信任库,并将您的证书添加到密钥库并将受信任的证书添加到信任库。这将完成证书验证。

仅供参考:密钥库和信任库

ii) 另一方面,HTTPS 将进行主机名验证以防止中间人攻击。基本上,如果 https url 中指定的 ip 与证书提供的 CommonName 匹配,则此验证方法将返回“true”。如果您知道要信任哪个 ip,则可以覆盖此验证方法。仅供参考:主机名验证

于 2014-02-23T21:39:47.433 回答