V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
lemayi
V2EX  ›  问与答

前后端分离的项目, API 接口如何防止重放攻击

  •  
  •   lemayi · 2017-07-29 15:21:23 +08:00 · 20146 次点击
    这是一个创建于 2703 天前的主题,其中的信息可能已经有所发展或是发生改变。
    当前的项目是前后端分离的,用了 laravel 的 jwt 做 token 验证。
    看过很多文档都说用时间戳 timestamp 和 nonstr 来做。
    但是客户端的时间戳没办法保证和服务器的一致。
    每个用户的可能时区都不一样。没办法通过传递时间戳参数来验证是否过期。
    如果只用 nonstr,如果设置 1 天清除一次。那么一天后重放也是可以的。
    所以想请教下怎么来处理这个问题呢?
    19 条回复    2017-07-30 11:25:54 +08:00
    shoaly
        1
    shoaly  
       2017-07-29 16:10:49 +08:00
    客户端传给服务器
    ?nonstr=md5(时间戳 +随机数+ 特定字符串)&time=时间戳&random=随机数

    服务器端:
    验证 md5(用客户端传入的时间戳+ 随机数+特定字符串) 与 nonstr 对比 验证是否是客户端的请求, 然后将这个 nonstr 存入缓存当中, 同一个 nonstr 如果已经存在, 则为重放攻击
    shoaly
        2
    shoaly  
       2017-07-29 16:11:42 +08:00
    补充 1 天之后的重放, ..时间戳能对比出来啊.... 客户端确实和服务器端时间不一致, 但是 差不了几秒.
    retanoj
        3
    retanoj  
       2017-07-29 16:16:38 +08:00 via Android
    为什么时间戳无法保证和服务器一致? 难道不是在用格林尼治时间吗? timestamp 加签名应该可以解决重放了
    des
        4
    des  
       2017-07-29 16:31:03 +08:00
    时间差?就不能请求获得服务器时间,然后基于这个时间戳校正吗,如果出错就先获取一次时间戳再请求一次。

    总不能客户端的一分钟被人偷走一秒吧?
    nealv2ex
        5
    nealv2ex  
       2017-07-29 16:39:50 +08:00
    单纯的放重放来说 ,全不用不管与服务器的差值,只看时间戳就可以,时间是线性增大,不会变小,如果不变或者变小了,就有问题。

    收到请求,校验时间戳必须比上一次 这个 session 的提交的时间戳大。

    比如
    第一次是 0,第二次 1 在过滤重放攻击这个条件下,这 2 个值都是合法的。
    ho121
        6
    ho121  
       2017-07-29 16:53:24 +08:00
    @nealv2ex 改时间怎么办?
    danielmiao
        7
    danielmiao  
       2017-07-29 17:02:28 +08:00   ❤️ 1
    令牌桶,客户端请求的时候判断超过 x 分钟,更新令牌;
    然后旧令牌的失效时间为,更新令牌的时间+[0.2, 1] * x,以保证正常的并发请求;

    简单的说就是动态发放令牌,有效令牌为当前发放和有效期内的旧令牌,最好使用 cookie,保证对页面的无侵入
    danielmiao
        8
    danielmiao  
       2017-07-29 17:03:22 +08:00
    @nealv2ex 这个方案有个问题就是 ajax 并发请求的时候,后发先置的问题
    paradoxs
        9
    paradoxs  
       2017-07-29 17:11:37 +08:00
    参照 google authy 的验证机制, 把随机数发生器内置在前端就行了。
    nealv2ex
        10
    nealv2ex  
       2017-07-29 17:42:35 +08:00
    @ho121 改时间,就不是 *重放* 攻击了。

    @danielmiao 这里只解决 *重放*,后发先至是其他问题。

    不过请思考一下,后发的请求已经到了,那么是不是说,

    客户端取消了先发的请求,要不然哪里来的后发请求,

    那么我只处理后发的请求,不处理先发的请求应该是合理的。
    lemayi
        11
    lemayi  
    OP
       2017-07-29 18:15:37 +08:00
    感谢各位的解答!非常感谢!
    @nealv2ex 的回答,之前我也有考虑。但是确实会有并发,后发先至的问题。既然是并发,先发,后发的请求都肯定是合理。如果参考你的处理的方式,肯定就排除掉了正常的请求。

    @shoaly @des @retanoj 因为是前后端分离的 web 项目。js 生成的时间戳应该是本地时间。所以这个时间是很有可能和服务器不一致的。跨时区,篡改等。我之前做过测试。在服务器端放个含时间 js 的 html。然后去请求。确实打印出来的时间是我本地的时间。我电脑改了。显示的时间也改变了。其实我问的问题,主要就是纠结于此。如果是在后端的程序请求 api 那就没这个问题。我可以让客户端设置成和服务器端的一致。

    @danielmiao 的方式令我思路大开。因为当前用的是 jwt 的 token。那么我可以让前端,每 30 分钟就去重新刷新获取新的 token。然后配置每次请求用 nonstr 来防止重发。记录 30 分钟内的 nonstr 集合。不在这个集合里面就是正常请求。这样即使过了 30 分钟,发起重放。实际上我的 token 已经变了。就没这个问题了。也不会有时间戳一致的问题了。
    plantain
        12
    plantain  
       2017-07-29 18:23:42 +08:00
    标准方法难道不是 https ?
    des
        13
    des  
       2017-07-29 18:42:03 +08:00 via Android
    @lemayi 一开始以为是客户端,web 的话上 https 啊,不然重放都是小问题
    motian
        14
    motian  
       2017-07-29 18:50:26 +08:00 via iPhone
    时间戳本身是跟时区无关的,你在东八区和零时区,不同的是日期时间,时间戳是一致的,客户端和服务端的时间差问题可以进行定期的时间校准。
    retanoj
        15
    retanoj  
       2017-07-29 22:27:26 +08:00 via Android   ❤️ 1
    不过 lz,jwt 里有个字段是标识请求仅一次有效的啊,好像是 iat ?可以参考一下
    whileFalse
        16
    whileFalse  
       2017-07-29 22:29:40 +08:00 via iPhone
    你要防止的是啥?中间人重放的话上 https 就行
    lianz
        17
    lianz  
       2017-07-29 22:34:42 +08:00
    timestamp=int(timestamp()/30),这样服务器客户端时间误差 30 秒内就可以满足条件。

    当然请求签名是一定要做的,不然一切白费。
    voocel
        18
    voocel  
       2017-07-29 22:45:25 +08:00 via iPhone
    请问用的是哪个 jwt 包
    lemayi
        19
    lemayi  
    OP
       2017-07-30 11:25:54 +08:00
    @voocel "tymon/jwt-auth": "^1.0@dev"
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   830 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:13 · PVG 05:13 · LAX 13:13 · JFK 16:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.