第一章:Go默认TLS配置的安全隐患全景扫描
Go语言标准库的crypto/tls包在设计上追求简洁与向后兼容,但其默认TLS配置在现代安全实践中存在多处隐性风险。开发者若未显式覆盖默认值,极易在生产环境中暴露于中间人攻击、降级攻击或弱密码套件滥用等威胁之下。
默认不验证服务器证书
当使用http.Client发起HTTPS请求且未自定义Transport.TLSClientConfig时,Go不会自动校验服务器证书的有效性——除非明确设置InsecureSkipVerify: false(该字段默认为false,但若用户手动构造tls.Config却遗漏RootCAs或ServerName,仍可能绕过验证)。更危险的是,部分第三方库或示例代码会错误地将InsecureSkipVerify: true设为默认,导致全链路信任任意证书。
支持已废弃的弱密码套件
Go 1.19之前版本默认启用TLS_RSA_WITH_AES_256_CBC_SHA等已遭NIST弃用的RSA密钥交换套件。即使在Go 1.20+中,若未显式禁用,tls.Config.CipherSuites仍继承运行时内置列表,包含TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(RC4已被证实不安全)等高危组合。验证方式如下:
# 检查当前Go版本支持的默认套件(需配合openssl s_client)
go run -u main.go # 其中main.go调用 tls.Config{} 并打印 len(cfg.CipherSuites)
TLS版本协商过于宽松
默认MinVersion为tls.VersionTLS10,允许客户端接受TLS 1.0/1.1连接,而这些版本存在POODLE、BEAST等不可修复漏洞。实际部署中应强制MinVersion: tls.VersionTLS12。
证书名称验证缺失常见场景
以下配置将导致SNI匹配失败或跳过主机名检查:
| 场景 | 问题代码片段 | 风险 |
|---|---|---|
| 未设置ServerName | &tls.Config{} |
无法验证证书CN/SAN |
| ServerName为空字符串 | &tls.Config{ServerName: ""} |
触发空名称跳过验证 |
| 使用IP直连未禁用验证 | &tls.Config{ServerName: "192.168.1.1"} |
证书通常不含IP SAN,校验失败 |
正确做法是始终显式设置ServerName并确保其与目标域名一致,同时加载系统根证书:
rootCAs, _ := x509.SystemCertPool()
cfg := &tls.Config{
RootCAs: rootCAs,
ServerName: "api.example.com", // 必须与目标域名严格一致
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{ /* 显式指定安全套件 */ },
}
第二章:ServerName与SNI机制的深层剖析与攻防实践
2.1 ServerName在Go TLS握手中的实际作用与默认行为解析
ServerName 的核心职责
ServerName 是 tls.Config 中 ServerName 字段(客户端视角)或 GetConfigForClient 回调中用于匹配 SNI(Server Name Indication)的关键标识。它不参与证书验证逻辑本身,但决定客户端向服务器声明“我想连接哪个域名”。
默认行为解析
当未显式设置 ServerName 时:
- Go 客户端自动从
net.Conn的地址中提取主机名(如example.com:443→example.com); - 若地址为 IP(如
192.168.1.10:443),则ServerName保持为空字符串,不发送 SNI 扩展; - 空
ServerName可能导致虚拟主机路由失败(如 Nginx 或 Envoy 无法选择正确证书)。
实际代码示例
conf := &tls.Config{
ServerName: "api.example.com", // 显式声明,强制发送 SNI
}
conn, _ := tls.Dial("tcp", "10.0.0.5:443", conf)
此处
ServerName覆盖自动推导逻辑,确保即使连接 IP 地址,也携带api.example.com的 SNI 扩展,使服务端能返回对应域名的证书。
| 场景 | ServerName 值 | 是否发送 SNI |
|---|---|---|
tls.Dial("tcp", "foo.com:443", nil) |
"foo.com" |
✅ |
tls.Dial("tcp", "192.168.1.1:443", nil) |
""(空) |
❌ |
显式设为 "" |
"" |
❌ |
2.2 SNI缺失导致的证书误配与中间人劫持复现实验
当客户端未发送SNI(Server Name Indication)扩展时,TLS握手阶段服务器无法识别目标域名,只能返回默认虚拟主机配置的证书。
复现环境搭建
- 使用
openssl s_server启动双域名HTTPS服务(a.example.com/b.example.com) - 客户端强制禁用SNI:
openssl s_client -connect 127.0.0.1:443 -servername ""
关键验证命令
# 发送无SNI请求,观察返回证书CN
openssl s_client -connect localhost:443 -tls1_2 -ign_eof 2>/dev/null | \
openssl x509 -noout -subject
逻辑分析:
-servername ""清空SNI字段;-tls1_2确保协议兼容性;管道后提取证书主体。若返回CN=b.example.com而请求目标为a.example.com,即证实证书误配。
中间人风险路径
graph TD
A[Client] -->|No SNI| B[Reverse Proxy]
B --> C[Backend Server A]
B --> D[Backend Server B]
C -.->|Default cert served| A
| 场景 | 是否触发证书误配 | 风险等级 |
|---|---|---|
| Nginx未配置default_server | 是 | 高 |
| Apache未启用NameVirtualHost | 是 | 中 |
| Cloudflare边缘节点回源无SNI | 是 | 高 |
2.3 基于net/http和crypto/tls构建SNI感知型服务端验证框架
SNI(Server Name Indication)使单IP可承载多域名TLS服务,而crypto/tls的GetConfigForClient回调提供了在握手阶段动态选择证书的能力。
SNI路由与证书分发机制
srv := &http.Server{
Addr: ":443",
TLSConfig: &tls.Config{
GetConfigForClient: func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
// 根据SNI主机名匹配预加载的域名证书
if cfg, ok := certStore[chi.ServerName]; ok {
return cfg, nil // 返回对应域名的*tls.Config
}
return nil, errors.New("no certificate for SNI: " + chi.ServerName)
},
},
}
该回调在TLS握手初始阶段触发,chi.ServerName即客户端声明的SNI主机名;certStore为map[string]*tls.Config,每个键值对封装独立私钥、证书链及可选的客户端CA校验策略。
验证流程关键节点
- ✅ 动态证书加载(避免重启)
- ✅ 按SNI隔离TLS配置(含
ClientAuth级别) - ❌ 不支持ALPN协议协商透传(需额外扩展)
| 组件 | 作用 |
|---|---|
ClientHelloInfo |
提供SNI、支持密码套件、ALPN等元数据 |
GetConfigForClient |
实现零延迟证书路由决策点 |
graph TD
A[Client TLS Handshake] --> B{SNI Present?}
B -->|Yes| C[Invoke GetConfigForClient]
C --> D[Lookup certStore by ServerName]
D -->|Found| E[Return domain-specific tls.Config]
D -->|Not Found| F[Reject handshake]
2.4 客户端强制指定ServerName的合规写法与常见误用陷阱
合规写法:显式构造SNI扩展
现代TLS客户端必须通过ServerNameIndication(SNI)扩展明确传递目标主机名,而非依赖DNS解析结果或默认配置:
// Go net/http 客户端合规示例
tlsConfig := &tls.Config{
ServerName: "api.example.com", // ✅ 强制指定,覆盖Host头和DNS解析
NextProtos: []string{"h2", "http/1.1"},
}
client := &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsConfig},
}
ServerName字段直接注入ClientHello的SNI扩展,确保服务端能路由至正确虚拟主机。若为空,某些严格策略网关将拒绝连接(如Cloudflare Enterprise规则)。
常见误用陷阱
- ❌ 将
Host请求头误等同于SNI(HTTP层 ≠ TLS层) - ❌ 使用IP地址作为
ServerName(违反RFC 6066,导致证书校验失败) - ❌ 在多域名复用场景中复用未重置的
tls.Config
SNI合规性验证对照表
| 场景 | ServerName值 | 是否合规 | 原因 |
|---|---|---|---|
| 多租户API网关 | "tenant-a.api.prod" |
✅ | 符合FQDN规范,匹配通配符证书 |
| 内网服务直连 | "10.0.1.5" |
❌ | IP地址不被SNI标准支持,触发证书验证错误 |
graph TD
A[发起TLS握手] --> B{ServerName是否为合法FQDN?}
B -->|是| C[插入SNI扩展]
B -->|否| D[连接被TLS栈拒绝或证书校验失败]
2.5 利用Wireshark+Go testbed捕获并分析SNI明文泄露链路
SNI(Server Name Indication)在TLS 1.2及更早版本中以明文形式出现在ClientHello消息中,构成典型侧信道泄露面。为精准复现与验证,我们构建轻量级Go测试床。
构建可控HTTPS客户端
// client.go:强制指定SNI并禁用ALPN以简化握手
conn, _ := tls.Dial("tcp", "example.com:443", &tls.Config{
ServerName: "target.evil.com", // 显式注入待泄露的SNI
InsecureSkipVerify: true,
})
ServerName字段直接写入ClientHello的SNI扩展;InsecureSkipVerify跳过证书校验,确保连接快速建立,便于抓包聚焦。
抓包与过滤关键帧
在Wireshark中应用显示过滤器:
tls.handshake.type == 1 && tls.handshake.extensions_server_name
| 字段 | 值 | 含义 |
|---|---|---|
tls.handshake.type |
1 | ClientHello |
tls.handshake.extensions_server_name |
1 | SNI扩展存在 |
泄露链路可视化
graph TD
A[Go客户端发起连接] --> B[构造ClientHello]
B --> C[SNI字段明文填充]
C --> D[TCP层发送至服务端]
D --> E[Wireshark捕获原始字节]
E --> F[解析TLS扩展定位SNI]
第三章:ALPN协议协商中的隐蔽风险与加固路径
3.1 ALPN扩展在TLS 1.2/1.3中的协商逻辑与Go runtime实现差异
ALPN(Application-Layer Protocol Negotiation)是TLS层协议协商的关键扩展,但其在TLS 1.2与TLS 1.3中语义与触发时机存在本质差异。
协商阶段差异
- TLS 1.2:ALPN仅在ServerHello中单向返回服务端选定协议,客户端无回执机制;
- TLS 1.3:ALPN在EncryptedExtensions中发送,且必须与密钥交换原子绑定,禁止降级重协商。
Go runtime关键实现路径
// src/crypto/tls/handshake_server.go#L1267(Go 1.22)
if c.config.NextProtos != nil && len(c.clientAgnosticAlpnProtocols) > 0 {
c.sendALPN = true // 仅当客户端提供ALPN extension时才启用服务端响应
}
该逻辑表明:Go的TLS 1.2服务端不主动发起ALPN协商,仅响应客户端携带的application_layer_protocol_negotiation扩展;而TLS 1.3中该判断已移入encryptedExtensions构造流程,与supported_versions强耦合。
协商结果可见性对比
| 场景 | TLS 1.2 ConnectionState() |
TLS 1.3 ConnectionState() |
|---|---|---|
| 客户端未发ALPN扩展 | NegotiatedProtocol == "" |
NegotiatedProtocol == "" |
| 服务端无匹配协议 | panic(若配置了NextProtos) | 返回http/1.1(fallback) |
graph TD
A[ClientHello] -->|TLS 1.2| B[ServerHello with ALPN]
A -->|TLS 1.3| C[EncryptedExtensions with ALPN]
B --> D[应用层直接使用NegotiatedProtocol]
C --> E[须等待Finished确认后才可信]
3.2 ALPN不匹配引发的静默降级与协议指纹泄露实测
当客户端声明 ALPN 协议列表(如 h2,http/1.1),而服务端仅支持 http/1.1 且未正确拒绝不匹配,TLS 握手虽成功,却隐式降级——HTTP/2 连接被静默回退至 HTTP/1.1,且不返回 ALPN failure 警告。
触发静默降级的关键条件
- 服务端 TLS 实现忽略 ALPN 协商结果(如某些 Nginx 旧版配置
ssl_protocols TLSv1.2;但未启用http_v2) - 客户端未校验
SSL_get0_alpn_selected()返回值
实测响应差异(Wireshark 提取)
| 客户端 ALPN | 服务端支持 ALPN | 实际协商协议 | 是否暴露指纹 |
|---|---|---|---|
h2,http/1.1 |
http/1.1 |
http/1.1 |
✅(Server: nginx/1.18.0) |
h2 |
http/1.1 |
http/1.1 |
✅(无 H2 响应头,暴露降级行为) |
// OpenSSL 客户端检查 ALPN 结果示例
const unsigned char *alpn = NULL;
unsigned int alpn_len = 0;
SSL_get0_alpn_selected(ssl, &alpn, &alpn_len);
if (alpn_len == 2 && memcmp(alpn, "h2", 2) == 0) {
// 预期 h2,但服务端返回 http/1.1 → 降级发生
}
该代码在 TLS 握手后读取协商结果;若 alpn_len == 0 表示服务端未响应 ALPN(常见于降级场景),此时应主动终止连接而非继续发送 HTTP/2 帧。
graph TD
A[Client: ALPN=h2,http/1.1] --> B[Server: only http/1.1]
B --> C{ALPN extension present?}
C -->|Yes, but no match| D[Silent fallback to HTTP/1.1]
C -->|No ALPN in ServerHello| E[No ALPN negotiation → fingerprint leak]
3.3 构建ALPN策略白名单机制:从tls.Config到http.Server的全链路控制
ALPN(Application-Layer Protocol Negotiation)是TLS握手阶段协商应用层协议的关键扩展。白名单机制确保仅允许预设协议(如 h2、http/1.1)通过,阻断非法或降级协议。
核心实现路径
- 在
tls.Config中通过NextProtos显式声明白名单; http.Server自动继承并校验 ALPN 结果;- 拒绝非白名单协议的连接,在 TLS 层即中断。
白名单配置示例
tlsConfig := &tls.Config{
NextProtos: []string{"h2", "http/1.1"}, // 严格白名单顺序
MinVersion: tls.VersionTLS12,
}
NextProtos 是 TLS 层唯一可控的 ALPN 协议列表;Go 的 http.Server 会依据此列表匹配客户端 ALPN extension,不匹配则关闭连接(返回 tls: client requested unsupported application protocol)。
协议兼容性对照表
| 客户端 ALPN 请求 | 是否放行 | 原因 |
|---|---|---|
h2 |
✅ | 在白名单首位 |
http/1.1 |
✅ | 显式列入白名单 |
http/1.0 |
❌ | 未声明,被 TLS 拒绝 |
全链路控制流程
graph TD
A[Client Hello with ALPN] --> B{Server tls.Config.NextProtos 匹配?}
B -->|Yes| C[继续握手 → http.Server.Serve]
B -->|No| D[Abort TLS handshake]
第四章:三重隐患交织场景下的综合防护体系构建
4.1 复合漏洞利用链:SNI+ALPN+ServerName组合式信息泄露POC开发
TLS握手阶段的扩展字段常被忽视为信息泄露通道。SNI(Server Name Indication)、ALPN(Application-Layer Protocol Negotiation)与ClientHello中冗余的server_name字段若被服务端非对称处理,可触发缓存侧信道或日志回显。
关键交互逻辑
- SNI用于虚拟主机路由,但部分WAF/代理未校验其与ALPN协议的一致性
- ALPN协商值若被错误写入调试日志,结合SNI长度差异,可推断后端服务拓扑
- 某些CDN将
server_name重复解析两次,导致二次解析时触发DNS缓存污染日志
POC核心片段
from ssl import SSLContext, PROTOCOL_TLS_CLIENT
import socket
ctx = SSLContext(PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = socket.create_connection(("target.com", 443))
conn = ctx.wrap_socket(s, server_hostname="attacker.evil") # SNI
conn.send(b"\x16\x03\x03\x00\xdc\x01\x00\x00\xd8\x03\x03...") # 手动构造含双server_name的ClientHello
此代码强制发送含
server_name扩展与额外ServerName字段的ClientHello;server_hostname参数控制SNI值,而手动构造的ClientHello中嵌套ALPN(0x0010扩展)与重复SNI标签,用于触发目标服务解析歧义。关键参数:server_hostname影响TLS层SNI,手动载荷控制应用层解析路径。
| 扩展类型 | 协议位置 | 泄露风险载体 |
|---|---|---|
| SNI | ClientHello extension | 日志、WAF规则匹配记录 |
| ALPN | ClientHello extension | 后端协议路由日志 |
| ServerName | 自定义TLV字段(非标准) | 中间件二次解析缓存键 |
graph TD
A[ClientHello] --> B[SNI: attacker.evil]
A --> C[ALPN: h2,http/1.1]
A --> D[Custom ServerName: admin.internal]
B --> E[CDN路由决策]
C --> F[后端协议分发]
D --> G[日志系统写入异常字段]
G --> H[通过错误响应时延差异提取内部域名]
4.2 自研TLS安全检测工具tls-audit-go:集成go:embed的离线审计能力
tls-audit-go 是一款面向红蓝对抗与合规检查场景设计的轻量级 TLS 配置审计工具,核心能力在于无需网络依赖即可完成全链路 TLS 安全策略离线评估。
嵌入式规则引擎设计
借助 go:embed 将 JSON 格式的 TLS 检查规则(如弱密码套件、过期证书策略、不安全密钥交换算法)直接编译进二进制:
import _ "embed"
//go:embed rules/tls-1.2.json
var tls12Rules []byte
此处
tls12Rules在编译期注入,避免运行时读取外部文件带来的路径/权限/网络不确定性;[]byte类型便于直接json.Unmarshal解析为结构化规则集,提升启动速度与环境隔离性。
规则元数据对照表
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | CVE编号或自定义标识(如 CIPHER_SUITE_WEAK) |
severity |
int | 1–5 级风险等级 |
match |
map[string]interface{} | TLS 握手字段匹配条件 |
审计流程概览
graph TD
A[加载嵌入规则] --> B[解析目标服务X.509证书]
B --> C[提取TLS版本/扩展/签名算法]
C --> D[逐条匹配规则并打分]
D --> E[生成JSON/HTML离线报告]
4.3 生产环境TLS配置黄金模板:面向Kubernetes Ingress与gRPC-Gateway的适配方案
统一证书管理策略
使用 cert-manager 自动签发 Let’s Encrypt 通配符证书,并通过 Secret 同步至 Ingress 与 gRPC-Gateway:
# ingress-tls.yaml —— 供 Nginx Ingress 使用
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api.example.com
secretName: tls-wildcard # 引用 cert-manager 生成的 Secret
rules:
- host: api.example.com
http:
paths:
- path: /v1/
pathType: Prefix
backend:
service:
name: grpc-gateway-svc
port: {number: 8080}
此配置强制 HTTPS 重定向,并将
/v1/路径流量透传至 gRPC-Gateway。secretName必须与 cert-manager 的Certificate资源所写入的 Secret 名称一致,确保私钥与证书链原子同步。
gRPC-Gateway TLS 协同要点
gRPC-Gateway 本身不终止 TLS,依赖前端 Ingress 终止并透传 X-Forwarded-Proto: https,需在启动参数中启用:
grpc-gateway --enable-https=false --forwarded-for-header=X-Forwarded-For
| 组件 | TLS 终止位置 | HTTP 头信任要求 |
|---|---|---|
| Nginx Ingress | ✅ 边缘 | X-Forwarded-Proto |
| gRPC-Gateway | ❌ 透传 | X-Forwarded-For, X-Real-IP |
安全加固建议
- 禁用 TLS 1.0/1.1,Ingress 配置
nginx.ingress.kubernetes.io/configuration-snippet注入ssl_protocols TLSv1.2 TLSv1.3; - 所有 Secret 使用
kubeseal加密存储,避免明文泄露
4.4 基于eBPF的运行时TLS元数据监控:实时捕获异常ServerName/SNI请求
传统TLS拦截需修改应用或依赖用户态代理,延迟高且易绕过。eBPF提供内核级、无侵入的SNI提取能力,在tcp_connect和ssl_set_servername等tracepoint处动态挂载程序。
核心监控逻辑
// 在 ssl_set_servername tracepoint 中提取 SNI
SEC("tracepoint/ssl/ssl_set_servername")
int trace_ssl_sni(struct trace_event_raw_ssl_set_servername *ctx) {
char sni[256] = {};
bpf_probe_read_user_str(sni, sizeof(sni), ctx->servername);
if (sni[0] && is_suspicious_sni(sni)) { // 自定义黑白名单判断
bpf_ringbuf_output(&events, &sni, sizeof(sni), 0);
}
return 0;
}
该程序在SSL握手早期捕获原始SNI字符串;bpf_probe_read_user_str安全读取用户态内存;is_suspicious_sni()可对接YARA规则或正则引擎实现动态匹配。
异常SNI识别维度
| 维度 | 示例值 | 风险类型 |
|---|---|---|
| 长度异常 | >128字符或空 | 模糊测试/协议混淆 |
| 域名结构异常 | *.google.com\0evil.com |
SNI注入 |
| 黑名单命中 | malware-c2.xyz |
C2通信 |
数据流向
graph TD
A[内核tracepoint] --> B[eBPF程序提取SNI]
B --> C{是否匹配规则?}
C -->|是| D[RingBuffer推送]
C -->|否| E[丢弃]
D --> F[用户态go程序消费]
第五章:从防御到免疫——Go TLS安全演进路线图
Go 语言自1.0版本起内置crypto/tls包,但早期默认配置存在明显安全短板:TLS 1.0默认启用、不校验证书主机名、弱密码套件未禁用、SNI缺失导致中间人攻击面扩大。2018年Cloudflare报告指出,当时约37%的Go Web服务因InsecureSkipVerify: true硬编码而完全绕过证书验证——这不是理论风险,而是真实被利用的生产事故。
零信任证书验证模型
现代Go服务应废弃tls.Config{InsecureSkipVerify: true},转而采用基于证书透明度(CT)日志与OCSP Stapling的主动验证链。示例代码强制校验主机名并启用OCSP:
config := &tls.Config{
ServerName: "api.example.com",
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(verifiedChains) == 0 {
return errors.New("no valid certificate chain")
}
// 集成RFC 6960 OCSP响应验证逻辑
return ocsp.Validate(rawCerts[0], rawCerts[1:], time.Now())
},
}
自动化证书轮换流水线
Kubernetes集群中运行的Go微服务需对接Cert-Manager实现证书热更新。关键在于监听/tmp/tls/cert.pem文件变更事件,避免重启进程:
| 组件 | 实现方式 | 安全收益 |
|---|---|---|
| 证书监听器 | fsnotify.Watcher监控PEM文件mtime |
消除证书过期停服风险 |
| 密钥加载器 | tls.LoadX509KeyPair()动态重载 |
防止私钥硬编码泄露 |
| 连接平滑过渡 | http.Server.Close()配合Shutdown() |
避免TLS握手中断 |
QUIC加密栈集成实践
Go 1.21+原生支持crypto/tls与quic-go协同。某支付网关将TLS 1.3会话票据(SessionTicket)与QUIC连接ID绑定,实现跨协议会话恢复:
graph LR
A[客户端发起QUIC握手] --> B[服务端生成TLS 1.3 SessionTicket]
B --> C[嵌入QUIC Connection ID哈希]
C --> D[票据存储至Redis集群]
D --> E[后续请求携带票据]
E --> F[服务端验证哈希一致性]
F --> G[跳过完整TLS握手]
双向mTLS零信任网关
某金融API网关强制所有上游调用使用双向mTLS。Go服务通过ClientAuth: tls.RequireAndVerifyClientCert开启,并集成SPIFFE身份验证:
config.ClientCAs = x509.NewCertPool()
config.ClientCAs.AppendCertsFromPEM(caPEM)
config.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil { return err }
if !strings.HasPrefix(cert.Subject.CommonName, "spiffe://prod/") {
return errors.New("invalid SPIFFE ID")
}
return nil
}
密码套件策略编排
Go 1.19后支持CurvePreferences与CipherSuites精细化控制。生产环境必须禁用TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256等RSA密钥交换套件,强制使用TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384并限定曲线为P-384。某银行核心系统通过go:build标签分离开发/生产配置:
//go:build prod
package main
var secureCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
}
该方案已在2023年PCI-DSS审计中通过全部TLS专项检查。
