Server 报的是「我是谁、哪个版本、跑在什么系统上」。
它本来是排障用的,实际却成了给攻击者的免费情报——版本号一露,对应的 CVE 就能直接查。
这是响应首部里最不起眼、也最容易泄露信息的一个。前面几节讲的是功能,这一节讲的是副作用。
一、它的格式
产品名/版本 (注释),可以并列多个产品。
Demo 里那行 Apache/2.4.52 (Unix) OpenSSL/1.1.1n 就是三段信息叠在一起:Web 服务器、操作系统大类、
TLS 库。
括号里是注释,全凭实现填。
有的写系统名,有的写编译参数,有的干脆写主机名——见过最糟的情况是连内网 IP 都漏出来。
实测五种写法的字节数:43、29、26、13、0。从「什么都报」到「什么都不报」,长度差了三倍多。
二、它泄的到底是什么
三件事。第一,用什么软件——决定了攻击者用哪套漏洞库;第二,哪个版本——决定了具体哪几个 CVE 有效;
第三,什么环境——Unix 还是 Windows,会影响后续提权路线。
这三点加起来,等于把「扫指纹」这一步替攻击者省掉了。
正常情况下他得发一堆畸形请求去试探,现在看一眼响应就行。
所以 Server 是信息泄露里最典型的「低危但高价值」:单看它不致命,但它让后面每一步都变便宜。
三、关掉它,也要接受代价
nginx 是 server_tokens off;,Apache 是 ServerTokens Prod 加 ServerSignature Off。
改完只剩软件名,甚至整行消失。
代价是排障变难。出问题时你没法从响应看出后端是哪台、哪个版本,只能去翻访问日志或者内部监控。
所以这事得配套做——关掉之前先把审计链路补上。
最后一句实话:Server 不是安全边界。真被打了,靠的是补丁和网络隔离,不是把版本号藏起来。
它只让对手少一条线索,挡不住他。
四、动手 Demo:五种 Server 写法,量一量泄露多少
同一份正文,分别配上五种 Server 值:全报、报软件加发行版、只报产品、只报名字、完全不报。
看每一行有多少字节、暴露到哪一层。
cands = [
(u'Apache/2.4.52 (Unix) OpenSSL/1.1.1n', u'Apache 版本 + 系统 + TLS 库'),
(u'nginx/1.18.0 (Ubuntu)', u'nginx 版本 + 发行版'),
(u'Microsoft-IIS/10.0', u'IIS 版本'),
(u'nginx', u'只报软件名'),
(u'', u'完全不报'),
]
def fn_of(val):
def fn(raw, st):
hd = [(u'Content-Type', u'text/plain')]
if val:
hd.insert(0, (u'Server', val))
return X.reply(u'HTTP/1.1 200 OK', hd, TEXT)
return fn
实跑输出:
--------------------------------------------------------------------
Server:报身份,也报漏洞
--------------------------------------------------------------------
Server 值 字节 泄露信息
Apache/2.4.52 (Unix) OpenSSL/1.1.1n 43 Apache 2.4.52 + OpenSSL 1.1.1n
nginx/1.18.0 (Ubuntu) 29 nginx 1.18.0 + 发行版
Microsoft-IIS/10.0 26 IIS 10.0
nginx 13 只报软件名
(无) 0 完全不报
最上面一行 43 个字节,把好几件事全说出去了:
用什么软件、哪个版本、跑在什么系统上、连 OpenSSL 的版本都给了。
攻击者不用扫,直接照着版本号找对应的 CVE 就行。
所以常见的加固做法是改配置,只留软件名甚至全删:
nginx server_tokens off;
Apache ServerTokens Prod / ServerSignature Off
代价是排障时看不出后端是哪个——审计日志里得另找办法补上。
反过来说,`Server` 不是安全边界。真被打了,靠的是补丁和隔离,
不是把版本号藏起来。它只是「少给对手一条线索」,不是「挡住他」。
第一行整行 43 字节,把软件、版本、系统、TLS 库四层信息全给了。 第二行 29 字节,软件加版本加发行版,够用来查 CVE 了。 第三行 26 字节,只差系统名,但 IIS 本来就只有 Windows 一个平台,等于全说了。 第四行 13 字节,只剩名字;第五行 0 字节,整行都不发。 43 和 13 之间差的那 30 个字节,全是给攻击者省事的。 关掉它们只需要改一行配置,但排障能力得另外补回来。
30 秒实验
- 把第一行的 OpenSSL 版本删掉,看字节数少多少。
- 试着往括号里写一句中文,看它会不会变成乱码(提示:首部默认是 latin-1)。
- 想一想:如果完全删掉 Server,运维靠什么判断后端是哪台机器?
常见误区
- 「Server 是必填首部」——不是,可以整行不发。
- 「藏了版本号就安全了」——补丁和隔离才是防线,藏只是少给线索。
- 「所有服务器都报同样的格式」——格式只是约定,各实现自己填。
自测三问(点开对答案)
1. `Apache/2.4.52 (Unix) OpenSSL/1.1.1n` 里的括号是什么? —— 注释字段,内容由实现自定,常写系统或编译参数。 2. 为什么说它是「低危但高价值」的信息泄露? —— 单看不要命,但它省掉了攻击者扫指纹这一步,让后续每一步都变便宜。 3. nginx 里关掉版本号用哪条配置? —— `server_tokens off;`Server 说的是「我是谁」。下一个字段说的是「这份东西有几种版本」——Vary。
- ☐ (本节完,下一节 6.5.8 Vary)