V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
JasonLaw
V2EX  ›  程序员

怎么安全地在 web 前端存储私钥?

  •  
  •   JasonLaw · 2021-07-03 23:13:52 +08:00 · 8976 次点击
    这是一个创建于 1264 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    also24 的回复说得非常棒。

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

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

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

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

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


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

    46 条回复    2021-07-04 22:38:40 +08:00
    janus77
        1
    janus77  
       2021-07-03 23:20:02 +08:00
    浏览器的环境实现吧,浏览器这个软件本身是利用了操作系统的功能,和硬件做了交互。
    JasonLaw
        2
    JasonLaw  
    OP
       2021-07-03 23:25:00 +08:00 via iPhone
    @janus77 #1 可以更加具体一点、使用外行人的语言描述吗?
    xmumiffy
        3
    xmumiffy  
       2021-07-03 23:36:03 +08:00 via Android
    这种都是服务端间的交互方式,密钥直接写在代码里的。
    要认证客户端可以使用双向证书
    FaiChou
        4
    FaiChou  
       2021-07-03 23:38:29 +08:00 via iPhone   ❤️ 2
    你是想在网页端做 ssl pinning ?

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

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

    前端的东西都是透明的,不像 app 客户端可以封在包里,不过封在包里也可以被越狱 /root 来篡改。楼主想要的绝对安全的地方,不存在的。
    hahasong
        5
    hahasong  
       2021-07-03 23:40:48 +08:00
    jwt 就行,已经标准化了
    JasonLaw
        6
    JasonLaw  
    OP
       2021-07-03 23:54:04 +08:00 via iPhone
    @FaiChou #4 很多人都说“使用签名来保证来源的可靠性等等”,那它们是怎么处理私钥的?
    JasonLaw
        7
    JasonLaw  
    OP
       2021-07-03 23:56:05 +08:00 via iPhone
    @hahasong #5 JWT 中的签名是由服务端存储的密钥产生的,跟我的提问有啥关系呢?
    IvanLi127
        8
    IvanLi127  
       2021-07-04 00:06:25 +08:00 via Android
    我觉得每次都输入密码,用密码签名所有参数和时间戳,就能保证收到的东西就是你现在发送的。这个密码存可信的地方才行,你相信浏览器,那就能安全存储了。如果不相信自己,每次输入密码都没用
    walpurgis
        9
    walpurgis  
       2021-07-04 00:16:12 +08:00
    带签名的接口一般都是给服务端调用的,原帖没有任何地方提到前端
    JasonLaw
        10
    JasonLaw  
    OP
       2021-07-04 00:21:28 +08:00 via iPhone
    @IvanLi127 #8 问题就是“怎么安全地在浏览器存储密码”😅
    binux
        11
    binux  
       2021-07-04 00:23:06 +08:00 via Android
    浏览器安全是由浏览器保证的,你作为前端无论做什么都是没用的
    JasonLaw
        12
    JasonLaw  
    OP
       2021-07-04 00:25:49 +08:00 via iPhone
    @walpurgis #9 是的,一般都是服务端请求服务端,原帖没有提到前端。但我的问题是关于浏览器端请求服务端。
    SP00F
        13
    SP00F  
       2021-07-04 00:29:14 +08:00
    前端是防小人,不防君子。

    都不信任,那就只能走一律不信的态度了
    3dwelcome
        14
    3dwelcome  
       2021-07-04 00:41:59 +08:00
    "怎么安全地在 web 前端存储私钥?"
    以前貌似我也在 V 站讨论过这问题,结论是普通加密保存就可以了。

    但是有两点额外要素,第一是解密后私钥只确保使用一次后就失效。第二是每次用代码生成不一样的加密算法,就是前端存的不是私钥的解密密码,而是生成的解密代码虚拟机本身,比如 WEBASM 。
    also24
        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 盾,导出你的私钥,不又看光光了么?
    此时『安全的存储』就愈发变态了,还要考虑到硬件设备被破解的情况。
    那可能就要搬出更极端的存储设备了 - 全靠大脑,直接硬背。


    那有些搞催眠的朋友……………………
    jadec0der
        16
    jadec0der  
       2021-07-04 02:12:58 +08:00
    银行? U 盾啊
    unco020511
        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 安全性无关,属于你自己主动安装第三方证书
    unco020511
        18
    unco020511  
       2021-07-04 02:33:40 +08:00
    如果你问的是 oath2,那前端怎么会存储私钥呢
    xuanbg
        19
    xuanbg  
       2021-07-04 04:57:30 +08:00   ❤️ 1
    所以银行会发你一个 usbKey 来存储证书啊。就是因为浏览器干不来这个事情,客户端任何常规存储方式都是不安全的。
    JasonLaw
        20
    JasonLaw  
    OP
       2021-07-04 07:27:18 +08:00 via iPhone
    @also24 #15 👍 简单易懂
    JasonLaw
        21
    JasonLaw  
    OP
       2021-07-04 07:30:29 +08:00 via iPhone
    @unco020511 #17 “私钥加密,公钥解密”是什么东西?
    FaiChou
        22
    FaiChou  
       2021-07-04 08:45:42 +08:00 via iPhone
    @JasonLaw 你想了解的是:对称加密和非对称加密。去搜搜这相关的知识吧。元知识需要学的,不靠问。
    mxT52CRuqR6o5
        23
    mxT52CRuqR6o5  
       2021-07-04 09:23:14 +08:00 via Android
    私钥不私的话破坏了很多假设,安全性就无法保证
    ZeawinL
        24
    ZeawinL  
       2021-07-04 10:06:12 +08:00 via Android
    前端使用用户密码 hash 作为密钥?
    evilStart
        25
    evilStart  
       2021-07-04 10:29:52 +08:00 via Android
    @FaiChou 该学习的是你吧。明明是公钥加密,私钥解密。要是能用公钥解密那这加密还有啥意义。
    vibbow
        26
    vibbow  
       2021-07-04 10:39:24 +08:00
    U 盾就是干这个事情的呀
    unco020511
        27
    unco020511  
       2021-07-04 10:43:31 +08:00
    @JasonLaw 非对称加密除了加密外,还有一个应用叫做数字签名,完整的非对称加密流程包括:对原文非对称加密+签名,也就是实际是将密文(公钥加密的)+签名(实际为 hash 后用私钥进行一次非对称加密运算得到)一起打包发送,这样做既保证了数据不会被中间方窃听,又保证了数据的完整性以及来源
    JasonLaw
        28
    JasonLaw  
    OP
       2021-07-04 10:43:36 +08:00 via iPhone
    @vibbow #26 前面已经有很多人说了这个了,如果没有其它想法的话,最好不要重复同样的东西。如果冒犯到你的话,我先说句抱歉,我只是不想让主题包含太多没意义的信息。
    unco020511
        29
    unco020511  
       2021-07-04 10:46:58 +08:00
    @evilStart 25# 签名
    BoringBB
        30
    BoringBB  
       2021-07-04 10:50:21 +08:00 via Android
    使用客户端证书啊,在 ssl 握手的时候,服务器可以要求客户端提供证书来验证身份
    geniussoft
        31
    geniussoft  
       2021-07-04 10:56:34 +08:00
    @also24 啥? U 蹲被破解?

    以后开通网上银行,要求用户默背 2048 位密钥,并且熟练掌握口算非对称加密,绝对不能动笔😂
    also24
        32
    also24  
       2021-07-04 10:57:02 +08:00
    @unco020511 #17
    看内容要参照上下文,不要直接臆测某句话的意思。

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

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

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

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

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

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


    最后小结:
    1 、不要把『加密』和『认证』混淆了,二者虽然有联系,但不等同
    2 、最常见的 https 使用方式都是『单向认证』,但不要忽视了『双向认证』的存在
    xylophone21
        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 万一用户配合泄露了,不影响其他人
    JasonLaw
        34
    JasonLaw  
    OP
       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).

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

    ---

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

    ---

    我对安全方面并不是特别了解,如果有错误的地方,希望你能够指出。
    unco020511
        35
    unco020511  
       2021-07-04 12:20:32 +08:00
    @JasonLaw #27 没错,你说的更准确,非对称加密不包括验签,我想表达的是非对称加密与验签往往成套出现.所以我用的是"非对称加密流程",而不是"非对称加密"
    ----你说的签名使用私钥产生这句话肯定是对的,因为"产生"这个词范围很大,我想表达是更具体一些的原理,非对称加密的算法有多种,但最终都是经过"复杂精密设计过的数学运算",有一个特点就是非对称加密和解密使用的是同一个"计算方式"(并不是逆运算,这与对称加密的算法有本质区别),所以加密和解密其实都是同样的数学运算,如果要抠字眼,那加密和解密其实不应该有严格区分.
    ----
    “签名是用私钥进行一次非对称加密运算得到”,可以改成"签名是用私钥进行一次非对称运算(可能没有这个词,那就是数学运算)得到"
    -----
    我认为我俩表达的实际上完全是一个东西
    JasonLaw
        36
    JasonLaw  
    OP
       2021-07-04 12:24:12 +08:00
    @FaiChou #22 你的这个回复是回复哪个的?如果你愿意的话,可以指出我的错误,而不是不愿意分享的样子。
    unco020511
        37
    unco020511  
       2021-07-04 12:28:37 +08:00
    @also24 #32 抱歉,我是没有看到你原贴后面楼层的内容,只是看到楼主单独贴出的内容,所以就进行了讨论,误解了你的观点.
    -----
    "银行确认是你"这个问题,你说的是业务方面[权限][用户]的身份确认,我说的是主机 a 与主机 b 通信中的身份确认,都没有错,只是没有对到一起

    周末愉快
    JasonLaw
        38
    JasonLaw  
    OP
       2021-07-04 12:34:48 +08:00
    @unco020511 #37
    @also24 #32

    我的错🤐
    also24
        39
    also24  
       2021-07-04 12:39:02 +08:00
    @JasonLaw #34
    @unco020511 #35
    关于『签名』,我想要补充一下,其实『签名』这个词,也是可以产生误解的。

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

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

    例如:md5( data + sk + nonce + ts)
    这就是一种很常用的模式,它与 『 RSA 签名』毫无关联,但是从作用上来说,也可以被称作『签名』。
    Lemeng
        40
    Lemeng  
       2021-07-04 14:42:17 +08:00
    u 盾,多少年前就可以。现在估计企业的银行业务都还是靠 u 盾,不过普通人用的少了
    Quarter
        41
    Quarter  
       2021-07-04 15:25:12 +08:00 via iPhone
    为啥私钥要在浏览器端存储啊,最多浏览器存存公钥就差不多了
    Jooooooooo
        42
    Jooooooooo  
       2021-07-04 15:26:11 +08:00
    先用对称加密交换密钥, 这个交换过程公网公开可见

    然后再用这个密钥干活
    FaiChou
        43
    FaiChou  
       2021-07-04 15:31:06 +08:00 via iPhone
    @JasonLaw 36 楼。

    我看了你的问题和对上面的回复,感觉是对加密算法这些概念不是很清楚,所以让你先去学习下,我并没有什么好分享的,要分享也只是搬运网上的知识点过来,所以还是指出知识点让你自己去看更好。看了楼下的讨论,或许讨论多了你更能知道自己的疑问所在。可能最后一句冲突到了你,抱歉。
    关于加密算法这里面有个简易的教程: http://blog.cnbang.net/tech/3386/

    希望能帮到你。



    @evilStart 谢谢指出问题,4 楼我的回复有错。
    wzwb
        44
    wzwb  
       2021-07-04 16:50:57 +08:00 via Android
    wooyuntest
        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 的时候,只用验证指纹,思考下这个是如何实现的?
    Nyarime
        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/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1402 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 17:24 · PVG 01:24 · LAX 09:24 · JFK 12:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.