这是一个创建于 814 天前的主题,其中的信息可能已经有所发展或是发生改变。

最近在看安全方面的知识,为了防止重放攻击,我查阅了一些资料。

also24 的回复说得非常棒。

单向的 https 只能保证你请求的银行真的是银行,但银行无法确定你是你。

加了 token 才能让银行知道你是你,但是银行不知道你带的东西是你自己带的,还是别人塞进你包里的。

加上 nonce 和签名,才能让银行知道这些东西全都是你带的,别人没有夹带,但是银行不知道这个你,是否是用 1 年前的你克隆出来的。

加上时间戳,银行才能确定面前的你确实是当前时间点真实存在你,东西也是你带的东西。

最后,稀奇古怪的加密又是什么呢? 答:你带的东西是一个保险箱~


但是我有一个疑问,产生签名是需要密钥的,那怎么安全地在 web 前端存储私钥呢?

46 条回复    2021-07-04 22:38:40 +08:00

1

janus77      2021-07-03 23:20:02 +08:00

浏览器的环境实现吧,浏览器这个软件本身是利用了操作系统的功能,和硬件做了交互。

2

JasonLaw      2021-07-03 23:25:00 +08:00 via iPhone

@janus77 #1 可以更加具体一点、使用外行人的语言描述吗?

3

xmumiffy      2021-07-03 23:36:03 +08:00 via Android

这种都是服务端间的交互方式,密钥直接写在代码里的。
要认证客户端可以使用双向证书

4

FaiChou      2021-07-03 23:38:29 +08:00 via iPhone  
2

你是想在网页端做 ssl pinning ?

前端私钥怎么来的?服务器给的?服务器给的时候就可能被窃取。

安全存储了私钥,你得用代码获取它吧,你的代码能获取,怎么保证其他代码不能获取?

前端的东西都是透明的,不像 app 客户端可以封在包里,不过封在包里也可以被越狱 /root 来篡改。楼主想要的绝对安全的地方,不存在的。

5

hahasong      2021-07-03 23:40:48 +08:00

jwt 就行,已经标准化了

6

JasonLaw      2021-07-03 23:54:04 +08:00 via iPhone

@FaiChou #4 很多人都说 “使用签名来保证来源的可靠性等等”,那它们是怎么处理私钥的?

7

JasonLaw      2021-07-03 23:56:05 +08:00 via iPhone

@hahasong #5 JWT 中的签名是由服务端存储的密钥产生的,跟我的提问有啥关系呢?

8

IvanLi127      2021-07-04 00:06:25 +08:00 via Android

我觉得每次都输入密码,用密码签名所有参数和时间戳,就能保证收到的东西就是你现在发送的。这个密码存可信的地方才行,你相信浏览器,那就能安全存储了。如果不相信自己,每次输入密码都没用

9

walpurgis      2021-07-04 00:16:12 +08:00

带签名的接口一般都是给服务端调用的,原帖没有任何地方提到前端

10

JasonLaw      2021-07-04 00:21:28 +08:00 via iPhone

@IvanLi127 #8 问题就是 “怎么安全地在浏览器存储密码”😅

11

binux      2021-07-04 00:23:06 +08:00 via Android

浏览器安全是由浏览器保证的,你作为前端无论做什么都是没用的

12

JasonLaw      2021-07-04 00:25:49 +08:00 via iPhone

@walpurgis #9 是的,一般都是服务端请求服务端,原帖没有提到前端。但我的问题是关于浏览器端请求服务端。

13

SP00F      2021-07-04 00:29:14 +08:00

前端是防小人,不防君子。

都不信任,那就只能走一律不信的态度了

14

3dwelcome      2021-07-04 00:41:59 +08:00

"怎么安全地在 web 前端存储私钥?"
以前貌似我也在 V 站讨论过这问题,结论是普通加密保存就可以了。

但是有两点额外要素,第一是解密后私钥只确保使用一次后就失效。第二是每次用代码生成不一样的加密算法,就是前端存的不是私钥的解密密码,而是生成的解密代码虚拟机本身,比如 WEBASM 。

15

also24      2021-07-04 00:50:07 +08:00  
7

感谢认可之前的回答,本身不是专门做安全的,可能还有很多纰漏。

具体到本帖的问题,我觉得不妨先定义一下『安全的存储』指的是什么。

首先,『安全的存储』可能是指:让这个私钥不出现在前端代码里。
那可以在用户的登录鉴权的接口中,增加一个字段返回,给每个用户自己专属的私钥。

按这个思路,为了复用这个私钥,需要将其存储在本地,可选 Cookie 或 localStorage 来存储。
此时『安全的存储』可能是指:让这个私钥不被本站之外的其它恶意站点获取。
- 使用 Cookie 方式存储时,正确设置 domain / path / secure 等字段。
- 使用 localStorage 方式存储时,默认只有同源页面可读写。

考虑到 Cookie 会被自动发送,相对来说更建议使用 localStorage 来存储私钥。

但是有些机智的朋友就说了,我打开 DevTools,不就直接看光光了么?
此时『安全的存储』可能是指:不让用户在 DevTools 中看到,或者手动运行 JS 代码获取到。

那就要请出第三方存储了,例如 U 盾等专门存储密钥的东东就是专门干这个的。
使用 U 盾还有一个好处,就是不需要登录鉴权就可以有私钥了,可以同时保护登录接口的安全。

那有些更机智的朋友肯定又想到了,那我直接破解你的 U 盾,导出你的私钥,不又看光光了么?
此时『安全的存储』就愈发变态了,还要考虑到硬件设备被破解的情况。
那可能就要搬出更极端的存储设备了 - 全靠大脑,直接硬背。

那有些搞催眠的朋友……………………

16

jadec0der      2021-07-04 02:12:58 +08:00

银行? U 盾啊

17

unco020511      2021-07-04 02:27:26 +08:00

--- 单向的 https 只能保证你请求的银行真的是银行,但银行无法确定你是你。
这句话很明显就是错误的, 而且起码两处错误
1. 什么叫单向 https?https 的过程是先由客户端和服务端通过 [非对称加密]+[签名] 机制协商出一套[对称加密秘钥], 然后使用这套对称秘钥进行加密传输, 双方数据都是使用对称加密传输的, 何来单向一说?

2. 只能保证你请求的是银行, 但银行无法确定你是你?
1.https 非对称加密过程中, 客户端会使用自己的私钥加密原文 (的 hash) 得到签名并发送, 服务端会使用对应的公钥解密得到原文的 hash 并比对
2. 同时也会校验 host 是不是客户端域名 (或 ip), 银行能确定你是你
3. 如果说的是抓包 / 控制台 F12, 那与 https 安全性无关, 属于你自己主动安装第三方证书

18

unco020511      2021-07-04 02:33:40 +08:00

如果你问的是 oath2, 那前端怎么会存储私钥呢

19

xuanbg      2021-07-04 04:57:30 +08:00  
1

所以银行会发你一个 usbKey 来存储证书啊。就是因为浏览器干不来这个事情,客户端任何常规存储方式都是不安全的。

20

JasonLaw      2021-07-04 07:27:18 +08:00 via iPhone

22

FaiChou      2021-07-04 08:45:42 +08:00 via iPhone

@JasonLaw 你想了解的是:对称加密和非对称加密。去搜搜这相关的知识吧。元知识需要学的,不靠问。

23

mxT52CRuqR6o5      2021-07-04 09:23:14 +08:00 via Android

私钥不私的话破坏了很多假设,安全性就无法保证

24

ZeawinL      2021-07-04 10:06:12 +08:00 via Android

前端使用用户密码 hash 作为密钥?

25

evilStart      2021-07-04 10:29:52 +08:00 via Android

@FaiChou 该学习的是你吧。明明是公钥加密,私钥解密。要是能用公钥解密那这加密还有啥意义。

26

vibbow      2021-07-04 10:39:24 +08:00

U 盾就是干这个事情的呀

27

unco020511      2021-07-04 10:43:31 +08:00

@JasonLaw 非对称加密除了加密外, 还有一个应用叫做数字签名, 完整的非对称加密流程包括: 对原文非对称加密 + 签名, 也就是实际是将密文 (公钥加密的)+ 签名(实际为 hash 后用私钥进行一次非对称加密运算得到) 一起打包发送, 这样做既保证了数据不会被中间方窃听, 又保证了数据的完整性以及来源

28

JasonLaw      2021-07-04 10:43:36 +08:00 via iPhone

@vibbow #26 前面已经有很多人说了这个了,如果没有其它想法的话,最好不要重复同样的东西。如果冒犯到你的话,我先说句抱歉,我只是不想让主题包含太多没意义的信息。

30

BoringBB      2021-07-04 10:50:21 +08:00 via Android

使用客户端证书啊,在 ssl 握手的时候,服务器可以要求客户端提供证书来验证身份

31

geniussoft      2021-07-04 10:56:34 +08:00

@also24 啥? U 蹲被破解?

以后开通网上银行,要求用户默背 2048 位密钥,并且熟练掌握口算非对称加密,绝对不能动笔😂

32

also24      2021-07-04 10:57:02 +08:00

@unco020511 #17
看内容要参照上下文,不要直接臆测某句话的意思。

『单向的 https 』 指的是 https 只有『单向加密』么?
当然不是,参照上下文可以得出,此处指的是最常使用的,只有『单向认证』的 https,在后面的楼层中,我专门强调了 https 是支持『双向认证』的。

那『加密』和『认证』有什么区别呢?
加密只是用来保证数据在传输的过程中,其它人无法解码。
认证则是为了证明自己是自己。

在最常见的『单向认证』的 https 使用形式中,只对服务器端进行了『认证』。
客户端内置有可信根证书机构的证书信息,服务端持有根证书签发的中间证书签发的服务器证书(大部分场景下,还包含了相应的证书链信息),客户端通过验证服务端的证书,即可确认返回的信息是否确实来自对应的服务端。

也就是,让你确认『你请求的是银行』。

但是此时,银行能通过这个 https 连接信息确认『你是你』么?当然不能,因为每个人都可以这样发起连接。
想要确认『你是你』,就必须通过登录等其它手段来进行验证。

此时,如果引入『双向认证』,也就是客户端也持有一份证书,在 https 握手的时候发给服务端,服务端验证证书可信后才会放行。
在这个场景下,如果每个人持有的证书是独立的,就可以做到银行通过 https 的握手信息就能确认『你是你』。

最后小结:
1 、不要把『加密』和『认证』混淆了,二者虽然有联系,但不等同
2 、最常见的 https 使用方式都是『单向认证』,但不要忽视了『双向认证』的存在

33

xylophone21      2021-07-04 11:19:44 +08:00  
1

说一下我的理解吧
1 、U Key 这种,额外的硬件。里面的私钥可以认为是读不出来的
2 、不能用 U Key,那么只能做一些假设
2.1 用户不会自己配合泄露私有,特别是配合很复杂时。比如打开浏览器的开发者页面做一顿骚操作,把手机借给黑客等。
2.2 黑客不能通过程序很容易的获取到,主要靠 OS 、浏览器保护。比如不能读别人的 localstorage,不允许跨域等
2.3 万一用户配合泄露了,不影响其他人

34

JasonLaw      2021-07-04 11:57:56 +08:00

@unco020511 #27

你说 “完整的非对称加密流程包括: 对原文非对称加密 + 签名”,但是我看了 https://en.wikipedia.org/wiki/Public-key_cryptography,里面所说的是:

With public-key cryptography, robust authentication is also possible. A sender can combine a message with a private key to create a short digital signature on the message. Anyone with the sender's corresponding public key can combine that message with a claimed digital signature; if the signature matches the message, the origin of the message is verified (i.e., it must have been made by the owner of the corresponding private key).

按照它所说的,非对称加密是非对称加密,签名是签名,并不是 “包括” 的关系。

---

还有你说 “签名是用私钥进行一次非对称加密运算得到”,我不同意加密这个说法,“签名是使用私钥产生的” 会更加准确。

---

我对安全方面并不是特别了解,如果有错误的地方,希望你能够指出。

35

unco020511      2021-07-04 12:20:32 +08:00

@JasonLaw #27 没错, 你说的更准确, 非对称加密不包括验签, 我想表达的是非对称加密与验签往往成套出现. 所以我用的是 "非对称加密流程", 而不是 "非对称加密"
---- 你说的签名使用私钥产生这句话肯定是对的, 因为 "产生" 这个词范围很大, 我想表达是更具体一些的原理, 非对称加密的算法有多种, 但最终都是经过 "复杂精密设计过的数学运算", 有一个特点就是非对称加密和解密使用的是同一个 "计算方式"(并不是逆运算, 这与对称加密的算法有本质区别), 所以加密和解密其实都是同样的数学运算, 如果要抠字眼, 那加密和解密其实不应该有严格区分.
----
“签名是用私钥进行一次非对称加密运算得到”, 可以改成 "签名是用私钥进行一次非对称运算 (可能没有这个词, 那就是数学运算) 得到"
-----
我认为我俩表达的实际上完全是一个东西

36

JasonLaw      2021-07-04 12:24:12 +08:00

@FaiChou #22 你的这个回复是回复哪个的?如果你愿意的话,可以指出我的错误,而不是不愿意分享的样子。

37

unco020511      2021-07-04 12:28:37 +08:00

@also24 #32 抱歉, 我是没有看到你原贴后面楼层的内容, 只是看到楼主单独贴出的内容, 所以就进行了讨论, 误解了你的观点.
-----
"银行确认是你" 这个问题, 你说的是业务方面 [权限][用户] 的身份确认, 我说的是主机 a 与主机 b 通信中的身份确认, 都没有错, 只是没有对到一起

周末愉快

39

also24      2021-07-04 12:39:02 +08:00

@JasonLaw #34
@unco020511 #35
关于『签名』,我想要补充一下,其实『签名』这个词,也是可以产生误解的。

你们在讨论的『签名』,其实默认了在说『 RSA 签名』,是一种具体的签名机制。

原回复中的『签名』,其实是一个宽泛概念,只要能起到对内容验证的作用,就算签名,实际上,很多时候都在用更简单的方式来生成这种签名。

例如:md5(data + sk + nonce + ts)
这就是一种很常用的模式,它与 『 RSA 签名』毫无关联,但是从作用上来说,也可以被称作『签名』。

40

Lemeng      2021-07-04 14:42:17 +08:00

u 盾,多少年前就可以。现在估计企业的银行业务都还是靠 u 盾,不过普通人用的少了

41

Quarter      2021-07-04 15:25:12 +08:00 via iPhone

为啥私钥要在浏览器端存储啊,最多浏览器存存公钥就差不多了

42

Jooooooooo      2021-07-04 15:26:11 +08:00

先用对称加密交换密钥, 这个交换过程公网公开可见

然后再用这个密钥干活

45

wooyuntest      2021-07-04 17:07:25 +08:00

此处的签名 指的不是非对称加密算法(如 RSA 、ECC 等)算法中的签名、此处的签名指的只是对浏览器发出的 http 报文用前后端约定好的算法生成一个 sign 字段附在报文中,后段程序接到请求后,使用同样的算法再次计算 sign 并与你发送的报文中的 sign 字段比对,若 sign==sign 则认为报文没有经过篡改(在 App 没有被逆向得到加密算法的情况下),也就是你说的 “才能让银行知道这些东西全都是你带的,别人没有夹带”。 这种做法常常用在 App 中来防止用户抓包修改报文以实现一些爬虫、辅助功能或用来测试后段的安全性。(因为在拿不到签名算法的情况下,用户篡改的报文在 ckecksign 阶段就被拒绝了),在基于浏览器的 web 应用中不会有这种做法,如果在浏览器中使用这种做法,会直接将加密算法公开,使得这种验签来判断报文没有经过修改的方式失去意义。

// 但是这种做法银行还是没有办法确认你就是你,判断你访问的银行就是真正的银行,是基于 PKI 体系来实现的。所以目前银行用来判断你就是你,用的方法也类似。在硬件设备(也就是常说的 U 盾)中创建非对称密钥对,导出公钥并与你关联,私有永久保存在 u 盾中,认证的时候,有一个密码学常见的 challenge-response 过程来实现。(可以思考下 ssh 密钥对登陆的密码学原理)

// 可以看下 webauthn 的介绍和文档,他实现了 passless,也是未来 web 认证的趋势。会有不少新的收获。

//ps 在配备有 Touch ID 的 Mac 上用 Safari 登陆 iCloud 的时候,只用验证指纹,思考下这个是如何实现的?

46

Nyarime      2021-07-04 22:38:40 +08:00

https://secure.quantumca.com.cn QuantumCA 的前端(下简称 “Secure Center”)颁发 SSL 证书平台采用了 WebCrypto + WebAssembly 的前端私钥方案

具体策略如下:
1. 浏览器 JS 调用 WebCrypto 来生成 CSR + KEY ( RSA 或 ECC ),
2. 用户提交 SSL 订单,发送 CSR 和用户的域名信息到 Quantum 服务器,
3. Quantum 服务器提交到 CA,
4. CA 签发 SSL 证书,回调给 Quantum,
5. Secure Center 利用接口返回的 SSL 和浏览器本地( localStorage )的私钥,用 WebAssembly + Go 转换出 IIS 的 PFX 格式证书,和 Java 的 JKS 证书,并打包压缩 ZIP,生成 Blob 对象绑定到下载按钮上。

以上是我们采用的高性能的,解决信任问题的证书签发下发系统。

参考文献:
1. <Go WebAssembly 尝试 & 瘦身> - 2020/09/16 https://yryz.net/post/go-wasm/