关于web资金系统提现安全保护,防止极快的重复并发请求导致重复提现的解决思路
关于WEB金融系统中的提现安全问题很多人没有深入思想,导致有漏洞,常常会遇到有些人遇到被攻击到导资金损失的麻烦, 其实要彻底解决重复并发请求 导致重复提现问题,是需要花点心思的,并没有看起来的那么 简单,即使是最直观简单的语句都是有漏洞的比如:
-----------------------------------------场景1--------------------
发现很多朋友的项目一个漏洞:先为一账户充值100元,然后瞬间发送10次提现请求(都是提现100,提现接口是有做余额不足校验的),其中大约有四五次都是成功的,剩下的会报余额不足。期望是,只有一次可以成功完成提现,分析到能部分请求能通过余额不足校验原因是,由于是瞬间发出的提现请求,这些请求中拿到的余额数据都是余额扣减之前的数据。
以上场景可以提炼出两个关键步骤:
- 查询余额并校验,select * from account where user_id = 123;
- 扣减余额并支付,update account set balance...
根据以上步骤,可知:1.在两条SQL语句执行的中间这段时间,由于重复请求攻击,可能会出现多次请求的第一步操作成功,并继续执行第二步,最后导致资金损失。2.由于第一步操作是查询操作,没有数据库会限制重复读取数据
-----------------------------------------场景2----------------------------------
重复提交,表面上是重复提交,威力不大,但实际。。。我们来分析分析:
假设一个用户,余额100,平台恰好有个提现的地方,理所当然用户最多只能提取100元。
我们来分析下程序在生成提现数据的过程:
开启事务;
用户发起一次提现请求,到达应用后,程序判断用户余额是否够用,如果不够就跳出事务了;
然后扣除100元,
然后再提现数据表中插入一条数据,
到这里还没结束,因为事务还没提交,当上面进行顺利时,到达这里就应该commit提交了,如果上面操作任何一步异常,就rollback回滚了。
看起来挺完美的过程,其实!弱暴了!
为啥?
假如用户发起两个请求,而且同一时间(1/1000秒级)请求到服务器,
再走一次上面的逻辑:
请求一达到服务器 请求二达到服务器
开启事务 开启事务
余额检查->通过 余额检查->通过
扣除余额->done 扣除余额->done
插入提现记录->done 插入提现记录->done
提交->commit(); 提交->commit();
两边几乎同时进行一样的操作,为什么没被拦截掉只处理一个请求呢?因为余额检查时,别的请求的事务未提交,在此请求内select的数据还未生效,所以两个请求处理都通过了检查。
那怎么防御呢?
token?
扯J8蛋!token用来防御这原子级别的攻击?别说session了,即使你重写php底层,让session动态调用php的内存也无济于事。原因自己脑补;
队列是终极解决方案。
然后有一个临时方案,提现的表中肯定会有time/datetime之类的字段,在建表时将这个表中的time/datetime
+ userId
设置为联合主键,然后事务在插入提现数据时,因为时间同一秒且同一用户所以数据冲突,只会成功一条,然后事务报错启动回滚,近乎完美。唯一的瑕疵就是假如前后误差1ms,
然后恰好前一个时间是xxxx1,后一个时间是xxxx2,这样就扯痛蛋了。。。千分之一的概率。
-----------------------------------------原因-----------------------------------
有人人甚至认为无解 ,其实是对数据库理解不够深,如事务级别(脏读,读提交,不可重复读,序列化级,快照级,)、并发机制、锁(共享锁,更新锁,X独占锁,行级锁,页级锁、意向锁),这么多底层知识有足够的理解才能解决这个问题,因为这些方便资料很少,愿意花精力去研究的人更不多,更郁闷的是微软数据库对查询作了优化,文档和实际执行效果是不一样的,比如微软文档明文写着,共享锁 与更新锁是可以相关排斥的,select语句默认是发布hold共享锁,如果你真信就完了,你实际执行结果是共享锁和updlock不会排斥,除非你显示指定,select * from account with(holdlock) 文档和实际不一致,只有遇到坑后请求微软技术支持才他技术人员才知道你,微软对select做了特别优势默认不是被排斥很多锁的,瞬间被坑,当年还记有个携程的主程序员不懂锁乱用,给一个查询加了with(nolock), 订票资金出现重大事故教训。 所以我提供以下几个常用的解决方法。不是不可能其实也很简单。
数据在数据库层面解决这个问题很简单,反相用了ORM EntityFramework之类的才不好解决数据库解决方案
解决方案1:使用显示事务
begin tran
select * from account with(rowlock updlock) where user_id = 123; --发布行级更新锁,第二并发请求到这里严格排序,不管有多快,这里有个技巧,因为第二个并发撞进来第一句也是updlock所以两个updlock之间会排斥
update account set balance...
commit
解决方案2:在代码层使用分布事务
using (TransactionScope ts = new TransactionScope()) //用这个需要本地单独开MSDTC (Distributed Transaction Coordinator)服务,并不一定通用 有门槛
{
exesql("update account set balance=balance where user_id =123"); //这一句很重要,事务中开头一句update让数据库先发布一个x锁,后面的并发将被严格排队
exesql("select * from account where user_id = 123;");
exesql("update account set balance..");
ts.Complete();
}
方案3 ,在代码入口使用线程锁
public static object lockObj =new object();
public void Withdraw(int user_id,int amount){
lock (lockObj){ //让提现操作在线程线严格排序,不管并发有多快,缺点是不同的用户 提现也得按顺序排序,但一般提现操作是小概率操作,不会很密集,正常提现的没阻塞感知,但是攻击者可以反复发起请求,导致正确用户提现变慢或阻塞
exesql("select * from account where user_id = 123;");
exesql("update account set balance.."); }
}
一个很重要的技巧是在一个事务内,第一句先 写一个无意义的update Account with(rowlock) set balance=balance where userid=123 ; 这个技巧在任何时候都适用,强制让数据库在事务期内发布x级独占行级锁锁,后面的操作被严格排队,就算攻击者重复请求也只会阻塞他自己的用户查询,不会阻塞别人的
总结:最可行的是存储过程方案1,缺点是不灵活,在如今ORM满天飞的情况下新一代人很少有会写存储过程SQL了,
方案2,是一个折中方案,一般可控性还好
方案3,使用最简单,基本是零成本,零难度,但是会有潜被拒绝服务攻击的功能,但保证最重要的数据安全
关于web资金系统提现安全保护,防止极快的重复并发请求导致重复提现的解决思路的更多相关文章
- C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路
C#不用union,而是有更好的方式实现 用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...
- OS.js – 开源的 Web OS 系统,赶快来体验
OS.js 是一个开源的 Web OS 系统,可以在浏览器中运行,提供了窗口管理器,应用程序API,用户界面开发套件和抽象的文件系统等.可以部署在 Node 或者 PHP 环境中运行.OS.js is ...
- 【转发】构建高可伸缩性的WEB交互式系统(下)
原文转自:http://kb.cnblogs.com/page/504518/ 本文是<构建高可伸缩性的WEB交互式系统>系列文章的第三篇,以网易的NEJ框架为例,对模块的可伸缩性进行分析 ...
- 【转发】构建高可伸缩性的WEB交互式系统(中)
原文转自:http://kb.cnblogs.com/page/503953/ 在<构建高可伸缩性的WEB交互式系统>的第一篇,我们介绍了Web交互式系统中平台的可伸缩性.本文将描述模块的 ...
- 【转发】构建高可伸缩性的WEB交互式系统(上)
原文转自:http://kb.cnblogs.com/page/503460/ 可伸缩性是一种对软件系统处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展过程中,能够保证旺盛的生命力,通过很少的改 ...
- android系统掉电保护
/************************************************************************ * android系统掉电保护 * 说明: * An ...
- Walle 瓦力 web部署系统
Walle 一个web部署系统工具,可能也是个持续发布工具,配置简单.功能完善.界面流畅.开箱即用! 安装步骤: 1. git clone 首先配置成功(去百度找答案) 打开git bash命令窗口执 ...
- 基于Web的系统测试方法
基于Web的系统测试与传统的软件测试既有相同之处,也有不同的地方,对软件测试提出了新的挑战.基于Web的系统测试不但需要检查和验证是否按照设计的要求运行,而且还要评价系统在不同用户的浏览器端的显示是否 ...
- 谈Web应用系统的可维护性
每一个软件开发人员都十分清楚, 当软件构建得越来越复杂时, 可维护性就成了一个很突出的问题. 如何在构造软件系统的过程中始终保持可控制的可维护性呢? 一. 整体组织 ...
随机推荐
- 消息中间件activemq的使用场景介绍(结合springboot的示例)
一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...
- 如何发布jar包到maven中央仓库
自使用maven以来,没少使用maven中央仓库中的各种jar包,方便有效,但是咱们也不能总是只取不予,也应该懂得奉献,当你写好了一个十分好用的jar包,想贡献出去给大家使用的时候,应该怎么做呢?当然 ...
- 关于linux find命令的使用
find 和 xargs xargs和find 在 使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令 ...
- Python操作HBase之happybase
安装Thrift 安装Thrift的具体操作,请点击链接 pip install thrift 安装happybase pip install happybase 连接(happybase.Conne ...
- multiWriter.go
package blog4go import ( "errors" "fmt" ) var ( // ErrFilePathNotFound 文件路径找不到 E ...
- 在vs中编写代码常用的快捷键
作为一个程序员,能够熟悉使用各种快捷键,可以增加我们编写和调试代码的速度,下面我就对常使用的快捷键做一些总结,下面这些快捷键基本适用于所有版本的vs: 最给力: Ctrl+K+F 快速整理代码格式 ...
- bzoj [HNOI2008]Cards
群论第一题. 发现这题也是有颜色个数限制的,所以不能用$Polya$,只能用$Burnside$ $L={\frac{1}{|G|}}{\sum_{i=1}^{m}{D(a_{i})}}$ 先$dfs ...
- 三元运算符 与 return
有三元运算符可以很好的代替if else简单语句 但是在使用的时候发现 与 return使用的时候 需要用这种形式 错误形式: $a ? return 1 ? return 0; 正确形式: retu ...
- Ubuntu 17.10 UTC
UTC即Universal Time Coordinated,协调世界时(世界统一时间)GMT 即Greenwich Mean Time,格林尼治平时Windows 与 Mac/Linux 看待系统硬 ...
- 哎呀,我老大写Bug啦——记一次MessageQueue的优化
MessageQueue,顾名思义消息队列,在系统开发中也是用的比较多的一个中间件吧.我们这里主要用它来做日志管理和订单管理的,记得老老大(恩,是的,就是老老大,因为他已经跳槽了)还在的时候,当时也是 ...