WEB安全新玩法 [8] 阻止订单重复提交

交易订单的重复提交虽然一般不会直接影响现金流和商品流,但依然会给网站运营方带来损害,如消耗系统资源、影响正经常使用户订单生成、制造恶意用户发起纠纷的机会等。假若订单对象是虚拟商品,也有可能形成实际损失。订单重复提交的检查工做本应该由网站自身实现,而 iFlow 业务安全加固平台则能够为未实现这项功能的网站提供防御。php


以某开源购物网站为例,攻击者可以轻松实现订单的重复提交。咱们看看如何在不修改网站源代码的前提下,使用 iFlow 经过透明加入一次性令牌来阻止订单的重复提交攻击。程序员

1、不检查订单重复提交的原始网站

原始网站系统没有检查订单的重复提交,攻击者能够简单地重复提交订单。编程

1.1 正经常使用户访问

已登陆用户在选择购买一件商品后,进入到确认订单页面:json

pic1

用户点击提交订单按钮后,网站回复订单已生成:后端

pic2

能够在个人订单列表中看到刚才的订单:浏览器

pic3

订单生成的交互过程反映在 HTTP 协议层面以下:安全

sequenceDiagram participant 正经常使用户 participant 浏览器 participant Web服务器 正经常使用户->>浏览器: 选择购买商品 浏览器->>Web服务器: 请求:/index.php?s=/member/paymentorder Web服务器->>浏览器: 返回:确认订单页面 浏览器->>Web服务器: 请求:/js/payment_orders/payment_orders.js Web服务器->>浏览器: 返回:payment_orders.js 浏览器->>正经常使用户: 显示:确认订单页面 正经常使用户->>浏览器: 点击提交订单按钮 浏览器->>Web服务器: js发送AJAX:提交订单信息 Note over Web服务器: 系统生成正常订单 Web服务器->>浏览器: 重定向页面:订单已生成 浏览器->>正经常使用户: 显示:订单已生成

1.2 攻击者访问

攻击者使用 Burpsuite 工具做为浏览器和 Web 服务器之间的代理。服务器

攻击者象正经常使用户同样选择商品和确认提交后,可以在 Burpsuite 中的 HTTP history 中找到这个提交订单信息的请求。攻击者右键点击 Send to Repeater 后进入 Repeater 标签页。dom

pic4

攻击者经过屡次点击 Send 按钮来重复发出请求报文从而重复产生订单,并能够在个人订单中看到多个重复生成的订单,以下图所示:编程语言

pic5

HTTP 协议层面交互以下:

sequenceDiagram participant 攻击者 participant 浏览器 participant 攻击工具 participant Web服务器 攻击者->>浏览器: 选择购买商品 浏览器->>Web服务器: 请求:/index.php?s=/member/paymentorder Web服务器->>浏览器: 返回:确认订单页面 浏览器->>Web服务器: 请求:/js/payment_orders/payment_orders.js Web服务器->>浏览器: 返回:payment_orders.js 浏览器->>攻击者: 显示:确认订单页面 攻击者->>浏览器: 点击提交订单按钮 浏览器->>攻击工具: js发送AJAX:提交订单信息 rect rgb(250, 128, 128) Note over 攻击工具: 记录请求报文 end 攻击工具->>Web服务器: 发送AJAX:提交订单信息 Note over Web服务器: 系统生成正常订单 Web服务器->>浏览器: 重定向页面:订单已生成 浏览器->>攻击者: 显示:订单已生成 Loop 重复 rect rgb(250, 128, 128) 攻击者->>攻击工具: 发送已记录的请求报文 end 攻击工具->>Web服务器: 提交订单信息 rect rgb(250, 128, 128) Note over Web服务器: 系统生成重复订单 end Web服务器->>攻击工具: 返回响应报文 攻击工具->>攻击者: 忽略响应报文 end

2、iFlow虚拟补丁后的网站

咱们在 Web 服务器前部署 iFlow 业务安全加固平台,它有能力拦截、计算和修改双向 HTTP 报文并具有存储能力,成为 Web 应用的虚拟补丁。在本例中,iFlow 在加载订单支付代码时生成并加入一次性随机令牌,在提交订单时检查这个令牌的存在。

2.1 正经常使用户访问

用户在访问确认订单页面时,浏览器自动加载处理订单支付的 JS 代码 (payment_orders.js)。iFlow 截获这段代码的响应返回,生成一个随机令牌保存在本地存储中,并修改 JS 代码将随机令牌加入到 AJAX 发送列表中。用户在点击提交订单按钮时,JS 代码发出 AJAX 请求将随机令牌随同订单信息一块儿发出,iFlow 截获请求,检查参数中的令牌是否与保存的令牌一致,并清除本地存储中保存的令牌。对于一个正经常使用户来讲,它们必定是相同的,因而 iFlow 去掉令牌参数,将仅包含订单信息的请求发往 Web 服务器处理。

正经常使用户的 HTTP 协议交互过程以下:

sequenceDiagram participant 正经常使用户 participant 浏览器 participant iFlow participant Web服务器 正经常使用户->>浏览器: 选择购买商品 浏览器->>Web服务器: 请求:/index.php?s=/member/paymentorder Web服务器->>浏览器: 返回:确认订单页面 浏览器->>Web服务器: 请求:/js/payment_orders/payment_orders.js Web服务器->>iFlow: 返回:payment_orders.js Note over iFlow: 生成随机令牌并保存 rect rgb(160, 250, 160) iFlow->>浏览器: 修改:AJAX发送列表中加入令牌 end 浏览器->>正经常使用户: 显示:确认订单页面 正经常使用户->>浏览器: 点击提交订单按钮 浏览器->>iFlow: js发送AJAX:订单信息及令牌 rect rgb(160, 250, 160) Note over iFlow: 与保存的令牌比对:经过 Note over iFlow: 清除保存的令牌 end iFlow->>Web服务器: 提交订单信息 Note over Web服务器: 系统生成正常订单 Web服务器->>浏览器: 重定向页面:订单已生成 浏览器->>正经常使用户: 显示:订单已生成

2.2 攻击者访问

如前所示,攻击者记录下正常操做时的提交订单的请求报文,而后用工具重放这段报文。因为在第一次正常提交后,iFlow 已经清除了本地存储中保存的令牌,所以后续的重复提交被 iFlow 拒绝。

攻击者的 HTTP 协议交互过程以下:

sequenceDiagram participant 攻击者 participant 浏览器 participant 攻击工具 participant iFlow participant Web服务器 攻击者->>浏览器: 选择购买商品 浏览器->>Web服务器: 请求:/index.php?s=/member/paymentorder Web服务器->>浏览器: 返回:确认订单页面 Web服务器->>iFlow: 返回:payment_orders.js Note over iFlow: 生成随机令牌并保存 rect rgb(160, 250, 160) iFlow->>浏览器: 修改:AJAX发送列表中加入令牌 end Web服务器->>浏览器: 返回:payment_orders.js 浏览器->>攻击者: 显示:确认订单页面 攻击者->>浏览器: 点击提交订单按钮 浏览器->>攻击工具: js发送AJAX:订单信息及令牌 rect rgb(250, 128, 128) Note over 攻击工具: 记录请求报文 end 攻击工具->>iFlow: 发送AJAX:订单信息及令牌 rect rgb(160, 250, 160) Note over iFlow: 与保存的令牌比对:经过 Note over iFlow: 清除保存的令牌 end iFlow->>Web服务器: 提交订单信息 Note over Web服务器: 系统生成正常订单 Web服务器->>浏览器: 重定向页面:订单已生成 浏览器->>攻击者: 显示:订单已生成 Loop 重复 rect rgb(250, 128, 128) 攻击者->>攻击工具: 发送已记录的请求报文 end 攻击工具->>iFlow: 提交订单信息+令牌 rect rgb(250, 128, 128) Note over iFlow: 检查保存的令牌:无 end iFlow->>攻击工具: 中断链接 end

2.3 代码

iFlow 内置的 W2 语言是一种专门用于实现 Web 应用安全加固的类编程语言。它介于配置和通用语言之间,具有编程的基本要素和针对 HTTP 协议的特有扩展,能为业务系统编写涉及复杂判断和动态修改的逻辑。

考虑到安全产品的使用者一般为非程序员,他们习惯面对配置文件而非一段代码。所以,W2 语言虽包含语言要素,仍以规则文件方式呈现,并采用能够体现层次结构和方便词法校验的 JSON 格式。

用 W2 语言实现上述虚拟补丁的代码以下:

[
    {
        "if": "REQUEST_FILENAME == '/js/payment_orders/payment_orders.js'",
        "then": {
            "execution": [
                "TX.raw_token = md5(random())",
                "SESSION.order_token@300 = TX.raw_token",
                "TX.js_part = '\"' .. TX.raw_token .. '\"'",
                {
                    "directive": "alterResponseBody",
                    "op": "string",
                    "target": "'leavemessage' : leavemessage,", 
                    "substitute": "'leavemessage' : leavemessage, 'order_token' : ${TX.js_part},"
                }
            ]
        }
    },
    {
        "if": [
            "REQUEST_METHOD == 'POST'",
            "REQUEST_FILENAME == '/index.php'",
            "@ARGS.s == '/order/ordercreate'"
        ],
        "then": {
            "if": "SESSION.order_token",
            "then": {
                "if": "@ARGS.order_token != SESSION.order_token",
                "then": {
                    "verdict": {
                        "action": "drop",
                        "log": "${@ARGS.order_token} is not equal to ${SESSION.order_token}!"
                    }
                },
                "else": [
                    "SESSIOIN.order_token = null",
                    {
                        "directive": "alterArgPost",
                        "op": "unset",
                        "name": "order_token"
                    }
                ]
            },
            "else": {
                "verdict": {
                    "action": "drop",
                    "log": "${SESSION.order_token} is not exist!"
                }
            }
        }
    }
]

示例代码中有两条规则,分别做用以下:

第一条规则

当浏览器请求 payment_orders.js 时,iFlow 拦截响应报文。它首先生成一个随机令牌 raw_token 并将其存放在会话 (SESSION) 存储变量 order_token 中,而后修改处理用户提交订单的 AJAX 操做,将随机令牌加入到 POST 的发送参数列表中。

第二条规则

当用户执行提交订单时,JS 发出一个 AJAX 的 POST 请求,iFlow 拦截此请求。它检查会话 (SESSION) 存储变量 order_token 和参数中的 order_token,若是前者不存在或者二者不相等,即断定为非法请求。不然,将存储变量 order_token 清除,将请求参数 order_token 消除 (以避免影响后端应用),而后发给后端 Web 服务器。

注意:上述会话中的 order_token 标志是保存在服务器端的 iFlow 存储中的,在浏览器端是看不到数据更没法进行伪造的。

3、总结

iFlow 使用两条规则在不修改服务器端代码的前提下,透明地实现了随机令牌的一次性发放和使用,避免了简单的重复提交。

固然,若是攻击者彻底模拟用户正常操做,重复发起包含先后 2 次会话的攻击行为,则本文中的规则没法阻挡这种重复提交。但显然这种行为须要更复杂的攻击技巧而不仅依靠简单地重放实现,况且,使用 iFlow 还能构建出更复杂的防御策略。(张戈 | 天存信息)

相关文章
相关标签/搜索