python

如何防止别人抓包重放攻击 - Jay54520 - 博客园

文章暂存

systemime
2021-01-05
8 min

摘要.

现在做过几个 web 项目,都用到了 api,有服务器发往服务器的 api,也有微信上从前端发往后端的 api。然后,我在使用 Pycharm 断点时发现,如果在断点暂停时一直发送请求,那么很多请求都能执行成功。然后在不断点的正常情况下,我又使用 Charles 重复发送请求,并发设置为 10,一共发 100 个,也有 10 几个请求成功了。

前端向后端发送请求

我知道 csrf_token 可以防止跨站攻击,但如果现在是一个恶意用户怎么办?我有一个每日签到 API,调用之后,在数据库中添加对应记录,并增加积分。我先在的校验方法是,如果数据库中存在一个今天签到的记录,就返回错误。现在如果用户进行请求重放,在数据库写入记录之前发了 N 条记录,那么增加积分的操作就会执行 N 次了。

现在的思路是,看看微信的做法,它的参数中含有 timestamp 和 nonce,并进行了签名加密。以及 如何防止别人抓包重放攻击 的一些思路。

一、微信的做法(微信文档地址

  1. 验证消息的确来自微信服务器

通过 timestamp, nonce 以及双方约定好的 token 构成一个签名,能够验证信息是否来自微信。

在这里并不适用,因为 api 就是由用户调用的。

二、  如何防止别人抓包重放攻击

希望一个包就实现, 不是反复发包。

一个包实现并不仅仅指第一个包实现,可以是前 n 个失败,然后第 n+1 个成功了,然后就拒绝之后的包了。如果需要达成这样,我们要使能够实现的请求唯一,构建一个类似 id 这样,不能重复的东西。

三、 加锁

类似双十一抢购,当一个用户下单之后,就锁定 5 分钟等待其付款,期间拒绝其他用户的下单请求。我们能否做到,这个请求期间,针对一个 openid 的请求只进行一次,其他就在后面排队。当一个成功后,后面类似的请求就看做是失败的。

有一个同事做过类似的加锁,明天问问他 (没问,现在在写新需求以及调优,还没做这个)。

问了一个同事,建议使用 redis 的 sadd 命令,如果订单号存在于 set 中,就返回失败。还有 redis 的分布式锁,他不建议使用,如果没有弄清楚分布式锁的原理。

现在回到我们最初的问题:

如何保证用户每天只能签到一次?

获取签到请求后,通过唯一值去 redis 中查找,如果存在,则说明签到过了,返回 "今日已签到"

不存在,则说明今天还没有签到,进行签到流程。

将上面的步骤拆开:

  1. 根据用户的签到请求构建唯一值

  2. 去 redis 中查询唯一值

3.1 已签到,拒绝

3.2 未签到,设置 redis,进行签到流程

问:为什么要储存在 redis 而不是 mysql 中?

  • 因为储存到 mysql 中太慢了,

在储存到硬盘 (mysql) 的过程中,这个值是不存在的,若这时候用户再次发送签到请求,那么还是可以签到的。这就违反了 "用户每天只能签到一次"

问:如何将数据保存到 mysql 中?

储存到 redis 后,使用异步任务将这个数据同步到 mysql。

问:如何确保存到 redis 就足够快?

不确保。我本地测试的时候没发现能够多次签到。

储存到 redis 的速度大于两次请求的速度就能够确保。

问:锁是什么?这个与锁有什么异同?

问:如何查找相关资料?

直接搜索后,找不到很多资料。那么我们就把问题夸大或缩小。

夸大:

搜索`秒杀`

秒杀系统架构分析与实战

问:能否写一个函数\装饰器\上下文管理器实现这个功能?

问:有没有相关的第三方库?

服务器向服务器发送 api

一般的做法是使用 AES 加密待发送的内容,再使用 RSA 加密 AES 秘钥,然后将以上两个一起发送出去。令我感到疑惑的是:为什么需要使用 RSA 加密 AES 秘钥并传输,一开始两边约定好一个 AES 秘钥不就可以了吗?

所以我现在的做法就是,两边约定好 AES KEY,然后使用 AES 加解密内容,并对解密后的内容进行校验。

在一个抽奖功能中又遇到了库存的问题

需求:存在 N 种不同的抽奖奖品,抽奖的概率与各奖品的实时数量成正比。希望把所有的奖品发放完毕,又不希望超卖。奖品库存总量变为 0 后,固定返回一种奖品。

比如有 3 中商品,数量为 A:10, B: 20 C: 70。 则开始时抽中 A 的概率为 (10)/(10+20+70) = 10%,

抽中 A 后, 数量变为 A: 9, B: 20 C: 70,则 A 的概率变为 9 / (9 + 20 + 70)

应用场景:现场抽奖。

综合我的需求与实现难度,决定使用 django transaction + select_for_update

发现的问题:

select_for_update  只锁了行,能读不能写,会造成超卖;

解决

使用 MySQL 表锁


上面的写法有问题


因为 WRITE 的含义就是不可读、不可写

Option Description
READ Read lock, no writes allowed
READ LOCAL Read lock, but allow concurrent inserts
WRITE Exclusive write lock. No other connections can read or write to this table
LOW_PRIORITY WRITE Exclusive write lock, but allow new read locks on the table until we get the write lock.
WRITE CONCURRENT Exclusive write lock, but allow READ LOCAL locks to the table.

出处:LOCK TABLES and UNLOCK TABLES

锁表的注意事项

注意解锁

注意异常之后的解锁

比如捕捉异常后,解锁,再抛出异常

更好的方法

同事建议使用分布式锁

redis 单机锁

参考:

如何解决秒杀的性能问题和超卖的讨论

 select for update 带来的性能问题

测试

如何测试。 设置库存数量后,使用  timesleep 模拟并发的现象

参考:

(未看,待整合)Web 大规模高并发请求和抢购的解决方案

(从上面这篇文章中可以了解到,要吸收强者的经验,秒杀与抢购还有比 12306 和淘宝更多的吗?看看他们是怎么实现的吧!)

日请求从百万到八亿的技术历程

(规模性的东西还是大公司牛叉)

程序员对比在大公司和创业公司的工作和报酬

如果你关心你做某件事后产生的实际效果,在大公司绝对是有更大的实际效果,归因于大公司的规模。如果我是在一家创业公司做我当前的工作,获得的收益大约是每月 1 万美元。我没什么好蔑视,但这都支付不起我的工资。但是同样的事情在大公司创造的收益会是 1 万美元的 1000 倍以上。在大公司有更大的实际效果因为它的规模很大。这里的推论是小公司很小以至于他们很容易对自身造成影响,尽管这个影响值本身很小。我感觉不到我做的事情对大公司会产生促进还是阻碍的作用。但是当我在小公司时,看起来我们所做的事情可以影响整个公司的命运。 https://www.cnblogs.com/jay54520/p/6181604.html https://www.cnblogs.com/jay54520/p/6181604.html

上次编辑于: 2021/5/20 下午3:26:49