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

基于 HTTPS 对外提供 API,十万个为什么,求 web 安全高手指教

  •  
  •   phx13ye ·
    Alwayswithme · 2020-01-15 22:05:23 +08:00 via Android · 4771 次点击
    这是一个创建于 1811 天前的主题,其中的信息可能已经有所发展或是发生改变。
    对接过各种第三方,他们的 HTTPS 接口有时会要求
    1 对数据计算签名,有 https 还有必要吗?
    2 每次请求带上时间戳和 nonce,这个应该是防重放?
    3 OAuth2 那一套 token 机制
    4 稀奇古怪的数据加解密方式,主要是银行和支付公司

    搞得我每次调试时都很郁闷

    HTTPS 不是已经很完善了吗,上面四种哪些是瞎搞呢,应该做设计怎么做既可以简单实现,方便自己和他人调试,又足够安全呢?
    第 1 条附言  ·  2020-01-16 16:11:03 +08:00
    content-type 是 json 的话,怎样生成和传输签名大家有没有好的实践????

    我目前的想法是,对整个 body digest,然后把签名的 base64 存到请求首部

    但是 json 序列反序列化一般是框架处理,就要 hack 框架,好像不是很好

    对于请求,body 一般只能读取一次,读取时要先缓存一份,然后和请求首部比对

    对于响应,业务代码只返回对象,需要在序列化成 body 时拦截并设置响应首部,或者这一步省去,只判断请求,毕竟响应带签名,客户端收到也不一定会校验
    24 条回复    2020-01-16 18:23:14 +08:00
    Maboroshii
        1
    Maboroshii  
       2020-01-15 22:11:31 +08:00 via Android   ❤️ 1
    1 https 是加密,是让第三方看不见原文。签名是保证数据来源可靠。
    2 时间戳可以认为是防重放,nonce 更多的是保证签名不一致,防止签名被简单破解。
    3 token 是验证身份
    4 是更多一层的数据保护。
    yankebupt
        2
    yankebupt  
       2020-01-15 22:19:27 +08:00   ❤️ 1
    据说有限量的测试专用 api,可以找他们要,测试专用的,除了限量很低什么防范措施都没有那种
    比如语音音箱 *滴平台 就这样……
    blless
        3
    blless  
       2020-01-15 22:25:09 +08:00 via Android   ❤️ 1
    1、签名是防篡改,签名算法不可逆,中间人想要篡改信息需要签名算法跟盐
    2、时间戳跟 nonce 除了防重放本身也可以加强签名
    3、4 就是 1 楼说的
    phx13ye
        4
    phx13ye  
    OP
       2020-01-15 22:26:07 +08:00 via Android
    @yankebupt 倒也不算是为了绕过验证,只是明明已经用了 https 并且有一些还要设置 ip 白名单才能请求,我在想哪些步骤是没必要做的
    jugelizi
        5
    jugelizi  
       2020-01-15 22:28:29 +08:00
    所以你真的研究了 https ?
    also24
        6
    also24  
       2020-01-15 22:33:39 +08:00   ❤️ 22
    单向的 https 只能保证你请求的银行真的是银行,但银行无法确定你是你。

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

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

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

    最后,稀奇古怪的加密又是什么呢?
    答:你带的东西是一个保险箱~
    leloext
        7
    leloext  
       2020-01-15 22:37:52 +08:00   ❤️ 1
    仅说下看法。
    https 仅仅是防止了第三方对信息的截取(除去中间人攻击),业务逻辑还应该在服务端验证,客户端返回的信息都不可信,需要各种验证,这个验证并不是 https 能保证的。
    also24
        8
    also24  
       2020-01-15 22:41:02 +08:00
    @leloext #7
    https 是有双向认证机制的,可以保证双向通信可信,只是大家一般都没在用而已
    blless
        9
    blless  
       2020-01-15 22:48:58 +08:00 via Android
    1、签名是防篡改,签名算法不可逆,中间人想要篡改信息需要签名算法跟盐
    2、时间戳跟 nonce 除了防重放本身也可以加强签名
    3、4 就是 1 楼说的
    leloext
        10
    leloext  
       2020-01-15 22:49:04 +08:00
    @also24 有一说一,我真还没用过双向认证,我做的业务来说都是在 http 上面包一层 tls,确保传输的安全性,后端的各种验证一个都少不了。
    blless
        11
    blless  
       2020-01-15 22:50:02 +08:00 via Android
    ……点进来怎么又发了一遍
    Xbluer
        12
    Xbluer  
       2020-01-15 22:58:04 +08:00
    4、稀奇古怪的加解密方式,指的是国密算法?当然也可能是不专业的 Base64“加密"。
    eminemcola
        13
    eminemcola  
       2020-01-15 23:05:04 +08:00   ❤️ 1
    @blless #11 一次成功的重放(狗头
    phx13ye
        14
    phx13ye  
    OP
       2020-01-15 23:10:21 +08:00 via Android
    @also24

    所以一个都不能少吗?😂

    加解密,大概是这样他们把需要处理的内容数据用私钥加密,放在 body 某个地方,比如 xml 的一个字段,然后我用公钥解出来。我响应内容也是这样,公钥加密后发出去
    phx13ye
        15
    phx13ye  
    OP
       2020-01-15 23:14:35 +08:00 via Android
    @Xbluer 什么叫国密,一般是非对称或者 hmac sha 之类的哈,我应该没混淆加密,哈希,编码之类的概念😂
    also24
        16
    also24  
       2020-01-15 23:17:07 +08:00   ❤️ 1
    @phx13ye #14
    看你要的是 『尽可能安全』,还是 『尽可能省事』。
    鉴权认证、防监听、防篡改、防重放 都是事实需求啊。


    另外我倒数第二句不是疑问句,是设问句,答案就在最后一行啊。

    从实际角度来说,这样做也确实存在一定的意义,因为 https 的流量卸载很有可能在负载均衡那一步就进行了,如果没有这个二次加密,那么数据在负载均衡那一步就是 『裸』的了,而多了这一层加密,可以让数据在到达业务代码部分时才被解谜。
    Xbluer
        17
    Xbluer  
       2020-01-15 23:21:03 +08:00
    Kontinue
        18
    Kontinue  
       2020-01-16 08:18:56 +08:00   ❤️ 1
    之前博客中的总结。
    -------
    这边还想提一下 HTTPS。之前看到一则知乎上的提问:[使用了 https 后,还有必要对数据进行签名来确保数据没有被篡改吗?]( https://www.zhihu.com/question/52392988)

    总结一下就是:

    API 签名保证的是应用的数据安全和防篡改,并且可以作为业务的参数校验和处理重放攻击。

    HTTPS 保证的是运输层的加密传输,但是无法防御重放攻击。

    换句话说,HTTPS 保证通过中间人攻击抓到的报文是密文,无法或者说很难破解。但仍然可以将报文重发,形成 DDOS。同时,如果不签名,只用 HTTP 简单认证,通过抓包,直接可以获取到 Authorization,就可以随意发起请求了。因此最安全的方法就是结合 HTTPS 和 API 签名。
    SmiteChow
        19
    SmiteChow  
       2020-01-16 09:23:13 +08:00
    #6 通俗易懂 赞
    LeeSeoung
        20
    LeeSeoung  
       2020-01-16 11:10:28 +08:00
    简单来说,这些措施都是有必要的,但也只是增大破解难度而已,不能 100%防御。
    unco020511
        21
    unco020511  
       2020-01-16 11:30:11 +08:00
    都有必要啊,签名是防篡改的,一般 :签名=不可逆算法(参数列表+时间戳+密钥)
    phx13ye
        22
    phx13ye  
    OP
       2020-01-16 16:27:44 +08:00
    @also24

    要的是 『尽可能安全』
    HTTPS 的服务端收到请求,加上这些验证,还有什么遗漏吗?
    响应时,有什么措施是必须采纳的呢?
    also24
        23
    also24  
       2020-01-16 16:57:29 +08:00   ❤️ 1
    @phx13ye #22
    遗漏有很多种可能吧,我这二把刀应该保证不了没有遗漏。
    这东西应该只涉及到你期望覆盖的面的大小,没有尽头。

    比如说 客户端是否要对 https 证书进行强验证(不止验证证书有效性,还要验证证书是否一致)。
    比如说 时间戳+签名 需要有时间差冗余,避免时间不一致导致出错,而冗余就带来了短时间内重放的风险,此时又需要缓存若干个请求信息来防止短时间内的重放。
    比如说 你的 token 机制是否足够安全,是否有吊销机制( JWT 出来挨打)。

    每个措施,一般都是对应某个场景给出的,其实还是确认场景的问题。



    另外回应你附言中的问题:

    content-type 是 json 的话,签名可以放进 header,也可以给 json 再罩一层 “非签名” 区域。
    即:参与签名运算的只是某个子字段,而另一个子字段存储签名信息即可。

    但是这里需要注意:
    JSON 的 k-v 是无序的,且 JSON 序列化的格式是可以自定义的,当你期望对一个 JSON 求签名( hash )的时候,请务必协调好两端的 JSON 序列化格式及顺序。
    如果觉得麻烦,也可以选择将需要传递的 JSON 对象,先序列化之后,存于某个 JSON String 字段。


    > 对于请求,body 一般只能读取一次,读取时要先缓存一份,然后和请求首部比对
    这段话我没有理解是什么意思,为什么只能读取一次。
    可以考虑设置类似 “API 网关” 的概念,所有请求先经过网关的核验之后再传入业务代码。
    phx13ye
        24
    phx13ye  
    OP
       2020-01-16 18:23:14 +08:00
    @also24 感谢大佬

    防重放准备这样做
    1.验证签名防篡改
    2.判断服务器时间是否比请求 timestamp
    3.判断 nonce 是否 redis set 中

    是考虑 json 的字段可以嵌套和 k-v 无序,才将整个 body hash😭

    请求可以读取多次,只是很多时候 web 框架只让读取一次,可以缓存一份替代,在 java-spring 里是 ContentCachingRequestWrapper,在 go-gin 里是 ioutil.NopCloser
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1134 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 18:32 · PVG 02:32 · LAX 10:32 · JFK 13:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.