键盘上那个没人知道来历的符号,是一个 IBM 工程师为了省两个字符发明的
目录
- 那个没人知道用来干嘛的字符
- 斜杠的古老血统:它是逗号的祖先
- 1959 年,一个 IBM 工程师想写布尔运算
- 从「reverse divide」到「backslash」
- 现场验证:攻击者比我更懂反斜杠
- 一个字符的 80 年漂流
那个没人知道用来干嘛的字符
键盘上有些按键,我们每天按无数次,却从没想过它们从哪来。反斜杠就是其中之一。Windows 的文件路径 C:\Users\...,正则表达式里的 \d,Markdown 里的转义,Python 字符串里的 \n——它无处不在,按下它不需要任何思考。
但如果你问:这个字符一开始是为了什么发明出来的?
几乎没人答得上来。
2025 年,有人在 Mastodon 上发了一条帖子,大意是:我朋友说反斜杠的起源不明。它被收进电传打字机字符集,IBM 又把它放进了 ASCII——但没人知道它到底是干嘛的。网上有些不可靠的说法,说它在 16 世纪的排版里用来表示换行或分段。有人能确认或证伪吗?
这正是 Shady Characters 的作者 Keith Houston 看到的那条帖子。他花了整篇文章来回答这个问题——而答案比「换行符」精彩得多。反斜杠不是从什么古老传统里继承来的,它是一个 1950 年代的 IBM 工程师,为了在一台计算机上写布尔运算,硬凑出来的字符。
斜杠的古老血统:它是逗号的祖先
先把一件事说清楚:反斜杠并没有古老的排版血统。历史上有古老血统的是正斜杠。
在中世纪和近代早期,正斜杠(/)在排版里扮演着一个重要的角色:表示停顿。它被称为 virgula 或 virgule,停顿可长可短——短到逗号,长到分段,全看写字的人心情。它甚至演变成了今天的逗号。
这张图是 William Caxton 印刷的 Virgil《Eneydos》(1490 年)的扉页,里面的 / 就是 virgule——古人在书页上用它标注断句,就像我们今天用逗号:
一个在中世纪负责「让人喘口气」的符号,几百年后演变成了计算机里无处不在的路径分隔符和注释符——/home/keith、/* comment */、https://。它的后代遍布现代生活。
而反斜杠,作为它的镜像,出生晚了整整五百年,出生地也不是修道院的抄经室,而是 IBM 的实验室。
1959 年,一个 IBM 工程师想写布尔运算
1950 年代末,IBM 正在造一台叫 Stretch 的巨型计算机——第一台 Stretch 在 1961 年交付给了洛斯阿拉莫斯国家实验室。与此同时,IBM 的一位工程师 Bob Bemer 正在做两件事:一件是推动业界建立标准的字符集(这件事最终催生了 ASCII),另一件是决定 Stretch 上到底要放哪些字符。
Bemer 遇到的问题是 ALGOL。ALGOL 是当时 ACM 钦定的标准算法语言,它支持布尔逻辑——AND、OR、NOT。问题是:布尔运算的符号(∧ 和 ∨)在现有的字符集里根本不存在。
ALGOL 的解决方案是:用两个字符拼起来表示。∧(AND)写作 /\,∨(OR)写作 \/——一个正斜杠加一个反斜杠。
于是,为了这两组逻辑运算,Bemer 在 1959 年给 ACM 的论文里引入了这个新字符。他管它叫 "reverse divide"(反向除号)——因为它就是除号 / 的镜像。后来有人嫌这名字太拗口,改叫 "reverse slant"(反向斜线)。
但你我都知道,这些名字都没活下来。
从「reverse divide」到「backslash」
1960 年代,某个匿名程序员觉得「reverse slant」和「reverse divide」都太啰嗦,给这个字符起了个更顺口的名字:backslash。
Google Ngrams 数据显示,1967 年是「backslash」这个词的拐点——从那以后,它开始横扫一切旧名字。
从此,这个为了省两个字符而生的符号,开始接管数字世界:Windows 的路径分隔、正则表达式的转义、编程语言的换行符。它成了一个字符界的「全能替补」——哪里需要「一个表示『下一个字符很特殊』的符号」,哪里就有它。
而它的宿命也埋下了。反斜杠太常被当作「转义」和「分隔」符用了,以至于它成了攻击者最爱的玩具之一。
现场验证:攻击者比我更懂反斜杠
写这篇文章的时候,我翻了翻我这台服务器的访问日志——毕竟,一个讲反斜杠历史的人,总该看看自己的机器上反斜杠正在被用来干嘛。
结果很有教育意义。
在最近几万行日志里,我找到了被真实攻击者使用的反斜杠 payload。最有名的那个,是 ThinkPHP 的反斜杠命名空间攻击:
GET /index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=Hello
这个 payload 利用了 PHP 把 \think\app 当作命名空间分隔符的特性——在 2018 年的 ThinkPHP 5.x 漏洞里,攻击者通过这个反斜杠逃逸了框架的命名空间,直接调用任意函数。快 10 年了,这套扫描还在每天跑。
还有指向 FreePBX 的 %5Cmodules%5Cendpoint(%5C 是反斜杠的 URL 编码)——用反斜杠路径去猜一个「模块/端点」接口。以及各种把反斜杠塞进 SQL 注入、XSS 和 XML 解析里的尝试。
三天里,光我们这台小服务器就记录了几十个这类请求。
这有点讽刺:一个 1959 年为了让工程师写布尔运算而发明的字符,今天最高频的用途之一,是被攻击者用来拆别人的命名空间。 它从「让 AND 和 OR 写得出来」变成了「让系统防线破得开」。一样的转义,一样的越权。
一个字符的 80 年漂流
从 1490 年 Caxton 印刷厂里表示停顿的 virgule,到 1959 年 IBM 实验室里为 ALGOL 而生的 reverse divide,再到 1967 年那个匿名程序员脱口而出的 backslash,最后到 2026 年每天扫过我服务器日志的攻击 payload——一个字符,八十年的漂流。
有意思的是,人们至今会把 URL 里的正斜杠念成「backslash」。广播广告里说"shady characters dot co dot you-kay backslash books",其实那个符号是正斜杠。大概是因为在 Windows 时代,人们看到 \ 的次数比 / 多得多,所以把短名字安到了最常见的那个符号上。
Keith Houston 在文章结尾说,反斜杠还有一个更早的双胞胎故事——但那是另一篇文章了。
回到 Modulux 那个 Mastodon 提问:他想要一个「最好带来源」的答案。他等来了一个写过一整本关于标点符号的书的人,翻出 Mackenzie 的《字符集的历史与发展》,找到 Bob Bemer 1959 年的 ACM 论文,还有他 2004 年去世前写的那篇《ASCII 是怎么得到它的反斜杠的》。Bemer 的答案很朴素:这个字符不是从什么古老传统里继承来的——它是为了在 ALGOL 里写 AND 和 OR,硬凑出来的应急字符。
然后它在接下来的七十年里,统治了全世界的键盘。
评论(0)
暂无评论,来写第一条吧~