HTTPS证书未校验漏洞¶
HTTPS详解¶
HTTPS实现原理¶
前面说到,HTTPS其实就是将HTTP的数据包再通过SSL/TLS加密后传输,那么SSL/TLS又是什么呢?
SSL(Secure Sockets Layer)安全套接层和TLS(Transport Layer Security)传输层安全协议其实是**一套东西**。
网景公司在1994年提出HTTPS协议时,使用的是SSL进行加密。后来IETF(Internet Engineering Task Force)互联网工程任务组将SSL进一步标准化,于1999年公布第一版TLS协议文件TLS 1.0。目前最新版的TLS协议是TLS 1.3,于2018年公布。
工作流程¶
我们先来看看HTTPS的加解密流程。

- 用户在浏览器发起HTTPS请求(如 https://www.mogu.com/),默认使用服务端的443端口进行连接;
- HTTPS需要使用一套**CA数字证书**,证书内会附带一个**公钥Pub**,而与之对应的**私钥Private**保留在服务端不公开;
- 服务端收到请求,返回配置好的包含**公钥Pub**的证书给客户端;
- 客户端收到**证书**,校验合法性,主要包括是否在有效期内、证书的域名与请求的域名是否匹配,上一级证书是否有效(递归判断,直到判断到系统内置或浏览器配置好的根证书),如果不通过,则显示HTTPS警告信息,如果通过则继续;
- 客户端生成一个用于对称加密的**随机Key**,并用证书内的**公钥Pub**进行加密,发送给服务端;
- 服务端收到**随机Key**的密文,使用与**公钥Pub**配对的**私钥Private**进行解密,得到客户端真正想发送的**随机Key**;
- 服务端使用客户端发送过来的**随机Key**对要传输的HTTP数据进行对称加密,将密文返回客户端;
- 客户端使用**随机Key**对称解密密文,得到HTTP数据明文;
- 后续HTTPS请求使用之前交换好的**随机Key**进行对称加解密。

对称加密与非对称加密¶
又是对称加密又是非对称加密,一会公钥一会私钥一会随机Key,为什么要这么复杂呢,一套搞到底不好么?
对称加密是指有一个密钥,用它可以对一段明文加密,加密之后也只能用这个密钥来解密得到明文。如果通信双方都持有密钥,且天知地知你知我知,绝对不会有别的人知道,那么通信安全自然是可以得到保证的(在**密钥足够强**的情况下)。
然而,在HTTPS的传输场景下,服务端事先并不知道客户端是谁,你也**不可能在事先不通过互联网和每一个网站的管理员都悄悄商量好一个通信密钥**出来,那么必然存在一个密钥在互联网上传输的过程,如果在传输过程中被别人监听到了,那么后续的所有加密都是无用功。
这时,我们就需要另一种神奇的加密类型,非对称加密。
非对称加密有两个密钥,一个是公钥,另一个是私钥。一般来说,公钥用来加密,这时密文只能用私钥才能解开。
那么,当客户端发起连接请求,服务端将公钥传输过去,客户端利用公钥加密好信息,再将密文发送给服务端,服务端里有私钥可以解密。
但是,当服务端要返回数据,如果用公钥加密,那么客户端并没有私钥用来解密,而如果用私钥加密,客户端虽然有公钥可以解密,但这个公钥之前就在**互联网上传输过**,很有可能已经有人拿到,并不安全,所以这一过程只用非对称加密是不能满足的。
注意,严格来讲,私钥并不能用来加密,只能用作签名使用,这是由于密码学中生成公钥私钥时对不同变量的数学要求是不同的,因此公钥私钥抵抗攻击的能力也不同,在实际使用中不可互换。签名的功能在HTTPS里也有用到,下文中会说明。
只有一组公钥私钥只能保证单程的加解密,那么如果我们准备**两组公钥私钥**呢,是不是可以解决这个问题?来看下面这个过程。
- 服务端有非对称加密的公钥A1,私钥A2;
- 客户端有非对称加密的公钥B1,私钥B2;
- 客户端向服务端发起请求,服务端将公钥A1返回给客户端;
- 浏览器收到公钥A1,将自己保存的公钥B1发送给服务端;
- 之后浏览器所有向客户端发送的数据,使用公钥B1加密,客户端可以使用私钥B2解密;
- 客户端所有向服务端发送的数据,使用公钥A1加密,服务端可以使用私钥A2解密。
此时,两条传输方向的数据都经过非对称加密,都能保证安全性,那么为什么不采用这种方案呢?

最主要的原因是**非对称加解密耗时要远大于对称加解密**,对性能有很大损耗,大家的使用体验很差。
所以,我们才最终选用了上文介绍到非对称加密+对称加密的方案,再复习一下↓↓↓
- 服务端有非对称加密的公钥A1,私钥A2;
- 客户端发起请求,服务端将公钥A1返回给客户端;
- 客户端随机生成一个对称加密的密钥K,用公钥A1加密后发送给服务端;
- 服务端收到密文后用自己的私钥A2解密,得到对称密钥K,此时完成了安全的对称密钥交换,解决了对称加密时密钥传输被人窃取的问题
- 之后双方通信都使用密钥K进行对称加解密。
看起来是一个非常完美的方案,**兼顾**了安全性和性能,但是,真的就安全了么?
CA颁发机构¶
依然考虑**中间人攻击**的情况,非对称加密的算法都是**公开**的,所有人都可以自己生成一对公钥私钥。
当服务端向客户端返回公钥A1的时候,中间人将其**替换**成自己的公钥B1传送给浏览器。
而浏览器此时一无所知,傻乎乎地使用公钥B1加密了密钥K发送出去,又被**中间人截获**,中间人利用自己的私钥B2解密,得到密钥K,再使用服务端的公钥A1加密传送给服务端,完成了通信链路,而服务端和客户端毫无感知。

出现这一问题的核心原因是**客户端无法确认收到的公钥是不是真的是服务端发来的**。为了解决这个问题,互联网引入了一个公信机构,这就是CA。
服务端在使用HTTPS前,去经过认证的CA机构申请颁发一份**数字证书**,数字证书里包含有证书持有者、证书有效期、公钥等信息,服务端将证书发送给客户端,客户端校验证书身份和要访问的网站身份确实一致后再进行后续的加密操作。
但是,如果中间人也聪明一点,只改动了证书中的公钥部分,客户端依然不能确认证书**是否被篡改**,这时我们就需要一些防伪技术了。
前面说过,非对称加密中一般公钥用来加密,私钥用来解密,虽然私钥加密理论上可行,但由于数学上的设计这么做并不适合,那么私钥就只有解密这个功能了么?
私钥除了解密外的真正用途其实还有一个,就是**数字签名**,其实就是一种防伪技术,只要有人篡改了证书,那么数字签名必然校验失败。具体过程如下
- CA机构拥有自己的一对公钥和私钥
- CA机构在颁发证书时对证书明文信息进行哈希
- 将哈希值用私钥进行**加签**,得到数字签名
明文数据和数字签名组成证书,传递给客户端。¶
- 客户端得到证书,分解成明文部分Text和数字签名Sig1
- 用CA机构的公钥进行**解签**,得到Sig2(由于CA机构是一种公信身份,因此在系统或浏览器中会内置CA机构的证书和公钥信息)
- 用证书里声明的哈希算法对明文Text部分进行哈希得到H
- 当自己计算得到的哈希值T与**解签**后的Sig2**相等**,表示证书可信,没有被篡改

这时,签名是由CA机构的私钥生成的,中间人篡改信息后无法拿到CA机构的私钥,保证了证书可信。
注意,这里有一个比较**难以理解**的地方,非对称加密的签名过程是,私钥将一段消息进行加签,然后将签名部分和消息本身一起发送给对方,收到消息后对签名部分利用公钥验签,如果验签出来的内容和消息本身一致,表明消息没有被篡改。
在这个过程中,系统或浏览器中内置的CA机构的证书和公钥成为了至关重要的环节,这也是CA机构公信身份的证明,如果系统或浏览器中没有这个CA机构,那么客户端可以不接受服务端传回的证书,显示HTTPS警告。
实际上CA机构的证书是一条信任链,A信任B,B信任C,以**掘金的证书**为例,掘金向RapidSSL申请一张证书,而RapidSSL的CA身份是由DigiCert Global根CA认证的,构成了一条信任链。
各级CA机构的私钥是绝对的私密信息,一旦CA机构的私钥泄露,其公信力就会一败涂地。之前就有过几次**CA机构私钥泄露**,引发信任危机,各大系统和浏览器只能纷纷吊销内置的对应CA的根证书。
有些老旧的网站会要求使用前下载安装他自己的根证书,这就是这个网站使用的证书并不能在系统内置的CA机构和根证书之间形成一条信任链,需要自己安装根证书来构成信任链,这里的风险就要**使用者自己承担**了。

总结¶
HTTPS 的出发点是解决HTTP明文传输时信息被篡改和监听的问题。
- 为了兼顾性能和安全性,使用了非对称加密+对称加密的方案。
- 为了保证公钥传输中不被篡改,又使用了非对称加密的数字签名功能,借助CA机构和系统根证书的机制保证了HTTPS证书的公信力。
中间人攻击¶
是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。

HTTPS在理论上是可以抵御MITM,但是由于开发过程中的编码不规范,导致https可能存在MITM攻击风险,攻击者可以解密、篡改https数据。
CA¶
如果网站只靠上图运作,可能会被中间人攻击,试想一下,在客户端和服务端中间有一个中间人,两者之间的传输对中间人来说是透明的,那么中间人完全可以获取两端之间的任何数据,然后将数据原封不动的转发给两端,由于中间人也拿到了三要素和公钥,它照样可以解密传输内容,并且还可以篡改内容。
为了确保我们的数据安全,我们还需要一个 CA 数字证书。HTTPS的传输采用的是非对称加密,一组非对称加密密钥包含公钥和私钥,通过公钥加密的内容只有私钥能够解密。上面我们看到,整个传输过程,服务器端是没有透露私钥的。而 CA 数字认证涉及到私钥,整个过程比较复杂,我也没有很深入的了解,后续有详细了解之后再补充下。
CA 认证分为三类:DV ( domain validation),OV ( organization validation),EV ( extended validation),证书申请难度从前往后递增,貌似 EV 这种不仅仅是有钱就可以申请的。
对于一般的小型网站尤其是博客,可以使用自签名证书来构建安全网络,所谓自签名证书,就是自己扮演 CA 机构,自己给自己的服务器颁发证书。
RA¶
PKI¶
是一组由硬件、软件、参与者、管理政策与流程组成的基础架构,其目的在于创造、管理、分配、使用、存储以及撤销数字证书。公钥存储在数字证书中,标准的数字证书一般由可信数字证书认证机构(CA,根证书颁发机构)签发,此证书将用户的身份跟公钥链接在一起。CA必须保证其签发的每个证书的用户身份是唯一的。
证书链¶
链接关系(证书链)通过注册和发布过程创建,取决于担保级别,链接关系可能由CA的各种软件或在人为监督下完成。PKI的确定链接关系的这一角色称为注册管理中心(RA,也称中级证书颁发机构或者中间机构)。RA确保公钥和个人身份链接,可以防抵赖。如果没有RA,CA的Root 证书遭到破坏或者泄露,由此CA颁发的其他证书就全部失去了安全性,所以现在主流的商业数字证书机构CA一般都是提供三级证书,Root 证书签发中级RA证书,由RA证书签发用户使用的证书。
www.google.com.hk 网站的证书链如下,CA证书机构是 GeoTrust Global CA,RA机构是 Google Internet Authority G2,网站的证书为 *.google.com.hk:

HTTPS通信所用到的证书由CA提供,需要在服务器中进行相应的设置才能生效。另外在我们的客户端设备中,只要访问的HTTPS的网站所用的证书是可信CA根证书签发的,如果这些CA又在浏览器或者操作系统的根信任列表中,就可以直接访问,而如12306.cn网站,它的证书是非可信CA提供的,是自己签发的,所以在用谷歌浏览器打开时,会提示“您的连接不是私密连接”,证书是非可信CA颁发的:

所以在12306.cn的网站首页会提示为了我们的购票顺利,请下载安装它的根证书,操作系统安装后,就不会再有上图的提示了。
自有数字证书的生成¶
HTTPS网站所用的证书可向可信CA机构申请,不过这一类基本上都是商业机构,申请证书需要缴费,一般是按年缴费,费用因为CA机构的不同而不同。如果只是APP与后台服务器进行HTTPS通信,可以使用openssl工具生成自签发的数字证书,可以节约费用,不过得妥善保护好证书私钥,不能泄露或者丢失。HTTPS通信所用的数字证书格式为X.509。
TLS和SSL协议分析¶
SSL/TLS是一个介于HTTP协议和TCP协议之间的可选层。
SSL(Secure Socket Layer,安全套接字层)由Netscape公司研发,用于确保数据在网络之上传输过程不会被截取,当前版本为3.0。
TLS(Transport Layer Security,传输层安全协议)则是由IETF(Internet Engineering Task Force,Internet工程任务组)纳入标准化的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本,可以理解为SSL 3.1。

SSL/TLS协议能够提供的安全目标主要包括如下几个:
- 认证性——借助数字证书认证服务器端和客户端身份,防止身份伪造
- 机密性——借助加密防止第三方窃听
- 完整性——借助消息认证码(MAC)保障数据完整性,防止消息篡改
- 重放保护——通过使用隐式序列号防止重放攻击
简单的说,SSL/TLS就是一套协议,为客户端和服务器之间通信,约定和交换密钥,提供了可靠的机制。
SSL/TLS 及其协议格式¶
SSL/TLS协议被设计为一个两阶段协议,分为握手阶段和应用阶段:
- 握手阶段也称协商阶段,在这一阶段,客户端和服务器端会认证对方身份(依赖于PKI体系,利用数字证书进行身份认证),并协商通信中使用的安全参数、密码套件以及MasterSecret。后续通信使用的所有密钥都是通过MasterSecret生成。
- 在握手阶段完成后,进入应用阶段。在应用阶段通信双方使用握手阶段协商好的密钥进行安全通信。
SSL/TLS协议有一个高度模块化的架构,分为很多子协议,如下图所示:

握手层:
- SSL握手协议:包括协商安全参数和密码套件、服务器身份认证(客户端身份认证可选)、密钥交换;
- SSL握手密钥参数更换协议:一条消息表明握手协议已经完成;
- SSL告警协议:对握手协议中一些异常的错误提醒,分为fatal和warning两个级别,fatal类型的错误会直接中断SSL链接,而warning级别的错误SSL链接仍可继续,只是会给出错误警告;
记录层:包括对消息的分段、压缩、消息认证和完整性保护、加密等。
- 数据分片
- 数据压缩记录/解压缩
- 数据加密规约/负载保护
SSL/TLS 握手过程详解¶
原理详解¶

首先得完成传输层的握手,然后进入SSL握手阶段:
1、ClientHello
客户端发送Client Hello Message,表明自己可以使用的SSL版本、以及相应参数。
- client_version:SSL版本。客户端会从高到低去尝试填入自己支持的SSL版本,例如这里就是SSLv3.0。
- random:客户端随机数。客户端的随机字符序列,用于后续协商密钥。
- session_id:本次会话ID。用于后面恢复会话。如果没有会话ID,则这里可以为空。
- cipher_suites:支持的加密套件列表。根据客户端想要使用的且自己支持的加密套件由高到低排序。
- compression_methods:压缩算法列表。根据客户端想要使用的且自己支持的压缩算法由高到低排序。可以为null,表示不要压缩。
2、ServerHello
【Server Hello】
服务器接受到ClientHello后,会返回ServerHello。服务器从客户端在ClientHello中提供的密码套件、SSL/TLS版本、压缩算法列表里选择它所支持的项,并把它的选择包含在ServerHello中告知客户端。
ServerHello中同样会包含一个随机数,同样4+28 字节类型,由服务器生成。
- server_version:服务器支持的SSL版本。例如这里就是SSLv3.0。
- random:服务器随机数。服务器端的随机字符序列,用于后续协商密钥。
- session_id:本次会话ID。如果服务器找到了客户端传过来的session_id会话,并且可以恢复会话,那么这里会填入和Client Hello相同的session_id;否则这里将填入新session_id
- cipher_suite:密钥套件。服务器支持的,且是从客户端给的列表中选的密钥套件。
- compression_method:压缩算法。服务器支持的,且是从客户端给的列表中选的压缩算法。
接下来SSL协议的建立就基于服务器选择的密码套件类型、SSL/TLS协议版本以及压缩算法。
3、Server -> Client:Certificate、Server Key Exchange、Certificate Request、ServerHello Done
【Certificate】服务器证书
和所选密钥套件中密钥交换算法(Key Exchange)匹配的,用于客户端验证服务器身份和交换密钥的X.509证书。
【Server Key Exchange】服务器密钥交换信息
如果服务器没有证书,或者服务器的证书仅用来签名(如DSS证书、签名RSA证书),或者使用的是FORTEZZA KEA密钥交换算法(现在已经不用了,不需要去了解),那么就需要发送这条消息。
【Certificate Request 】证书请求
要求客户端也发送它的证书,让服务端校验客户端的身份。
【Server Hello Done】服务器Hello阶段结束信息
一条简单的表明状态的空消息。
3、客户端校验服务端身份
【校验服务器身份】
客户端会校验服务器发过来的证书的合法性,包括:
- 证书链的可信性
- 证书是否被吊销
- 证书是否处于有效期
- 证书的域名是否和当前访问域名匹配
如果发现证书不合法,客户端可以发起告警信息。
4、Client → Server:Certificate Verify、Change Cipher Spec、Encrypted Handshake Message
【Certificate】客户端证书
如果服务器发送了“Certificate Request”,要求校验客户端身份,那么客户端需要回应自己的证书。如果客户端没有合适的证书,直接抛出告警信息让服务端处理(服务端的处理方式通常就是断开TCP连接)。和所选密钥套件中密钥交换算法(Key Exchange)匹配的,用于服务器验证客户端身份的X.509证书。
【Client Key Exchange*】客户端密钥交换信息:合法性校验通过后,客户端计算产生随机数字Pre-master,并用证书公钥加密,发送给服务器。
对于不同的密钥交换算法,密钥交换信息格式也不同:
| 密钥交换算法 | 密钥交换信息 |
|---|---|
| RSA | EncryptedPreMasterSecret |
| Diffie-Hellman | ClientDiffieHellmanPublic |
| Fortezza Kea | FortezzaKeys |
通常我们都会用RSA,而RSA的密钥交换信息就是加密的预主密钥(Encrypted Pre-Master Secret)。服务端会生成48字节的预主密钥,用服务器传过来的公钥证书加密该预主密钥。
【Certificate Verify】客户端证书校验信息
用于提供客户端证书的显示校验信息,仅在具有签名功能的客户端证书(也就是除包含固定diffie-hellman参数的证书外的所有证书)之后发送。它是采用客户端的私钥加密的一段基于已经协商的通信信息得到的数据,服务器可以用公钥解密验证。
【Change Cipher Spec】更换加密规约
此时客户端已经获得全部的计算协商密钥所需信息:两个明文随机数random_S与random_C,还有自己计算产生的Pre-master。
用于提示服务端这条连接以后都使用当前协商好的加密方式以及主密钥,是一条事件消息。
【Finish(Encrypted Handshake Message)】完成信息
当所有操作完成后,发送Finish信息。Finish信息包含了Handshake信息、主密钥的哈希散列(如SHA、MD5)数据,用于验证身份校验和密钥交换过程都成功了。Finish消息不要求收到回包,发送之后可以立刻进行加密应用数据传输。
5、服务端回应客户端
服务端同样地发送Change Cipher Spec、Finish(Encrypted Handshake Message)消息,然后开始数据传输。
TLS与SSL差别¶
| 协议 | 使用情况 |
|---|---|
| SSLv3.0以下 | 有安全问题,且已被废弃,不建议使用 |
| TLSv1.0/v1.1 | 过渡版本,不建议使用 |
| TLSv1.2 | 目前绝大多数都在使用,不知道选什么就选这个版本 |
| TLSv1.3 | 最新的更快更安全的协议,如果有条件建议一步到位 |
SSL/TLS由于使用了加密算法,非常消耗CPU资源,因此应该尽可能将SSL在LB(负载均衡,Load Balance)层终结掉。终结的意思是负载均衡器对外提供SSL连接,对内提供TCP连接,把SSL剥离掉。这样传输更快,且后端服务不需要消耗CPU资源处理SSL了(后端服务一般都是VPC的,肯定安全)。
TLS的主要增强内容
TLS的主要目标是使SSL更安全,并使协议的规范更精确和完善。TLS 在SSL v3.0 的基础上,提供了以下增强内容:
- 1.更安全的MAC算法
- 2.更严密的警报
- 3.“灰色区域”规范的更明确的定义
TLS对于安全性的改进
- 1.对于消息认证使用密钥散列法:TLS 使用“消息认证代码的密钥散列法”(HMAC),当记录在开放的网络(如因特网)上传送时,该代码确保记录不会被变更。SSLv3.0还提供键控消息认证,但HMAC比SSLv3.0使用的(消息认证代码)MAC 功能更安全。
- 2.增强的伪随机功能(PRF):PRF生成密钥数据。在TLS中,HMAC定义PRF。PRF使用两种散列算法保证其安全性。如果任一算法暴露了,只要第二种算法未暴露,则数据仍然是安全的。
- 3.改进的已完成消息验证:TLS和SSLv3.0都对两个端点提供已完成的消息,该消息认证交换的消息没有被变更。然而,TLS将此已完成消息基于PRF和HMAC值之上,这也比SSLv3.0更安全。
- 4.一致证书处理:与SSLv3.0不同,TLS试图指定必须在TLS之间实现交换的证书类型。
- 5.特定警报消息:TLS提供更多的特定和附加警报,以指示任一会话端点检测到的问题。TLS还对何时应该发送某些警报进行记录。
SSL/TLS 可以解决什么安全问题¶
TLS的基本工作方式是,客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥,然后对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取。
身份验证:通过验证CA证书,验证服务器的身份,不能造假。
信息加密:通过加密,混淆明文数据,让数据在网络中传输更加安全。
散列算法:由于散列函数不可逆,攻击者无法还原明文,同时无法篡改文件内容。
TLS和SSL证书¶
对没有加密的服务器证书应该被评估为配置错误和弱加密签名,下面是证书检查的项目列表:
使用以下方式获取目标服务器证书:
证书检查项目表
| 名称 | 描述 |
|---|---|
| 识别证书颁发者 | 确保证书颁发机构(CA)来自受信任的来源,不使用自签名证书,因为自签名证书允许中间人员攻击(除非是内部的,并且与内部CA签名)。 |
| 签名算法 | 用于确保证书完整性的算法,您应该确保密码是安全的,而不是使用MD5(已知不安全)或SHA1。 |
| 公钥 | 关键长度应该足够长以确保它不能被破解,最小值应该是2048位。 |
| Not Before | 证书开始日期。 |
| Not After | 证书结束日期。 |
| Subject&Subject Alternative Name | Subject应该列出证书所涉及的DNS名称,如果这是不正确的浏览器会引发错误。Subject Alternative Name应列出通配符证书的DNS名称,应列出此证书的所有DNS名称。 |
修复方法¶
建议升级 openssl 来进行修复,openssl-0.98m 之后的版本就已经修复了该漏洞,使用了 Secure Renegotiation。
其他测试方法¶
# 验证 ssl-cert 证书问题可以使用
nmap -sV -Pn --script ssl-cert 端口 IP
# 验证 SSL 证书的有限期可以使用
nmap -sV -Pn --script ssl-date 端口 IP
# 验证 Debian OpenSSL keys 可使用
nmap -sV -Pn --script ssl-known-key 端口 IP
# 验证 CVE-2014-0160 OpenSSL Heartbleed bug 可使用
nmap -sV -Pn --script ssl-heartbleed 端口 IP
========
# 测试单个主机上的所有内容并输出到控制台
./testssl.sh -e -E -f -p -y -Y -S -P -c -H -U TARGET-HOST
# 测试单个主机上的所有内容并输出到 HTML
./testssl.sh -e -E -f -p -y -Y -S -P -c -H -U TARGET-HOST | aha> OUTPUT-FILE.html
# 测试子网上的所有主机并输出到 HTML
./testssl.sh -e -E -f -p -y -Y -S -P -c -H -U 192.168.1.0/24 | aha> OUTPUT-FILE.html
# 与上述相同,但只列举每个服务器支持的密码类型
./testssl.sh -E 192.168.1.0/24 | aha> OUTPUT-FILE.html