05-TLS只是马甲:端口选择隐蔽性与为什么不钉扎
我是黒漂技术佬。前面几篇我们把端到端加密的里子讲透了——Scrypt、HKDF、AES-256-GCM、序号抗重放,样样都扛在应用层。到这一篇,该聊聊最容易被误解的一层:TLS(传输层安全协议)。
很多人一看到「远程桌面」「加密」「TLS」「443 端口」,脑子里会自动拼出一个等式:用了 TLS + 443 = 安全。但本项目对 TLS 的定位非常克制,甚至可以说是「把它当马甲穿」。这篇就讲清楚三件事:① 端口号到底影不影响安全;② 为什么「往非标准端口发加密流量」反而更危险;③ 为什么项目宁可让 TLS 不作安全前置条件、也不强制证书钉扎。
一、先破一个迷思:端口不影响安全强度
我直接给结论,免得绕弯子:端口号不影响安全强度。安全性来自加密算法和密钥,不来自端口号。
把整个数据流向画出来就一目了然:
画面/键鼠 ──► AES-256-GCM 加密 ──► TLS ──► 走任意端口 ──► VPS 盲转 ──► 解密 ↑ 真正的安全保障在这里 ↑ 端口在这,只是个门牌号真正把内容锁死的那把锁,是应用层的 AES-256-GCM,密钥由你的 password 派生。TLS 只是在外面再套了一层「传输层加密外壳」。而端口,连外壳都算不上,它只是「这层外壳从哪个门牌号进出」——是 443 还是 8080 还是某个随机高位端口,对「锁有多结实」毫无影响。
换句话说:你把 443 换成 8080,加密强度完全一致;换成随便一个五位数端口,强度还是一致。端口改不了「被不被破解」这件事,它只影响两件完全不涉密的事——① 能不能穿公司防火墙;② 会不会被注意到。
这一点必须反复强调,因为太多教程把「用了 443」当成安全卖点来宣传,这是把「门牌号」和「门锁」混为一谈。
二、端口对比表:穿墙能力与隐蔽性
既然端口只影响「穿墙」和「显眼程度」,那就把常见候选摆出来比较。基于项目实测与协议特性,得到下面这张表(安全性一列全部「同样强」,因为前面已论证它和端口无关):
| 端口 | 穿防火墙 | 隐蔽性 | 安全性 |
|---|---|---|---|
| 443 | ★★★★★ 几乎必放行 | ★★★★★ 就是标准 HTTPS,最泯然众人 | 同样强 |
| 8443 | ★★★★ 多数放行 | ★★★★ HTTPS 备用端口,常见 | 同样强 |
| 80 | ★★★★★ 几乎必放行 | ★★★☆ 明文 HTTP 端口上跑 TLS,略怪 | 同样强 |
| 8080 | ★★★★ 多数放行 | ★★★★ HTTP 备用端口,非常常见 | 同样强 |
| 22 | ★★★ 看策略 | ★★★ SSH,可能被重点审计 | 同样强 |
| 随机高位端口 | ★☆ 常被直接拦 | ★☆ 容易被当成木马回连 | 同样强 |
怎么读这张表:
- 穿防火墙取决于公司/运营商的出口策略。443(标准 HTTPS)和 80(标准 HTTP)几乎是所有网络都会放行的「生命线端口」;而高位随机端口常常在出网策略里被一刀切拦掉。所以选 443 不是因为它「更安全」,而是因为它「最可能出得去」。
- 隐蔽性指的是「混在普通流量里像不像」。443 上本来就是海量 HTTPS 流量,你的加密连接往里一塞,几乎无法被单独挑出来;而一个随机高位端口上挂着持续加密流量,在流量审计设备眼里反而「很扎眼」。
三、反直觉点:往非标准端口发加密流量 = 木马回连特征
这是本项目最反直觉、却最实用的一条经验,必须单独拎出来说。
安全设备(比如企业网的 NDR/EDR 类系统)有一条经典的检测规则:「往非标准端口发送持续加密流量」是木马回连(C2 通信)的典型特征。
为什么?因为正常业务应用,加密流量几乎都走 443/8443 这种「该走的地方」。而恶意程序为了绕过检测,常常自己挑一个冷门高位端口悄悄回连 C2 服务器,且为了不被看出内容,流量必然是加密的。于是「冷门端口 + 持续加密」这套组合,在风控规则里几乎就是红色警报。
推论就很有意思了:你越是想「躲」,反而越容易被盯上。你把中继放在某个随机端口上,以为低调,结果在审计系统看来,你这种行为模式和木马一模一样,反而被重点关照。
反过来,用443就舒服多了——它就是标准的 HTTPS 门牌,海量正常流量都走这。你的连接混在里头,审计系统看不出任何异常特征。最低调的反而最安全(从「不被注意」的角度)。这也解释了为什么项目默认relay_ports把 443 排在第一位。
顺带澄清一个容易混淆的点:这里说的「隐蔽性」只是「不被网络审计注意」,它和「内容保密」是两回事。内容保密永远由 AES-256-GCM 负责,跟端口无关。别把「低调」误当成「加密」。
四、为什么 TLS 不作安全前置条件
前面说了,真正的安全在应用层端到端加密。那 TLS 这层外壳,到底还重不重要?项目的态度很明确:TLS 只是「穿墙的马甲」,不能把它当成安全的前置条件。这带来一个非常实用的性质,分两种公司网络情形来看:
情形 A:公司没做 TLS 中间人解密。
那最好,TLS 正常建立,里面套着 AES-GCM 密文,双层加密,安全。
情形 B:公司做了 TLS 中间人解密(很多大企业为了审计会部署)。
这时候,公司的安全设备会在你和目标服务器之间「插一脚」:它用自己的证书和你建立 TLS,再和目标建立另一条 TLS,把流量解密、审计、再加密转发。对你来说,你以为连的是 VPS,实际连的是公司的审计设备。
如果是「自签证书 + 严格钉扎」的方案(下面会讲),这种情况下 TLS 握手会因为「证书指纹对不上」直接失败——连接建立不起来,项目死在公司网络上。
而本项目因为安全不依赖 TLS,所以情形 B 下:TLS 被公司解开也无所谓,因为解出来的只是一层 AES-GCM 密文,公司依旧读不到内容。连接照样能建立、照样能用。端到端加密让「公司是否解密 TLS」变得无关紧要。
这就是「不把 TLS 当前置条件」换来的健壮性:无论公司网络怎么折腾 TLS,应用层加密都兜住了底。
五、为什么不严格钉扎:钉扎会直接死在公司网络上
顺着情形 B 往下想,就能理解项目为什么只把证书钉扎当可选项,绝不强制。
「证书钉扎(certificate pinning)」是指:客户端预先写死「我信任的服务器证书指纹」,握手后比对,不一致就断开。这能防「中间人伪造证书」。
但问题来了:公司做 TLS 中间人时,它用的就是它自己的证书,指纹和你 VPS 的自签证书必然不同。如果项目强制钉扎,那么——
公司网络做了中间人解密 → 客户端看到的证书是公司的 → 和钉扎的指纹不符 → 项目直接拒绝连接 → 你在公司电脑上连不上家里的桌面这正是项目最怕出现的结果:因为一项「为了更安全」的设计,反而让整个工具在公司网络上彻底不可用。对「在公司电脑上远程回家里」这个核心场景来说,这是致命的。
所以项目的取舍是:钉扎可选,默认留空。如果你所在网络干净(没有中间人),你可以把 VPS 自签证书的指纹填到两端的tls.pinned_fingerprint,获得更强的防伪造连接能力;但一旦遇到公司做中间人,把钉扎留空即可——应用层加密仍然保证内容安全,连接也还能建立。源码里的报错文案也明确提示了这一点:「若公司网络做 TLS 中间人解密,请把 pinned_fingerprint 留空」。
这里有个重要的安全观:不要为了「理论上更严」去引入一个会让系统在真实环境里直接失效的约束。能用、且内容安全,比「更严但连不上」有价值得多。
六、TLS 上下文配置细节
虽然 TLS 不是安全前置条件,但它的配置也得「正确且够用」,不能拖后腿。项目源码里的关键设置:
# 客户端(Agent / Viewer 侧)ctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)ctx.check_hostname=Falsectx.verify_mode=ssl.CERT_NONE# 自签证书没有 CA 可校验ifpinned_fp:ctx.minimum_version=ssl.TLSVersion.TLSv1_2# 服务端(中继侧)ctx=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)ctx.minimum_version=ssl.TLSVersion.TLSv1_2# 最低 TLS 1.2,淘汰老旧协议ctx.load_cert_chain(cert,key)几个点:
- 最低 TLSv1_2:直接把 TLS 1.0 / 1.1 这些已知有缺陷的老协议挡在门外,只接受 1.2 及以上。
- 自签证书 → CERT_NONE:因为自签证书没有可信 CA 链条可校验(没有花钱买 CA 签名,也不可能为每个纯 IP 的 VPS 搞 CA 体系),所以默认
CERT_NONE跳过证书链校验。这听起来「不校验证书」,但别慌——真正的内容机密性在应用层,TLS 这层即使被中间人解开也只是密文。证书指纹比对(若配置了钉扎)则在握手之后单独做。 - 指纹比对时机:连接建立后,从
ws.transport.get_extra_info("ssl_object")拿 DER 证书,算 SHA256,和配置里的pinned_fingerprint比对。不一致时,报错文案会同时给出「期望」和「实际」两串指纹,并提示「若公司网络做 TLS 中间人解密,请把 pinned_fingerprint 留空」。这个「给期望也给实际 + 给处理建议」的写法,比单纯报错友好太多。
七、公网 VPS 被扫描为何无害
最后聊一个很多人的心结:把中继放在公网 VPS 的 443 端口,天天被互联网上的扫描器扫,会不会很危险?
结论:被扫描本身无危害。理由一条条摆:
- 中继只接受「握手成功、且密钥校验通过」的连接。握手那 7 步(见本系列第 1 篇)不过,立刻断开。扫描器连 HELLO 都未必发对,更别提正确的
relay_token。 - 握手有
HANDSHAKE_TIMEOUT = 15.0秒超时,连上来不说话的,15 秒后自动踢。 - 即使真的被「连上」了(比如扫描器恰好发了合法 HELLO),它拿到的也只是转发密文的管道,没有
password它解不开任何内容。 - 可选加固项还有:fail2ban、握手超时(已有)、连接数限制。这些属于「锦上添花」,不是必须。
所以「公网被扫」对这套架构来说,和「你家大门口每天有人路过看一眼」差不多——门是锁着的,锁的钥匙他根本没有,看一眼毫无意义。
八、小结
把这一篇收个尾,关键认知就这几条:
- 端口不影响安全强度——安全来自算法和密钥,端口只是门牌号,只影响「穿不穿得过去」和「显不显眼」。
- 443 之所以是首选,不是因为它更安全,而是因为它最可能穿防火墙、且最不显眼、最泯然于正常 HTTPS 流量。
- 反直觉点:往非标准端口发持续加密流量,正是木马回连的典型特征;想「躲」反而更显眼,443 反而最低调。
- TLS 不作安全前置条件:公司不做中间人 → 正常加密;公司做中间人 → 解出来的也只是 AES-GCM 密文,依旧安全。
- 不强制钉扎:严格钉扎会在「公司做中间人」时直接让连接失败,项目因此把钉扎设为可选,留空即兼容两种网络。
- TLS 配置底线明确:最低 TLSv1_2、自签证书用 CERT_NONE、指纹比对在握手后做、报错给足期望/实际/建议。
- 公网被扫描无害:握手校验 + 超时 + 中继盲转密文,三重保险下「被扫」无实质风险。
把端口、TLS、钉扎这些「外层」的事想清楚之后,剩下的就是把整条连接建立过程串成一条完整链路:从「按顺序试端口」到「代理兜底」,从「HELLO 握手」到「中继只读状态透传」,前面散落的点正好在这一篇全部缝起来。