我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
标签 #信息安全

GPG 与端到端加密:论什么才是可以信任的

本文由我去年 6 月在 LeanCloud 的一次技术分享整理而来,需要读者对公钥加密算法有基本的了解。

如果提到 GPG 那么不得不提的就是公钥加密算法,首先我们先来快速地了解一下最知名的公钥加密算法 —— RSA:在 RSA 中有「公钥」和「私钥」两种密钥,其中私钥可以导出公钥,但公钥无法反推私钥。如果用公钥加密数据的话,那么只有私钥可以解密;如果使用私钥签名数据的话,那么可以验签名。

密码学账户

顾名思义,通常我们会将公钥公开地发布,作为自己身份的代表,就好像用户名一样(虽然是一个难以记忆的随机字符串);而将私钥秘密地保存,作为身份的证明,就像密码一样。如果一个账户是由一个公钥加密算法的密钥对所保护的,那我们称之为「密码学账户」,我们身边常见的密码学账户包括:

  • SSL 证书(用于 HTTPS 等加密通讯)
  • S/MIME 证书(用于邮件加密和认证)
  • SSH 密钥(用于登录服务器的凭证)
  • Bitcoin 钱包
  • GPG ID

这些账户和我们通常注册的互联网服务很不一样,在我们通常注册的互联网服务中,你的账户实际上是由网站的维护者管理的,你为账户设置的密码并没有真的用来加密数据,而只是一种证明账户所有权的凭证,在每次登录时你向网站发送密码,来证明自己是账户的所有者。一旦你忘记了密码,网站的维护者在通过其他渠道(邮箱地址、手机号码)确认了你的身份之后,也可以帮助你重置密码。

而在密码学账户中,你拥有账户的唯一凭证就是你的密钥对 —— 通常是计算机上的一个文件,你通过使用这个密钥对进行签名来管理你的账户、进行解密来访问机密数据。密码学账户既是更加安全的(不存在具有特权的管理者),同时也是更加危险的(一旦丢失无法找回;一旦泄漏也无法重置)。

公钥交换

公钥加密算法看上去很美好,一旦我们有了对方的公钥,只需在通讯时用对方的公钥加密数据,就万无一失了,但这其中 最薄弱的一个环节就是「得到对方的公钥」,即「公钥交换」

上图是理想的情况,A 和 B 互相之间交换公钥,后续的通讯就可以以加密的方式进行了。但如果这时出现了一个能够监听和篡改 A 和 B 之间的通讯的 C,然后将自己的公钥发给 A 和 B,就变成了这样:

A 和 B 都以为自己得到了对方的公钥,但实际上他们得到的都是中间人 C 的公钥,这意味着他们之后的通讯都会以 C 的公钥来加密,C 便可以在中间继续进行窃听和篡改。

所以在理论上 不可能存在一种在线的、可靠的、不事先协商的公钥交换机制,因为交换公钥意味着双方还没有开始加密通讯,交换的过程自然没有保障。那么在实际应用中我们是如何解决公钥交换的问题的呢,我们来看一下 SSL 所使用的 X.509 证书体系:

在 X.509 中有一个被称为「证书颁发机构(CA)」的权威的第三方,CA 的数量较为有限,资格变化也并不频繁,所有浏览器(browser)都内置了 CA 的公钥。而网站方(website)在生成了自己的公钥后,需要先找 CA 对自己的公钥进行签名然后才能使用。当浏览器访问一个新的网站时,网站方需要提供经过 CA 签名的公钥,浏览器使用内置的 CA 公钥来验证签名,确保收到的网站的公钥没有被篡改过。

X.509 实际上就是要求大家信任一个权威的第三方并将它们的公钥内置在客户端中,这并不是一个完美的方案,因为在这个体系中 CA 有着非常大的权力,一旦 CA 不按照规则签发证书,那么客户端是无法察觉这种攻击行为的。

GPG

GPG 是 PGP(Pretty Good Privacy)的一个 GPL 实现,也是目前使用最广泛的实现。

「信任一个权威的第三方」对于一些去中心化爱好者是无法接受的:首先我们凭什么去信任这个第三方,其次在 CA 申领证书通常也是需要付费的。那么有没有可能去除这个权威的第三方,而允许大家互相进行认证呢?如何确认一个公钥就是属于这个人的呢?这就是我们接下来要介绍的 GPG 的信任模型:

在 GPG 的信任模型中,用户互相之间对公钥进行认证(通过用自己的私钥进行签名的方式),例如 Alice 和 Bob 是很好的朋友,要么 Alice 就会用自己私钥给 Bob 的公钥签名,然后将这个签名通过 Key Server 广播给其他人。Key Server 仅仅用来交换公钥和签名,因为签名本身是可校验的,所以 Key Server 并没有任何特权。

当另外一个 Alice 的朋友看到 Bob 的公钥,并且发现 Alice 给 Bob 的公钥签过名,那么就可以认为他的朋友 Alice 已经检查过 Bob 的公钥了,如果看到更多朋友给 Bob 签过名,那么就几乎可以认定 Bob 的身份是真实的。

所以 GPG 实际上是一个由「熟人关系」建立起的信任网络,当你认可一个人的身份,即认可这个公钥是属于这个人的,你便可以给他的公钥进行签名,形成一种信任关系,同时这种信任关系又是可以传递的。当你的 GPG 通讯录中有了一些互相信任的朋友之后,便可基于这个关系网来拓展你的朋友圈。

那么既然我们前面提到不可能有一个在线的、可靠的、不事先协商的公钥交换机制,那么这个信任网络在一开始是如何建立起来的呢?答案是使用分散的、多渠道的、可能是线下的方式来交换和确认公钥。如果使用一个固定的协议去交换公钥,而且这个协议本身没有加密保护,那么当然容易被中间人攻击,但如果两个人同时使用多种渠道来交换公钥,例如先用邮件发一次、再用微信发一次、最后再用电话说一次,那么这些渠道同时被攻击的可能性会非常小。

或者更进一步,我们可以采用线下的方式来进行确认,实际上很多技术类会议结束后可能会有一个 Key Signing Party 的活动,大家面对面地确认身份(你可能有必要出示印有照片的证件,例如身份证或护照),用纸和笔记下公钥,然后回家进行签名和上传。

最后你便得到了一些来自其他人签名,代表他们认可了你的身份,即认可了某一个 GPG 公钥代表了你。

GPG 通讯簿

因为 GPG 需要用户自己维护信任关系,因此每个 GPG 的用户都会有一个通讯簿,里面是大量的公钥(代表着一个其他用户)和签名(代表着信任关系)。

下面是我的通讯录中与我自己相关的部分:

~> gpg --list-keys jysperm
pub   4096R/E466CF1E 2014-11-23 [expires: 2017-05-17]
uid       [ultimate] Wang Ziting <jysperm@gmail.com>
uid       [ultimate] Wang Ziting <jysperm@icloud.com>
uid       [ultimate] [jpeg image of size 1476]
sub   2048R/1D795875 2014-11-23 [expires: 2017-05-17]
sub   2048R/289286B3 2014-11-23 [expires: 2017-05-17]
sub   2048R/35B5DE4D 2016-05-17 [expires: 2017-05-17]

其中 E466CF1E 是我的「根公钥」的简写形式,4096R 表示这是一个 4096 bit 的 RSA 密钥对,创建时间是 2014-11-23,有效期至 2017-05-17。一个 GPG 帐号下可以有多个 uid,我可以使用根公钥签署类似于「jysperm@gmail.com 是我的邮箱地址」的签名,来将自己的多个身份关联到一起,上面的 3 个 uid 即我的 2 个邮箱地址和一个头像图片。

我还可以使用根公钥签署一个类似于「我授权 1D795875 为我的子密钥,可以代替我进行签名等操作」的消息来添加额外的子密钥,例如上面的三个 sub 就是三个不同功能的子密钥。就像这样,其实对于 GPG 帐号的管理操作都是通过用私钥签名消息来实现的。

还可以列出与我有关的签名:

~> gpg --list-sigs jysperm
pub   4096R/E466CF1E 2014-11-23 [expires: 2017-05-17]
uid                  Wang Ziting <jysperm@gmail.com>
sig          C07CFB96 2016-05-04  paomian <qtang@leancloud.rocks>
sig 3        7CDC82A7 2015-05-11  Yeechan Lu <wz.bluesnow@gmail.com>
sig 3        E466CF1E 2016-05-17  Wang Ziting <jysperm@gmail.com>
sig          E411E711 2016-06-02  keybase.io/librazy <librazy@keybase.io>
sig          B0B002B8 2016-07-13  dennis (Dennis Zhuang) <killme2008@gmail.com>

这里看到除了我自己之外,还有其他四个人为我 jysperm@gmail.com 这个 uid 进行了签名,认可了 E466CF1E 这个公钥属于 Wang Ziting <jysperm@gmail.com> 这个人。对他人的签名也是可以区分不同的级别的,例如对于「在论坛上混了个脸属」和「每天都见面的同事」你可以给予不同级别的签名来更准确地表达信任关系

使用 GPG

前面我们介绍了 GPG 的信任模型和通讯簿,那么可以在哪些场景下使用 GPG 呢?

首先是你可以用它签名一段消息:

~> echo 'hello' | gpg --clearsign -a
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

hello
-----BEGIN PGP SIGNATURE-----

iQIcBAEBAgAGBQJXfifAAAoJELQPOvDkZs8epZAP/3/jP6k1Dev2a8i8KfY7VDfv
TVGl61kLEbgpgR3mWXFL7PaJ8SyW8N0Dv3cJhYbY8NGp8wbkZa7cUS7DkTb2ArhS
M+IKUJtwUwbfp5fOyT+esaDLWatqjSJ+5IjWX8BOnh5SnLNMURDxsYrJMShfecTD
tbBfnEkIeCFBwIfE0Xs5m23+6i7t77ZgLdn1qpWLTRNpd6Fzi0B653Kr8dREPflI
MAsn6CP90pX55V0LnsZGiAgZV+34iFolhFDd7N5mtPT/zF7OToN2SNJF3YOVikBp
M+1WJL9W8x9DwzhOq8AmPgHIEwBZVNS8Nv+UNadIZJuexR0ERl64e8MdTLft0Qui
ChjUiD7ibkLR433jcms+2EJ04xd6Ie0mp/nH5nMLY1mEgHtLMXql6VHQCbJt80Vf
ZrL2J+BF9Sk1zPh9Hn5NGe+RLX1d/CZ62rYMICRcEwiS9vpWq6m9ouSMNZUYr8S5
a/ooD6gc71t457pVgkMqjo3Auazf4PRilUsAraQZilr+8yPhciE/PX3gBL5CtKHJ
4vKH9P9RngigL6D+YyBB5vMcpXlhx9ShbH2qLr106adJ1XrCpGtSmfxygjRn3xX9
Q1dWUaELUahhcdtK6IwZ6qzyp9AESpSd+Z/bnV7jc8iC0VtMOXipVnMo7J5qyHKD
/+yt8/tzoC4+0MEzlaHJ
=aOto
-----END PGP SIGNATURE-----

上面我们先用 SHA1 对要签名的文本进行进行了一下摘要,然后用 GPG 对这个摘要进行了签名。

因为 Git 中的用户名和邮件地址其实是可以随意更改的,之前有过几次伪造 Commit 冒充大 V 的事件发生。最近 GitHub 支持了为帐号配置 GPG 公钥,然后你便可以在发布新版本的时候用 git tag -s v1.0.0 进行签名:

你可以在本地这样来验证签名,一旦签名认证通过,和正确的 GPG 帐号管理,那么这个 Commit 就不可能是伪造的:

~> git tag -v v1.1.0
object b0e42636df7394ac34f2b61c589233d0c3296d10
type commit
tag v1.1.0
tagger jysperm <jysperm@gmail.com> 1467084786 +0800

gpg: Signature made 6/28 11:33:17 2016 CST
gpg:                using RSA key B40F3AF0E466CF1E
gpg: Good signature from "Wang Ziting <jysperm@gmail.com>"

很多开源软件在发布新版本的二进制包时也会使用 GPG 进行签名,包括 Debian 的软件仓库 apt 也是通过 GPG 进行签名的。

然后你当然也可以用 GPG 加密消息:

~> echo 'hello' | gpg --encrypt -a -r jysperm
-----BEGIN PGP MESSAGE-----

hQEMAyp325QokoazAQf8DDqELYisLFSGo/9Gblr1MEabb9t3V3AcbWkA4uimpYeD
/DTWDlmxrIsvpmDDeV/1bAZ/gMc2DzhODfM4PQf8DfD+lvHwgMBhe1zSBCZlQwkj
xkP+CtF+S8xWTciaexIMQiTHLNu1tvhvCjIeR1qYJY0/E7tdKhS5iG4Jc3/oyCNN
a1m34O9GG5WJsHozGqpfKZFma50onDmQ6TnSuz4iDWrslvq3XuLRXvgOQ6DKArix
Sxnmxg1kMvlIF6AMmQRHaHpyXBoOlaX/NEsl8ESCe9w4KZFTCoFtsEB9tAwQGeGn
6Tnd7BLaxXOabqaSpoNcOlpWDlZcX89lbewAryKbY9I7AbURsuemI37bTizQUjWA
57xm4t7wTUJ/FLx22Amv1ljUa/Rq84rO8d38EQViNGyo31UmRVXy12AmBDU=
=T42S
-----END PGP MESSAGE-----

这里我们指定了我自己的公钥为收信人,可以看到不同于前面,加密消息没有明文部分,除非使用收信人的私钥,否则无法解密:

$ gpg -d < gpg-message
gpg 2048-bit RSA key, ID 289286B3, created 2016-06-28 (main key ID E466CF1E)
    Wang Ziting <jysperm@gmail.com>
hello

SSH

因为 SSH 和 GPG 都支持一样的公钥加密算法(例如 RSA),因此你也可以直接在 SSH 上使用 GPG 的密钥。

例如将你的 GPG 公钥添加到 GitHub,然后使用它来登录 GitHub:

~> export SSH_AUTH_SOCK=~/.gnupg/S.gpg-agent.ssh

~> ssh-add -l
2048 SHA256:wO93TcTQHZtltKfvS0jewFh0CMj4No6xnTegtB8FN+k

~> ssh git@github.com
Hi jysperm! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.

管理密码

有一个比较有趣的实践是你可以用 gpg 来加密你的密码,pass-store 是一个基于 GPG 和 Git 的非常简单的密码管理器,它可以用 GPG 来加密密码,然后用 Git 来进行版本控制:

~> pass find Coding
Search Terms: Coding
└── Code
    └── Coding
        └── jysperm.gpg

~> pass insert Code/Coding/jysperm

~> pass show Code/Coding/jysperm
DzizKKVIy22aHQwm

~> pass git pull --rebase
remote: Counting objects: 11, done.
remote: Total 11 (delta 1), reused 1 (delta 1), pack-reused 10
Unpacking objects: 100% (11/11), done.
From github.com:jysperm/passwords
   5026f34..e94a70f  master     -> origin/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to e94a70f8b42af5e1c13dd69246b156bbcb24a94c.

这样一来你甚至可以把密码托管在 GitHub 上:https://github.com/jysperm/passwords

KeyBase 和身份证明

再向大家介绍一个有趣的社区 —— Keybase,它就好像是 GitHub 之于 GPG 社区一样。提供了一个你的 GPG 身份的主页,上面有你的公钥、关联的 GitHub 帐号、Twitter 帐号以及所拥有的网站,他人还可以在这个网页上直接用你的公钥发送经过 GPG 加密的信息。你可以将你的私钥以加密的方式存储在 Keybase(当然不推荐这样做),Keybase 提供了在浏览器中使用密钥对进行加密、解密、签名、验签的功能。

前面我们提到在 Keybase 上你可以关联你的社交帐号,不同于一般的网站的「第三方帐号关联」,Keybase 的用户们显然都对去中心化非常在乎,那么如何去证明一个社交帐号是属于这个 GPG 公钥的呢?在这个场景中我们需要进行两方面的证明,一方面是要用 GPG 公钥去进行一个签名,声明他拥有这个社交帐号:

另一方面是用这个社交帐号将这个签名发布出来,来声明他拥有这个 GPG 公钥:

这种交叉的证明将不同的数字身份联系到了一起,我觉得这是一个挺有趣的事情,于是我在 KeyBase 之外也自己创建了一个 GPG 主页,并在上面列出了一些和其他数字身份的交叉证明,例如邮件、V2EX 帐号等。我还注册了一个我的 GPG 公钥后八位的域名来指向这个页面:http://E466CF1E.pub

端到端加密

我们再来转向这篇文章的第二个话题「端到端加密」,下图中分别展示了在无加密、SSL 加密和端到端加密的场景下,从 A 到 B、中间经过服务器的一次通讯的过程:

在没有加密的情况下,消息从 A 到服务器、再到 B 的全程都没有加密。这也意味着数据经过的链路上的任何一个节点(例如运营商的路由器)都可以查看和修改消息的内容,这种情况下的通讯安全是完全没有保证的。

在经过 SSL 加密的情况下,A 和 B 会分别在收发消息前通过 CA 签署的证书去认证服务器的身份,并协商一个用于加密数据的密钥。在从 A 到服务器,或从服务器到 B 的过程中,SSL 会保证数据不被窃听和篡改,但消息在服务器上则是以未加密的形态存在的,服务器可以查看和修改消息的内容,进行一些内容上的审查。

在端到端加密的情况下,消息在从 A 发出之前,就会利用我们前面介绍过的公钥加密技术,使用 B 的公钥进行加密,中间以加密的形式经过服务器和其他路由节点,直到 B 收到消息后,才使用自己的私钥进行解密。这种情况下的服务器并不能查看和修改消息,仅仅作为一个渠道来转发消息。

我们当然可以简单地使用 GPG 加密我们在第三方即时通讯软件上的聊天内容,就像下面这样:

但实际上例如 Tox、Line、WhatsApp、iMessage 等 IM 软件,都是默认提供了端到端加密的特性的,我们下面以 iMessage 为例去介绍一个 IM 软件是如何完成端到端加密通讯的。

iMessage 会在每台设备上生成一个 RSA 密钥对用于对数据进行加密,以及一个 ECDSA 密钥对用作签名(它出于安全性和性能的考虑使用了不同的密钥对,但其实是可以用同一个密钥对的)。这些密钥对中的公钥会被上传到 Apple IDS(Identity Services),然后其他用户会从 IDS 中取得你的所有公钥(每个设备一个公钥)。在 iMessage 中发出的所有文字消息都会使用自己的 ECDSA 私钥进行签名,使用对方的每个设备的 RSA 公钥加密一份,通过 APNs 发送给对方。对于多媒体消息,为了减少加密带来的开销,以及加密多份的流量开销,会使用一个临时密钥加密一次,通过 iCloud 进行传输,然后通过前面提到的方式发送临时密钥。苹果的很多应用都使用类似的方式进行端到端加密,包括 iCloud、Facetime、Keychain 等。

但在这个架构中,Apple IDS 依然是一个中心化的单点,它控制了全部的公钥交换过程,而且不允许用户干预。这就意味着 Apple 依然可以在 IDS 上进行中间人攻击,这也印证了我们在一开始提到的,公钥的交换是一个无法绕开的问题。在 GPG 中,用户需要繁琐地完全手动地去建立信任关系,换取了最高的去中心化程度;而在 iMessage 中 IDS 代劳了公钥交换的工作,方便了用户,但也引入了安全风险。

同时群聊也是一个比较难以解决的问题,在 Telegram 中群聊是无法开启端到端加密模式的,在 iMessage 中群聊也不总是可以使用端到端加密。这是因为就像前面提到的多设备的情况一样,在群聊的情况下我们必须用每个人的公钥来加密数据,这样数据在发送端就会膨胀许多倍(而不能发送端只发送一份,由服务器转发),具体的带宽开销会取决于群聊中的人数:

保管密钥

前面我们提到,在密码学账户中,你拥有一个账户的唯一凭证就是你的密钥对,一旦遗失,那么你便没有办法再去控制这个账户;或者一旦泄漏,其他人便拥有和你一样的控制这个账户的能力,无法重置,因此对密钥的保管显得尤为重要。

「备份」是具有两面性的 —— 一方面会让密钥更不容易丢失,但也让密钥变得更加容易泄漏。于是我就有个有趣的想法:能否做到将一个密钥分为若干「片段」保存,仅当拥有其中大部分片段的的时候才能够还原出密钥,但丢失了小部分片段又不影响还原呢?比如将密钥分为 5 份,仅当拥有其中任意三份的情况下可以还原出密钥。于是我找到了一个叫「Shamir’s Secret Sharing」的算法来做到这一点:

~> ssss-split -t 3 -n 5
Generating shares using a (3,5) scheme with dynamic security level.
Enter the secret, at most 128 ASCII characters: my secret root password
Using a 184 bit security level.
1-1c41ef496eccfbeba439714085df8437236298da8dd824
2-fbc74a03a50e14ab406c225afb5f45c40ae11976d2b665
3-fa1c3a9c6df8af0779c36de6c33f6e36e989d0e0b91309
4-468de7d6eb36674c9cf008c8e8fc8c566537ad6301eb9e
5-4756974923c0dce0a55f4774d09ca7a4865f64f56a4ee0

~> ssss-combine -t 3
Enter 3 shares separated by newlines:
Share [1/3]: 3-fa1c3a9c6df8af0779c36de6c33f6e36e989d0e0b91309
Share [2/3]: 5-4756974923c0dce0a55f4774d09ca7a4865f64f56a4ee0
Share [3/3]: 2-fbc74a03a50e14ab406c225afb5f45c40ae11976d2b665
Resulting secret: my secret root password

但将密钥作为一个文件,尤其是未加密的文件存储在电脑上仍然还是相当不安全的,因为你的电脑上安装了太多乱七八糟的软件了,目前大多数桌面操作系统其实都没有很好的权限控制 —— 几乎所有软件都可以随意地读取你所有的文件。因此 存储在硬盘上的文件是早晚要泄漏的,我们应该把密钥存储到一个无法被读取的地方,这就是我们下面要介绍的 TPM 芯片。

作为一个芯片来讲,外界必须通过已经被硬件定义好的协议去操作它,而对于 TPM 来讲,它在硬件的设计上就不允许你从芯片中取出私钥,你只能将私钥存进去,或者在 TPM 上生成密钥对。之后和私钥有关的所有运算,包括解密和签名都是在 TPM 芯片上进行的,整个过程中密钥都不会离开 TPM 芯片,即使作为硬件的 TPM 芯片被盗,他人也无法复制芯片中的私钥。

TPM 其实存在与很多笔记本和手机中,iPhone 的存储芯片和指纹识别都是依赖于 TPM 工作的。我目前在使用是的一款叫 Yubikey 的 USB TPM 芯片,它支持 GPG SmartCard 的标准,可以将 GPG 的密钥存储在其中。

小结

  • GPG 可以在互联网上,以数学为基础创造一个无法被伪造的身份,并以此身份签名信息、接收加密信息。
  • GPG 使用去中心化的信任模型,需要自行通过多种渠道来交换公钥,因此不会受制于单一的权威机构。
  • GPG 提供了身份管理和相互进行「信任签名」的机制来简化密钥的交换过程。
  • GPG 是一个开放的标准(兼容很多软件和硬件),有着活跃的社区,提供了相对易用的工具来进行公钥加密、解密、签名、验签。
  • 基于公钥加密并签名的端到端加密是从理论上保证通讯安全的唯一方法,但在此之前你需要通过某种方式来交换公钥。
  • 如果私钥丢失就只能改头换面、重新做人了。

参考来源

未来的互联网安全体系

这个博客创建于精子 7 岁第一天上学的时候,之后的十几年里精子的成长和价值观的改变是很大的。
所有的日志发布之后除了错别字都不会再修改,因此他可能并不完全同意早期日志中的观点。

前两天在撰写 RP 主机的「服务条款」,参考了几家知名的 VPS 和虚拟主机服务提供商(如 Linode)的服务条款,发现他们无一例外地对「滥发垃圾信息」非常在意。由此想到,现在很多邮件服务提供商,在以非常野蛮的方式来进行反垃圾邮件工作,例如直接封禁某些发出大量垃圾邮件的 IP, 所以 Linode 才会如此认真地保护他们的 IP 资源。

基于 IP 的反垃圾邮件策略显然是不合理的,那么让我来想象一下未来的互联网安全体系是怎样的。首先提示各位读者,阅读本文需要你对公钥加密算法有一个基本的了解。而且碰巧这里有一篇我以前写过的有关公钥加密算法的日志:http://jysperm.me/technology/1074.

我认为随着越来越多的基础服务依赖于互联网,人们会越来越重视网络安全,未来的 IP 协议(也许是 IPv7 或者一个其他的什么版本号), 应当强制对每一个数据包进行加密。网络上的每个终端都有一个密钥对,就好像 MAC 地址一样,出厂既有,当然,也可以自行更换。所有 IP 数据包都以对方的公钥来加密,同时以自己私钥来签名,这样便在 IP 层实现了端到端加密,和基于数字签名的身份鉴定。

公钥就是一个网络终端的身份证,权限认证可以直接基于公钥来进行,例如 IP 白名单会变成公钥白名单,邮件服务提供商也会依据公钥来封禁恶意的终端。甚至很多软件和网站也不再需要设置密码了,只要授权自己的设备的公钥以访问权限即可。

生成一个密钥对是十分简单的,所以这无法阻止一些恶意的用户通过生成大量的密钥对逃避封禁。因此,我认为生成一个密钥对应当是有成本的,需要支付现实世界的货币才可以。即类似于现在的 SSL 证书的模式,会有经过权威认证的证书(密钥对)颁发机构来颁发密钥对,每生成一个新的密钥对都要向颁发机构支付一定费用(例如 100 人民币), 这在全世界范围内会是一笔相当大的费用,可以考虑直接用这笔费用来维护核心路由等网络设施,于是大家接入互联网就不需要额外的费用了。

这个密钥对就是一个网络终端(通常也属于一个人或者机构)的身份证,代表着它在网络上的行为,例如发送垃圾邮件,对其他终端进行攻击等,都会被无可抵赖地记录在案。当一个密钥对的恶意行为过多时,执法机构可能会吊销这个密钥对,就如同现在吊销一个 SSL 证书一样。租用服务器的时候,提供商会要求你自行提供密钥对,服务提供商再也不必担心因为滥发垃圾信息而导致 IP 被封禁了。普通人是不需要频繁更换密钥对的,只有那些尝试破坏规则的恶意用户,才会经常被吊销密钥对。

可以想象,有些国家会要求对密钥对进行实名制管理,非实名的密钥对签名的数据包,不允许其通过骨干路由,这种方案会比模糊不清的封禁更加有效地管理互联网秩序。这样也带来了一个好处,就是密钥对会像实名制的手机卡一样,只要凭身份证明就可以方便地挂失,也不必去为各种网站和软件设置密码,因为这些帐号已经通过密钥对被绑定到你的名下了,只要通过身份证明就可以随时找回这些帐号。

多么美好的未来。

为什么我总是在黑「黑客」

和一些人从「黑客技术」入门计算机不同,我似乎从一开始就对所谓黑客技术没有什么兴趣。一开始当我刚刚听说黑客这个词的时候,因为大多数人对黑客精神的崇拜,我也对黑客这个称呼心存敬畏,但当我掌握了更多的知识,接触了更多的人和事之后,越发觉得黑客技术并不高端,黑客也不值得敬畏。在最近两年,黑客被洗白成了「白帽子」,入侵被洗白成了「信息安全」,一开始我觉得这似乎还是个好现象,但后来我觉得这其实也不见得有多少正面意义。

无论在哪里,只要提到黑客这个词,总有一批人跳出来为黑客洗白,而且会把黑客洗成「黑帽子」,「白帽子」,「骇客」,「红客」或者其他各种颜色。我觉得这样挺无趣的,有太多的词已经不是它本来的含义了,而且黑客这个词很模糊,总是被用来制造神秘感。如果是为了显示自己的专业性,介绍自己的职业,不如换成交互设计,桌面编程,代码审计,数据库运维等更具体的词。

我总是在吐槽某某知名 Web 安全界人士的编程水平不过关,当然,他们也确实不过关。会写代码是最低的标准,代码应当首先是写给人看的,顺便能在机器上运行。无论多么短小的程序,都应当考虑可读性和可维护性,并应用各种用于避免错误的技巧。对于如何提高可读性,如何避免错误,不同的人可能会有不同的观点,但是作为代码的作者,在写代码的时候至少应当考虑过这些问题。我认为绝大部分信息安全相关的工作,都需要首先是一名程序员,有过构建真正应用的经验才行。

我有这样的观点是因为我在思考一个问题,「信息安全」的最终目的是什么呢?我认为目的是为了减少程序的漏洞,但是现在业界的普遍目标是「发现更多的漏洞」。的确,发现漏洞是减少漏洞的前提,但是现在业界对于「发现漏洞」的热情实在太高了,以至于忘记了真正的目的。

比如说前一阵的 Heartbleed 事件,首先发现这个漏洞的人值得喝彩。但是随后几天,就开始有很多人在乌云网上提交各种网站没有更新 OpenSSL 的问题。在这些人看来,发现漏洞就是自己的目标,但他们从未想过如何能够减少漏洞。

我认为减少漏洞的途径应当是培养更多具有安全意识,能够写出高质量代码的程序员,专职的信息安全专家当然也要有,但至少他们应当曾经是一个「具有安全意识的程序员」。以我自己举例,我算是一个不太主流的程序员,除了写代码之外,我还花了更多时间学习新的技术,和新的知识——虽然这些知识可能我永远也不会在工作中用到。我虽然从未专门学习过有关「信息安全」的技术,但是因为我一直在研究如何编写更安全的代码,所以我对「信息安全」也算是触类旁通,甚至发现漏洞的水平也在一些人(通常被经常给黑客洗白的人称为「脚本小子」)之上。

很多没有学习过编程,而是首先开始学习信息安全的初学者,思维都很僵化。举个简单的例子,他们很难认识到「XSS」和「SQL Injection」其实是同一类攻击手法——注入 HTML 和注入 SQL. 也很难区分加密(Encryption), 编码(Encode)和散列(Hash)——因为它们似乎都是在把一个可读的字符串「加密」成一个不可读的字符串。所谓 SQL 注入不过是 SQL 的一个特例而已,如果你没有用过 SQL, 而直接学习如何注入,那你恐怕得一直按照「大牛」们总结的注入方法来实施,很难自己发现新的攻击方式,这就是我所说的,编码是 Web 安全的基础。

比如现在有些 Web 安全方面的书,在繁复地罗列一些很相似的攻击手法,并未抽象出其中的联系和规律,使读者只能照着做,而很难有所启发。更不要说这些书将安全与编码完全割裂了开来,并传达「代码只是工具,会写就行了」的思想。这样的书却被一些人奉为神作,这又印证了我之前的观点:太多人在以发现漏洞为目标,而不是减少漏洞。

最后,我的观点就是认为,直到目前,至少国内的「信息安全」的行业还处于一个非常原始的状态。当然,没有事情是完美的,我也期待这个行业能够迅速地成长起来。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2023-12-20.