1.商品数量为负数
商品数量为负数的情况多数出现在有站内货币(虚拟币)的网站上,当购买一个产品时,算法一般为"购买数量×商品单价=支付金额",但如果购买数量为负数,比如 5,那么支付金额将会为负数,看下面一段支付代码:
public class Order extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User u = (User) request.getSession().getAttribute("User"); u = new UserBiz().findUserById(u.getId()); int commodityID = Integer.parseInt(request.getParameter("commodityID")); int number = Integer.parseInt(request.getParameter("number")); Commodity comm = new CommodityBiz().finCommodityById(commodityID); //根据ID查询价格商品信息 if(comm.getNumber()<number){ request.setAttribute("message", "商品数量不足,无法购买"); request.getRequestDispatcher("/user/userinfo.do").forward(request, response); } double allprice = number * comm.getPrice() ; //查出总价格 if(u.getMoney()<allprice){ //如果用户金额小于商品金额 request.setAttribute("message", "您的金额不足,请及时充值,还差"+(allprice -user.getMoney())+"个金币"); request.getRequestDispatcher("/user/userinfo.do").forward(request, response); } u.setMoney(u.getMoney()-allprice); //把用户的金币去除 u.getCommodity().add(commodityID); UserDao userDao = new UserBiz(); boolean flag = userDao.saveOrUpdate(u); //更新用户金币 comm.setNumber(comm.getNumber()-number); CommodityDao comDao = new CommodityBiz(); boolean cflag = comDao.saveOrUpdate(comm); if(flag&& cflag){ request.setAttribute("message", "购买成功,您的余额为:"+u.getMoney()); request.setAttribute("User", u); request.setAttribute("Commodity ", comm); request.getRequestDispatcher("/user/userinfo.do").forward(request, response); }else{ request.setAttribute("message", "购买失败,出现异常情况"); request.getRequestDispatcher("/user/userinfo.do").forward(request, response); } } } |
简单介绍一下上述代码的执行流程。
① 从Session中取得User对象,也就是当前用户信息。
② 根据User的ID,在数据库中查询当前用户的最新信息。
③ 取得商品的ID,根据商品ID查询出商品的详细信息,包括价格、总数量。
④ 对商品数量进行判断,如果用户购买数量大于库存数量,则跳转页面,结束购物。
⑤ 根据数量计算出总价格。
⑥ 判断用户的Money是否大于总价格,如Moeny不够,则跳转页面,结束购物。
⑦ 将用户的Money与商品数量重置,然后保存,如果都成功,则进行下一步,否则页面跳转,结束购物。
⑧ 将最新的用户信息与商品信息放入Session中。
这段代码是一个比较常见的购买流程,但却存在安全隐患,那就是用户可以"刷钱"。现在假设用户的Money为100元,商品价格为30元,商品数量为20个。用户购买1个商品后,那么Money就变为70元,商品数量也剩下19个,这属于正规流程。下面来看不正规的流程,也就是最常见的支付逻辑漏洞之一。
将购买数量修改为 3,然后购买,当这段Servlet在计算价格时,并没有对负数进行验证,而是直接进行运算:30×( 3) = 90,现在的商品价格为 90元。接下来判断用户的金额是否大于 90,这里显然是大于的,所以通过验证,进入下一环节。
有趣的事情出现了,u.setMoney(u.getMoney()-allprice)的意思是将用户的Money进行重置,我们来算算,100 -( 90) = 190,我们账户中的钱不但没有减少,反增加了90元,如果购买的商品数量越多,那么你刷的"钱"也就越多。
再来看商品的数量comm. setNumber(comm.getNumber()-number),这句代码的意思是重置商品的数量,也就是原来的商品数量减去购买的数量,为:20 ( 3)=23,商品也是一样,不但没有减少,反而增加了。
2.0元购买商品
0元也能购买商品?对,你没听错。下面将介绍一种比较常见的0元商品的逻辑漏洞。
至今,大多数脚本语言都会支持异常处理机制,而异常处理机制有哪些好处呢?比如,我们的程序正在读取数据库,这时突然断网了,程序无法控制网络是否畅通,如果没有正确处理,则会产生致命的错误,可能程序都会因此而崩溃,而有了异常处理机制后,程序员可以将关键性的代码、可能会出现异常的代码块使用异常处理机制处理,即使这块代码真的出了问题,程序也不会因此问题而使整个软件崩溃,这就是异常处理。
一个存在BUG的Main.java源码如下:
public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("请输入一个整数:"); String str = sc.nextLine(); //接收键盘记录 int num = Integer.parseInt(str); //将字符串转换为整数 System.out.println("您输入的是:"+num); } |
当程序运行后,会提示用户输入一个整数,这时输入5,程序将会告诉你"您输入的是5",程序看起来没有什么错误,但如果某个不规矩的用户不输入整数,而是输入"Hello",那么程序又会怎样呢?毫无疑问,程序会崩溃,因为在Java中尝试将一个不是数字的字符串转为整型时,将会抛出一个致命的错误,java.lang.NumberFormatException类型转换异常,相信这个关键字大家并不陌生,在Java语言的SQL注射时可能会经常遇到这个关键字,如图10-17所示。
图10-17 类型转换错误
本文选自《Web安全深度剖析》第十章,本站经电子工业出版社和作者的授权。
版权声明:51Testing软件测试网获电子工业出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。