test.ssl.sh
SSL/TLS 测试靶场:20 个故意出错的 HTTPS 测试站点
test.ssl.sh 的每个子域名都只在一个地方“出问题”(或者特别新)。用浏览器打开能看到真实的报错,用 ssl.sh 检查能看到检查结果应该是什么。
怎么用
用你要测试的浏览器或 App 打开某个场景,对照下面的预期结果;需要给别人解释时,链接到对应的报错说明页。
测试检查工具本身:运行 curl ssl.sh/<场景>.test.ssl.sh。也可以拿这些地址测试自己的代码和工具:连 expired、self-signed、wrong-host 都不报错的客户端,就是没有校验证书。
curl ssl.sh/expired.test.ssl.sh
curl ssl.sh/pq-hybrid-only.test.ssl.sh
curl -I https://hsts.test.ssl.sh/
openssl s_client -connect tls-v1-0.test.ssl.sh:443 -tls1_3
证书错误
expired.test.ssl.sh
打不开- 证书
- Let's Encrypt 6 天证书(不续期)
- 服务器配置
一张 Let's Encrypt 6 天证书(
shortlived配置),只签发一次、不再续期,签发 160 小时后就过期了。- 浏览器表现
Chrome、Edge 显示
NET::ERR_CERT_DATE_INVALID;Firefox 显示SEC_ERROR_EXPIRED_CERTIFICATE;Safari 提示“此连接不是私人连接”。curl ssl.sh/expired.test.ssl.sh不通过,给出
expired:notAfter 已过,days_left为负数。- 相关
- NET::ERR_CERT_DATE_INVALIDSEC_ERROR_EXPIRED_CERTIFICATESSL 证书检查
self-signed.test.ssl.sh
打不开- 证书
- 自签名
- 服务器配置
sslsh testlab gen生成的自签名证书:签发者就是它自己,没有任何 CA 参与。- 浏览器表现
Chrome、Edge 显示
NET::ERR_CERT_AUTHORITY_INVALID;Firefox 显示MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT。curl ssl.sh/self-signed.test.ssl.sh不通过,给出
self_signed(链不可信)。直接用 curl 会报curl: (60) SSL certificate problem: self-signed certificate。- 相关
- MOZILLA_PKIX_ERROR_SELF_SIGNED_CERTNET::ERR_CERT_AUTHORITY_INVALIDcurl: (60) SSL certificate problem自签名证书
untrusted-root.test.ssl.sh
打不开- 证书
- 实验室私有 CA
- 服务器配置
叶子证书 → 中间证书 → 一个私有根证书(“ssl.sh Test Lab Untrusted Root”),这个根不在任何信任库里,也不对外发布。服务器发送叶子和中间证书。
- 浏览器表现
Chrome、Edge 显示
NET::ERR_CERT_AUTHORITY_INVALID;Firefox 显示SEC_ERROR_UNKNOWN_ISSUER。curl ssl.sh/untrusted-root.test.ssl.sh不通过,给出
untrusted_root。直接用 curl 会报curl: (60) SSL certificate problem: self-signed certificate in certificate chain或unable to get local issuer certificate。- 相关
- NET::ERR_CERT_AUTHORITY_INVALIDSEC_ERROR_UNKNOWN_ISSUERx509: certificate signed by unknown authority根证书
wrong-host.test.ssl.sh
打不开- 证书
- ssl.sh 主站证书
- 服务器配置
直接使用 ssl.sh 主站自己的有效证书,它只包含
ssl.sh和www.ssl.sh,不包含wrong-host.test.ssl.sh。- 浏览器表现
Chrome、Edge 显示
NET::ERR_CERT_COMMON_NAME_INVALID;Firefox 显示SSL_ERROR_BAD_CERT_DOMAIN。curl ssl.sh/wrong-host.test.ssl.sh不通过,给出
hostname_mismatch,SAN 列表里只有ssl.sh和www.ssl.sh。直接用 curl 会报no alternative certificate subject name matches target host name。- 相关
- NET::ERR_CERT_COMMON_NAME_INVALIDSSL_ERROR_BAD_CERT_DOMAINSAN
incomplete-chain.test.ssl.sh
看客户端- 证书
- Let's Encrypt 6 天证书
- 服务器配置
证书本身有效,但服务器只发送叶子证书(配置成
cert.pem而不是fullchain.pem),缺了中间证书。- 浏览器表现
浏览器通常能打开:Chrome、Edge、Safari 会按 AIA 地址自动下载缺的中间证书,Firefox 内置了已知中间证书。老安卓设备、App 和命令行工具会失败。
curl ssl.sh/incomplete-chain.test.ssl.sh不通过,给出
incomplete_chain。curl、OpenSSL、Python、Node.js 都会报unable to get local issuer certificate。- 相关
- 缺少中间证书curl: (60) SSL certificate problemPython CERTIFICATE_VERIFY_FAILED证书链补全
revoked.test.ssl.sh
看客户端- 证书
- Let's Encrypt 90 天证书(已吊销)
- 服务器配置
一张 Let's Encrypt 90 天证书(
classic配置,带 CRL 地址),签发后立即以 keyCompromise 理由吊销,大约每 80 天重新签发并吊销一次。- 浏览器表现
Firefox(CRLite)显示
SEC_ERROR_REVOKED_CERTIFICATE。Chrome、Edge 只有在序列号进入 Chrome 的 CRLSet 后才显示NET::ERR_CERT_REVOKED,之前照常打开。Safari 取决于苹果的吊销服务。curl ssl.sh/revoked.test.ssl.sh检查引擎查询 CA 的 CRL 时给出
revoked。直接用 curl、OpenSSL 默认不查吊销,会正常连接。- 相关
- NET::ERR_CERT_REVOKEDOCSP 与 CRLCRL
client-auth-eku.test.ssl.sh
打不开- 证书
- 实验室私有 CA
- 服务器配置
实验室 CA 签发、扩展密钥用途同时含 serverAuth 和 clientAuth 的证书:公共 CA 已在 2025—2026 年逐步停发这种“多用途”证书。
- 浏览器表现
没导入实验室根证书时:
NET::ERR_CERT_AUTHORITY_INVALID/SEC_ERROR_UNKNOWN_ISSUER。导入(带名称约束的)实验室根证书后能正常打开,浏览器不会因为多一个 clientAuth 而拒绝。curl ssl.sh/client-auth-eku.test.ssl.sh给出
client_auth_eku(公共 CA 续期时不会再给同样的 EKU),以及untrusted_root。- 相关
- unsupported certificate purpose(客户端认证 EKU 被移除)EKU客户端证书
协议版本与密钥交换
tls-v1-0.test.ssl.sh
打不开- 证书
- Let's Encrypt 6 天证书
- 服务器配置
只开 TLS 1.0(
ssl_protocols TLSv1加@SECLEVEL=0,OpenSSL 3 在默认安全级别下不允许 TLS 1.0)。- 浏览器表现
当前所有浏览器都拒绝连接:Chrome、Edge 显示
ERR_SSL_VERSION_OR_CIPHER_MISMATCH,Firefox 显示SSL_ERROR_UNSUPPORTED_VERSION。curl ssl.sh/tls-v1-0.test.ssl.sh快速检查给出
tls_handshake_failed;完整检查应显示只开了 TLS 1.0。- 相关
- ERR_SSL_VERSION_OR_CIPHER_MISMATCHERR_SSL_PROTOCOL_ERRORTLS 1.3
tls-v1-1.test.ssl.sh
打不开- 证书
- Let's Encrypt 6 天证书
- 服务器配置
只开 TLS 1.1(
ssl_protocols TLSv1.1加@SECLEVEL=0)。- 浏览器表现
和 TLS 1.0 一样:Chrome、Edge 显示
ERR_SSL_VERSION_OR_CIPHER_MISMATCH,Firefox 显示SSL_ERROR_UNSUPPORTED_VERSION。curl ssl.sh/tls-v1-1.test.ssl.sh快速检查给出
tls_handshake_failed;完整检查应显示只开了 TLS 1.1。- 相关
- ERR_SSL_VERSION_OR_CIPHER_MISMATCHERR_SSL_PROTOCOL_ERROR
tls-v1-2-only.test.ssl.sh
能打开tls-v1-3-only.test.ssl.sh
能打开- 证书
- Let's Encrypt 6 天证书
- 服务器配置
只开 TLS 1.3(
ssl_protocols TLSv1.3)。- 浏览器表现
当前浏览器都能打开。只支持到 TLS 1.2 的客户端(安卓 9 及更早、Windows 10 系统自带的 TLS 组件、较老的 Java 8)会报协议错误。
curl ssl.sh/tls-v1-3-only.test.ssl.sh通过,
tls_version为 TLSv1.3;完整检查会显示 TLS 1.2 未开启。- 相关
- TLS 1.3ERR_SSL_VERSION_OR_CIPHER_MISMATCH
pq-hybrid-only.test.ssl.sh
看客户端- 证书
- Let's Encrypt 6 天证书
- 服务器配置
只开 TLS 1.3,密钥交换组只有后量子混合算法 X25519MLKEM768(
ssl_ecdh_curve X25519MLKEM768;nginx 必须链接 OpenSSL 3.5 及以上)。- 浏览器表现
较新的 Chrome、Edge、Firefox 和 Safari 26 默认会发送 X25519MLKEM768 密钥份额,可以连上。老浏览器和多数 HTTP 库握手失败(Chrome 显示
ERR_SSL_VERSION_OR_CIPHER_MISMATCH)。curl ssl.sh/pq-hybrid-only.test.ssl.sh检查引擎的 OpenSSL 是 3.5+ 时通过,
key_exchange_group为 X25519MLKEM768;OpenSSL 更老时给出tls_handshake_failed。- 相关
- X25519MLKEM768后量子 TLSML-KEM
HTTP 安全头与页面内容
密钥类型与证书有效期
long-validity.test.ssl.sh
打不开- 证书
- 实验室私有 CA
- 服务器配置
实验室 CA 签发、有效期 3650 天的证书。公共可信证书自 2026 年 3 月 15 日起最长 200 天(CA/B 论坛 SC-081 投票),2029 年降到 47 天。
- 浏览器表现
没导入实验室根证书时:
NET::ERR_CERT_AUTHORITY_INVALID。导入后 Chrome、Firefox 能打开(有效期上限只针对公共根),Safari 等苹果客户端仍会拒绝:苹果对有效期超过 825 天的 TLS 证书一律不认,用户自己安装的根也一样。curl ssl.sh/long-validity.test.ssl.sh不通过,给出
untrusted_root;validity_days为 3650,远超公共证书 200 天的上限。- 相关
- 证书有效期时间线SC-081v347 天证书有效期计算器
short-lived-6-days.test.ssl.sh
能打开- 证书
- Let's Encrypt 6 天证书
- 服务器配置
Let's Encrypt
shortlived证书:有效期 160 小时(约 6.7 天),每 2~3 天自动续期。- 浏览器表现
正常打开。
curl ssl.sh/short-lived-6-days.test.ssl.sh通过;
validity_days为 7(160 小时向上取整),days_left在 0~6 之间。剩余天数少在这里是正常的,不是续期失败。- 相关
- 47 天证书ARILet's Encrypt 时间线
ecc-p256.test.ssl.sh
能打开rsa-4096.test.ssl.sh
能打开客户端证书
mtls-required.test.ssl.sh
打不开- 证书
- Let's Encrypt 6 天证书
- 服务器配置
服务器要求客户端出示实验室客户端 CA 签发的证书(
ssl_verify_client on)。测试用客户端证书放在 test.ssl.sh/downloads/。- 浏览器表现
不带客户端证书时,nginx 返回“400 Bad Request — No required SSL certificate was sent”。导入
client.p12(密码ssl.sh)后,浏览器会让你选择证书,然后正常打开。curl ssl.sh/mtls-required.test.ssl.sh服务器证书检查通过。不带客户端证书的 HTTP 请求得到 400;
curl --cert client.pem --key client.key https://mtls-required.test.ssl.sh/得到 200。- 相关
- 双向 TLS(mTLS)mTLS客户端证书
下载
下面这些文件放在 https://test.ssl.sh/downloads/,是故意公开的测试材料,不要用在任何正式环境。
- ssl.sh-test-lab-root.pem:实验室根证书,带名称约束,只能为
test.ssl.sh下的域名签发。临时导入后可以看到client-auth-eku、long-validity在“受信任”时的表现,测完请删除。 - client.p12(密码
ssl.sh)、client.pem、client.key:访问mtls-required用的测试客户端证书。 - ssl.sh-test-lab-client-ca.pem:签发上面客户端证书的 CA。
没有做的场景
有些经典测试项目,用现在的 OpenSSL 3 已经做不出来,或者做出来和别的场景报错完全一样。
- RC4
RFC 7465 自 2015 年起禁止在 TLS 中使用 RC4,各浏览器 2016 年已全部移除。OpenSSL 3 只在 legacy provider 里保留 RC4,标准编译的 nginx 无法提供;就算做出来,浏览器里看到的也和
tls-v1-0一样是加密套件不匹配。- RSA-1024
公共 CA 十多年前就不签 1024 位 RSA 证书了,大多数发行版自带的 OpenSSL 3 安全级别下 nginx 连这种密钥都加载不了。用私有 CA 做出来,浏览器只会显示“签发者不受信任”,和
untrusted-root重复。- SSLv3 / SSLv2
POODLE(2014 年)之后各浏览器移除了 SSLv3;OpenSSL 1.1.0 起已经没有 SSLv2,发行版编译时也关掉了 SSLv3。
- SHA-1 signatures
浏览器 2017 年起不再信任 SHA-1 证书,OpenSSL 3 默认安全级别也拒绝 SHA-1 签名;用私有 CA 做出来同样只会显示“签发者不受信任”。
靶场是怎么搭的
所有场景跑在同一台 nginx 上。证书类场景只是发送的证书不同;TLS 版本、密钥交换组和客户端证书这类握手参数,nginx 在知道访问的是哪个域名之前就已经确定了,所以这几个场景各自独占一个监听端口,由 443 端口上的 SNI 分流(ssl_preread)转过去。
需要公共可信的场景用 Let's Encrypt 的 6 天短期证书,保证它们只因为想演示的那一个原因出错;私有证书由 sslsh testlab gen 生成。
ssl.sh 主站的 HSTS 故意不带 includeSubDomains,所以访问过 ssl.sh 不会影响 *.test.ssl.sh 的场景:证书错误场景照样有“继续访问”按钮,no-hsts 和 mixed-content 里的 HTTP 请求也不会因为主站的 HSTS 被升级。