关于WEB金融系统中的提现安全问题很多人没有深入思想,导致有漏洞,常常会遇到有些人遇到被攻击到导资金损失的麻烦,     其实要彻底解决重复并发请求 导致重复提现问题,是需要花点心思的,并没有看起来的那么 简单,即使是最直观简单的语句都是有漏洞的比如:

-----------------------------------------场景1--------------------

发现很多朋友的项目一个漏洞:先为一账户充值100元,然后瞬间发送10次提现请求(都是提现100,提现接口是有做余额不足校验的),其中大约有四五次都是成功的,剩下的会报余额不足。期望是,只有一次可以成功完成提现,分析到能部分请求能通过余额不足校验原因是,由于是瞬间发出的提现请求,这些请求中拿到的余额数据都是余额扣减之前的数据。

以上场景可以提炼出两个关键步骤:

  1. 查询余额并校验,select * from account where user_id = 123;
  2. 扣减余额并支付,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资金系统提现安全保护,防止极快的重复并发请求导致重复提现的解决思路的更多相关文章

  1. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

  2. OS.js – 开源的 Web OS 系统,赶快来体验

    OS.js 是一个开源的 Web OS 系统,可以在浏览器中运行,提供了窗口管理器,应用程序API,用户界面开发套件和抽象的文件系统等.可以部署在 Node 或者 PHP 环境中运行.OS.js is ...

  3. 【转发】构建高可伸缩性的WEB交互式系统(下)

    原文转自:http://kb.cnblogs.com/page/504518/ 本文是<构建高可伸缩性的WEB交互式系统>系列文章的第三篇,以网易的NEJ框架为例,对模块的可伸缩性进行分析 ...

  4. 【转发】构建高可伸缩性的WEB交互式系统(中)

    原文转自:http://kb.cnblogs.com/page/503953/ 在<构建高可伸缩性的WEB交互式系统>的第一篇,我们介绍了Web交互式系统中平台的可伸缩性.本文将描述模块的 ...

  5. 【转发】构建高可伸缩性的WEB交互式系统(上)

    原文转自:http://kb.cnblogs.com/page/503460/ 可伸缩性是一种对软件系统处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展过程中,能够保证旺盛的生命力,通过很少的改 ...

  6. android系统掉电保护

    /************************************************************************ * android系统掉电保护 * 说明: * An ...

  7. Walle 瓦力 web部署系统

    Walle 一个web部署系统工具,可能也是个持续发布工具,配置简单.功能完善.界面流畅.开箱即用! 安装步骤: 1. git clone 首先配置成功(去百度找答案) 打开git bash命令窗口执 ...

  8. 基于Web的系统测试方法

    基于Web的系统测试与传统的软件测试既有相同之处,也有不同的地方,对软件测试提出了新的挑战.基于Web的系统测试不但需要检查和验证是否按照设计的要求运行,而且还要评价系统在不同用户的浏览器端的显示是否 ...

  9. 谈Web应用系统的可维护性

           每一个软件开发人员都十分清楚, 当软件构建得越来越复杂时, 可维护性就成了一个很突出的问题. 如何在构造软件系统的过程中始终保持可控制的可维护性呢?          一. 整体组织   ...

随机推荐

  1. mysql事务之间的隔离级别

    事务间未做隔离,会引起下面这些问题. 1.脏读:一个事务可读到另外一个尚未commit的事务中的数据. 2.不可重复读:在一个事务中,读取同一个数据 a,b,按顺序读取,在读a  b 之间,另外一个事 ...

  2. capwap学习笔记——初识capwap(三)(转)

    2.5.6 CAPWAP状态机详解 2.5.6.1 Start to Idle 这个状态变迁发生在设备初始化完成. ¢  WTP: 开启CAPWAP状态机. ¢  AC:  开启CAPWAP状态机. ...

  3. eclipse工具的使用心得

    一.eclipse工具的使用 eclipse是一个开源的IDE,进行javaEE开发一般使用myeclipse插件比较方便 1. java代码的位置 1)选择工作空间workspace 选择一个文件夹 ...

  4. Python3.6下scrapy框架的安装

    首先考虑使用最简单的方法安装 pip install scrapy 命令安装,提示 Failed building wheel for Twisted Microsoft Visual C++ 14. ...

  5. Nginx从听说到学会

    第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一 ...

  6. HTTP/HTTPS 学习笔记

    超文本传输协议(HyperText Transfer Protocol) 伴随着计算机网络和浏览器的诞生,HTTP1.0也随之而来,处于计算机网络中的应用层,HTTP是建立在TCP协议之上的. HTT ...

  7. Oracle的dual表是个什么东东

    dual是一个虚拟表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录.我们可以用它来做很多事情,如下: 1.查看当前用户,可以在 SQL Plus中执行下面语句 sele ...

  8. nsq源码阅读笔记之nsqd(一)——nsqd的配置解析和初始化

    配置解析 nsqd的主函数位于apps/nsqd.go中的main函数 首先main函数调用nsqFlagset和Parse进行命令行参数集初始化, 然后判断version参数是否存在,若存在,则打印 ...

  9. 【游戏开发】小白学Lua——从Lua查找表元素的过程看元表、元方法

    引言 在上篇博客中,我们简单地学习了一下Lua的基本语法.其实在Lua中有一个还有一个叫元表的概念,不得不着重地探讨一下.元表在实际地开发中,也是会被极大程度地所使用到.本篇博客,就让我们从Lua查找 ...

  10. resteasy简单实例

    1.建一个maven web项目 新建一个maven项目,next,第一个框不要勾选 选择maven-archetype-webapp,建一个web项目 键入项目组织id与项目id 一般此时搭建的只是 ...