Race The Web (RTW)
通过同时向目标 URL 发送用户指定数量的请求来测试 Web 应用程序中的竞争条件,然后比较服务器的响应是否唯一。包括许多配置选项。
更新:现在兼容 CI!
2.0.0 版本现在通过使用易于使用的 HTTP API,可以比以往更轻松地将 RTW 集成到您的持续集成管道(例如Jenkins、Travis或Drone )。更多信息可以在下面的使用部分中找到。
观看演讲
网络竞速 – Hackfest 2016
幻灯片:https ://www.slideshare.net/AaronHnatiw/racing-the-web-hackfest-2016
用法
使用配置文件
$ race-the-web config.toml
API
$ race-the-web
配置文件
包含的示例配置文件(config.toml):
# Sample Configurations # Send 100 requests to each target count = 100 # Enable verbose logging verbose = true # Use an http proxy for all connections proxy = "http://127.0.0.1:8080" # Specify the first request [[requests]] # Use the GET request method method = "GET" # Set the URL target. Any valid URL is accepted, including ports, https, and parameters. url = "https://example.com/pay?val=1000" # Set the request body. # body = "body=text" # Set the cookie values to send with the request to this target. Must be an array. cookies = ["PHPSESSIONID=12345","JSESSIONID=67890"] # Set custom headers to send with the request to this target. Must be an array. headers = ["X-Originating-IP: 127.0.0.1", "X-Remote-IP: 127.0.0.1"] # Follow redirects redirects = true # Specify the second request [[requests]] # Use the POST request method method = "POST" # Set the URL target. Any valid URL is accepted, including ports, https, and parameters. url = "https://example.com/pay" # Set the request body. body = "val=1000" # Set the cookie values to send with the request to this target. Must be an array. cookies = ["PHPSESSIONID=ABCDE","JSESSIONID=FGHIJ"] # Set custom headers to send with the request to this target. Must be an array. headers = ["X-Originating-IP: 127.0.0.1", "X-Remote-IP: 127.0.0.1"] # Do not follow redirects redirects = false
TOML 规范:https://github.com/toml-lang/toml
API
自 2.0.0 版起,RTW 现已拥有功能齐全的 API,让您可以轻松将其集成到您选择的持续集成 (CI) 工具中。这意味着,您可以在提交代码时快速轻松地自动测试 Web 应用程序是否存在竞争条件。
该 API 通过一组简单的 HTTP 调用工作。您以 JSON 形式提供输入并以 JSON 形式接收响应。3 个 API 端点如下:
POST
http://127.0.0.1:8000/set/config
:为您想要运行的竞争条件测试提供配置数据(JSON 格式)(见下面的示例)。GET
http://127.0.0.1:8000/get/config
:获取当前配置数据。数据以 JSON 响应的形式返回。POST
http://127.0.0.1:8000/start
:使用您已提供的配置开始竞争条件测试。所有结果均以 JSON 输出形式返回。
/set/config
JSON 配置示例(使用请求发送POST
)
{ "count": 100, "verbose": false, "requests": [ { "method": "POST", "url": "http://racetheweb.io/bank/withdraw", "cookies": [ "sessionId=dutwJx8kyyfXkt9tZbboT150TjZoFuEZGRy8Mtfpfe7g7UTPybCZX6lgdRkeOjQA" ], "body": "amount=1", "redirects": true } ] }
使用 curl 的示例工作流程
- 发送配置数据
$ curl -d '{"count":100,"verbose":false,"requests":[{"method":"POST","url":"http://racetheweb.io/bank/withdraw","cookies":["sessionId=Ay2jnxL2TvMnBD2ZF-5bXTXFEldIIBCpcS4FLB-5xjEbDaVnLbf0pPME8DIuNa7-"],"body":"amount=1","redirects":true}]}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8000/set/config {"message":"configuration saved"}
- 检索配置数据以进行验证
$ curl -X GET http://127.0.0.1:8000/get/config {"count":100,"verbose":false,"proxy":"","requests":[{"method":"POST","url":"http://racetheweb.io/bank/withdraw","body":"amount=1","cookies":["sessionId=Ay2jnxL2TvMnBD2ZF-5bXTXFEldIIBCpcS4FLB-5xjEbDaVnLbf0pPME8DIuNa7-"],"headers":null,"redirects":true}]}
- 开始竞争条件测试
$ curl -X POST http://127.0.0.1:8000/start
响应(已展开以提高可见性):
[<br> {<br> "Response": {<br> "Body": "\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n \n <title>Bank Test</title>\n\n \n <link href=\"/static/css/bootstrap.min.css\" rel=\"stylesheet\">\n\n \n \n \n\n \n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:site\" content=\"@insp3ctre\" />\n <meta name=\"twitter:title\" content=\"Race Condition Exploit Practice\" />\n <meta name=\"twitter:description\" content=\"Learn how to exploit race conditions in web applications.\" />\n <meta name=\"twitter:image\" content=\"/static/img/bank_homepage_screenshot_wide.png\" />\n <meta name=\"twitter:image:alt\" content=\"Image of the bank account exploit application.\" />\n </head>\n <body>\n <nav class=\"navbar\">\n <div class=\"container-fluid\">\n <div class=\"navbar-header\">\n <a class=\"navbar-brand\" href=\"/\">Race-The-Web</a>\n </div>\n <ul class=\"nav navbar-nav\">\n <li><a href=\"/bank\">Bank</a></li>\n </ul>\n <ul class=\"nav navbar-nav navbar-right\">\n <li><a href=\"https://www.youtube.com/watch?v=4T99v957I0o\"><img src=\"http://racetheweb.io/static/img/logo-youtube.png\" alt=\"Racing the Web - Hackfest 2016\" title=\"Racing the Web - Hackfest 2016\"></a></li>\n <li><a href=\"https://github.com/insp3ctre/race-the-web\"><img src=\"/static/img/logo-github.png\" alt=\"Race-The-Web on Github\"></a></li>\n </ul>\n </div>\n </nav>\n\n <div class=\"container\">\n <div class=\"row\">\n <div class=\"page-header\">\n <h1 class=\"text-center\">Welcome to SpeedBank, International</h1>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <p class=\"text-center bg-success\">You have successfully withdrawn $1</p>\n </div>\n </div>\n \n \n <div class=\"row\">\n <h2 class=\"text-center\">Balance: 9999</h2>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-8 col-xs-offset-3\">\n <form action=\"/bank/withdraw\" method=\"POST\" class=\"form-inline\">\n <div class=\"form-group\">\n <label class=\"sr-only\" for=\"withdrawAmount\">Amount (in dollars)</label>\n <div class=\"input-group\">\n <div class=\"input-group-addon\">$</div>\n <input type=\"text\" class=\"form-control\" id=\"withdrawAmount\" name=\"amount\" placeholder=\"Amount\">\n <div class=\"input-group-addon\">.00</div>\n </div>\n <div class=\"input-group\">\n <input type=\"submit\" class=\"btn btn-primary\" value=\"Withdraw cash\">\n </div>\n </div>\n </form>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <h2 class=\"text-center\">Instructions</h2>\n <ol>\n <li>Click “Initialize” to initialize a bank account with $10,000.</li>\n <li>Withdraw money from your account, observe that your account balance is updated, and that you have received the amount requested.</li>\n <li>Repeat the request with <a href=\"https://github.com/insp3ctre/race-the-web\">race-the-web</a>. Your config file should look like the following:</li>\n<pre>\n# Make one request\ncount = 100\nverbose = true\n[[requests]]\n method = \"POST\"\n url = \"http://racetheweb.io/bank/withdraw\"\n # Withdraw 1 dollar\n body = \"amount=1\"\n # Insert your sessionId cookie below.\n cookies = [“sessionId=<insert here>\"]\n redirects = false\n</pre>\n <li>Visit the bank page again in your browser to view your updated balance. Note that the total <em>should</em> be $100 less ($1 * 100 requests) than when you originally withdrew money. However, due to a race condition flaw in the application, your balance will be much more, yet you will have received the money from the bank in every withdrawal.</li>\n </ol>\n </div>\n </div>\n </div>\n \n <script type=\"text/javascript\">\n \n history.replaceState(\"Bank\", \"Bank\", \"/bank\")\n </script>\n \n\n <p class=\"small text-center\">\n <span class=\"glyphicon glyphicon-copyright-mark\" aria-hidden=\"true\"></span><a href=\"https://www.twitter.com/insp3ctre\">Aaron Hnatiw</a> 2017\n </p>\n \n <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js\"></script>\n \n <script src=\"/static/js/bootstrap.min.js\"></script>\n \n <script>\n (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n\n ga('create', 'UA-93555669-1', 'auto');\n ga('send', 'pageview');\n\n </script>\n </body>\n</html>\n",<br> "StatusCode": 200,<br> "Length": -1,<br> "Protocol": "HTTP/1.1",<br> "Headers": {<br> "Content-Type": [<br> "text/html; charset=utf-8"<br> ],<br> "Date": [<br> "Fri, 18 Aug 2017 15:36:29 GMT"<br> ]<br> },<br> "Location": ""<br> },<br> "Targets": [<br> {<br> "method": "POST",<br> "url": "http://racetheweb.io/bank/withdraw",<br> "body": "amount=1",<br> "cookies": [<br> "sessionId=Ay2jnxL2TvMnBD2ZF-5bXTXFEldIIBCpcS4FLB-5xjEbDaVnLbf0pPME8DIuNa7-"<br> ],<br> "headers": null,<br> "redirects": true<br> }<br> ],<br> "Count": 1<br> },<br> {<br> "Response": {<br> "Body": "\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n \n <title>Bank Test</title>\n\n \n <link href=\"/static/css/bootstrap.min.css\" rel=\"stylesheet\">\n\n \n \n \n\n \n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:site\" content=\"@insp3ctre\" />\n <meta name=\"twitter:title\" content=\"Race Condition Exploit Practice\" />\n <meta name=\"twitter:description\" content=\"Learn how to exploit race conditions in web applications.\" />\n <meta name=\"twitter:image\" content=\"/static/img/bank_homepage_screenshot_wide.png\" />\n <meta name=\"twitter:image:alt\" content=\"Image of the bank account exploit application.\" />\n </head>\n <body>\n <nav class=\"navbar\">\n <div class=\"container-fluid\">\n <div class=\"navbar-header\">\n <a class=\"navbar-brand\" href=\"/\">Race-The-Web</a>\n </div>\n <ul class=\"nav navbar-nav\">\n <li><a href=\"/bank\">Bank</a></li>\n </ul>\n <ul class=\"nav navbar-nav navbar-right\">\n <li><a href=\"https://www.youtube.com/watch?v=4T99v957I0o\"><img src=\"http://racetheweb.io/static/img/logo-youtube.png\" alt=\"Racing the Web - Hackfest 2016\" title=\"Racing the Web - Hackfest 2016\"></a></li>\n <li><a href=\"https://github.com/insp3ctre/race-the-web\"><img src=\"/static/img/logo-github.png\" alt=\"Race-The-Web on Github\"></a></li>\n </ul>\n </div>\n </nav>\n\n <div class=\"container\">\n <div class=\"row\">\n <div class=\"page-header\">\n <h1 class=\"text-center\">Welcome to SpeedBank, International</h1>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <p class=\"text-center bg-success\">You have successfully withdrawn $1</p>\n </div>\n </div>\n \n \n <div class=\"row\">\n <h2 class=\"text-center\">Balance: 9998</h2>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-8 col-xs-offset-3\">\n <form action=\"/bank/withdraw\" method=\"POST\" class=\"form-inline\">\n <div class=\"form-group\">\n <label class=\"sr-only\" for=\"withdrawAmount\">Amount (in dollars)</label>\n <div class=\"input-group\">\n <div class=\"input-group-addon\">$</div>\n <input type=\"text\" class=\"form-control\" id=\"withdrawAmount\" name=\"amount\" placeholder=\"Amount\">\n <div class=\"input-group-addon\">.00</div>\n </div>\n <div class=\"input-group\">\n <input type=\"submit\" class=\"btn btn-primary\" value=\"Withdraw cash\">\n </div>\n </div>\n </form>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <h2 class=\"text-center\">Instructions</h2>\n <ol>\n <li>Click “Initialize” to initialize a bank account with $10,000.</li>\n <li>Withdraw money from your account, observe that your account balance is updated, and that you have received the amount requested.</li>\n <li>Repeat the request with <a href=\"https://github.com/insp3ctre/race-the-web\">race-the-web</a>. Your config file should look like the following:</li>\n<pre>\n# Make one request\ncount = 100\nverbose = true\n[[requests]]\n method = \"POST\"\n url = \"http://racetheweb.io/bank/withdraw\"\n # Withdraw 1 dollar\n body = \"amount=1\"\n # Insert your sessionId cookie below.\n cookies = [“sessionId=<insert here>\"]\n redirects = false\n</pre>\n <li>Visit the bank page again in your browser to view your updated balance. Note that the total <em>should</em> be $100 less ($1 * 100 requests) than when you originally withdrew money. However, due to a race condition flaw in the application, your balance will be much more, yet you will have received the money from the bank in every withdrawal.</li>\n </ol>\n </div>\n </div>\n </div>\n \n <script type=\"text/javascript\">\n \n history.replaceState(\"Bank\", \"Bank\", \"/bank\")\n </script>\n \n\n <p class=\"small text-center\">\n <span class=\"glyphicon glyphicon-copyright-mark\" aria-hidden=\"true\"></span><a href=\"https://www.twitter.com/insp3ctre\">Aaron Hnatiw</a> 2017\n </p>\n \n <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js\"></script>\n \n <script src=\"/static/js/bootstrap.min.js\"></script>\n \n <script>\n (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n\n ga('create', 'UA-93555669-1', 'auto');\n ga('send', 'pageview');\n\n </script>\n </body>\n</html>\n",<br> "StatusCode": 200,<br> "Length": -1,<br> "Protocol": "HTTP/1.1",<br> "Headers": {<br> "Content-Type": [<br> "text/html; charset=utf-8"<br> ],<br> "Date": [<br> "Fri, 18 Aug 2017 15:36:30 GMT"<br> ]<br> },<br> "Location": ""<br> },<br> "Targets": [<br> {<br> "method": "POST",<br> "url": "http://racetheweb.io/bank/withdraw",<br> "body": "amount=1",<br> "cookies": [<br> "sessionId=Ay2jnxL2TvMnBD2ZF-5bXTXFEldIIBCpcS4FLB-5xjEbDaVnLbf0pPME8DIuNa7-"<br> ],<br> "headers": null,<br> "redirects": true<br> }<br> ],<br> "Count": 1<br> },<br> {<br> "Response": {<br> "Body": "\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n \n <title>Bank Test</title>\n\n \n <link href=\"/static/css/bootstrap.min.css\" rel=\"stylesheet\">\n\n \n \n \n\n \n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:site\" content=\"@insp3ctre\" />\n <meta name=\"twitter:title\" content=\"Race Condition Exploit Practice\" />\n <meta name=\"twitter:description\" content=\"Learn how to exploit race conditions in web applications.\" />\n <meta name=\"twitter:image\" content=\"/static/img/bank_homepage_screenshot_wide.png\" />\n <meta name=\"twitter:image:alt\" content=\"Image of the bank account exploit application.\" />\n </head>\n <body>\n <nav class=\"navbar\">\n <div class=\"container-fluid\">\n <div class=\"navbar-header\">\n <a class=\"navbar-brand\" href=\"/\">Race-The-Web</a>\n </div>\n <ul class=\"nav navbar-nav\">\n <li><a href=\"/bank\">Bank</a></li>\n </ul>\n <ul class=\"nav navbar-nav navbar-right\">\n <li><a href=\"https://www.youtube.com/watch?v=4T99v957I0o\"><img src=\"http://racetheweb.io/static/img/logo-youtube.png\" alt=\"Racing the Web - Hackfest 2016\" title=\"Racing the Web - Hackfest 2016\"></a></li>\n <li><a href=\"https://github.com/insp3ctre/race-the-web\"><img src=\"/static/img/logo-github.png\" alt=\"Race-The-Web on Github\"></a></li>\n </ul>\n </div>\n </nav>\n\n <div class=\"container\">\n <div class=\"row\">\n <div class=\"page-header\">\n <h1 class=\"text-center\">Welcome to SpeedBank, International</h1>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <p class=\"text-center bg-success\">You have successfully withdrawn $1</p>\n </div>\n </div>\n \n \n <div class=\"row\">\n <h2 class=\"text-center\">Balance: 9997</h2>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-8 col-xs-offset-3\">\n <form action=\"/bank/withdraw\" method=\"POST\" class=\"form-inline\">\n <div class=\"form-group\">\n <label class=\"sr-only\" for=\"withdrawAmount\">Amount (in dollars)</label>\n <div class=\"input-group\">\n <div class=\"input-group-addon\">$</div>\n <input type=\"text\" class=\"form-control\" id=\"withdrawAmount\" name=\"amount\" placeholder=\"Amount\">\n <div class=\"input-group-addon\">.00</div>\n </div>\n <div class=\"input-group\">\n <input type=\"submit\" class=\"btn btn-primary\" value=\"Withdraw cash\">\n </div>\n </div>\n </form>\n </div>\n </div>\n \n <div class=\"row\">\n <div class=\"col-xs-12 col-sm-8 col-sm-offset-2\">\n <h2 class=\"text-center\">Instructions</h2>\n <ol>\n <li>Click “Initialize” to initialize a bank account with $10,000.</li>\n <li>Withdraw money from your account, observe that your account balance is updated, and that you have received the amount requested.</li>\n <li>Repeat the request with <a href=\"https://github.com/insp3ctre/race-the-web\">race-the-web</a>. Your config file should look like the following:</li>\n<pre>\n# Make one request\ncount = 100\nverbose = true\n[[requests]]\n method = \"POST\"\n url = \"http://racetheweb.io/bank/withdraw\"\n # Withdraw 1 dollar\n body = \"amount=1\"\n # Insert your sessionId cookie below.\n cookies = [“sessionId=<insert here>\"]\n redirects = false\n</pre>\n <li>Visit the bank page again in your browser to view your updated balance. Note that the total <em>should</em> be $100 less ($1 * 100 requests) than when you originally withdrew money. However, due to a race condition flaw in the application, your balance will be much more, yet you will have received the money from the bank in every withdrawal.</li>\n </ol>\n </div>\n </div>\n </div>\n \n <script type=\"text/javascript\">\n \n history.replaceState(\"Bank\", \"Bank\", \"/bank\")\n </script>\n \n\n <p class=\"small text-center\">\n <span class=\"glyphicon glyphicon-copyright-mark\" aria-hidden=\"true\"></span><a href=\"https://www.twitter.com/insp3ctre\">Aaron Hnatiw</a> 2017\n </p>\n \n <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js\"></script>\n \n <script src=\"/static/js/bootstrap.min.js\"></script>\n \n <script>\n (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n\n ga('create', 'UA-93555669-1', 'auto');\n ga('send', 'pageview');\n\n </script>\n </body>\n</html>\n",<br> "StatusCode": 200,<br> "Length": -1,<br> "Protocol": "HTTP/1.1",<br> "Headers": {<br> "Content-Type": [<br> "text/html; charset=utf-8"<br> ],<br> "Date": [<br> "Fri, 18 Aug 2017 15:36:36 GMT"<br> ]<br> },<br> "Location": ""<br> },<br> "Targets": [<br> {<br> "method": "POST",<br> "url": "http://racetheweb.io/bank/withdraw",<br> "body": "amount=1",<br> "cookies": [<br> "sessionId=Ay2jnxL2TvMnBD2ZF-5bXTXFEldIIBCpcS4FLB-5xjEbDaVnLbf0pPME8DIuNa7-"<br> ],<br> "headers": null,<br> "redirects": true<br> }<br> ],<br> "Count": 98<br> }<br>]
二进制文件
该程序是用 Go 编写的,因此可以编译到目前使用的所有常见平台上。以下架构已编译,可在发布选项卡中找到:
- Windows amd64
- Windows 386
- Linux 的 amd64
- Linux 386
- OSX amd64
- OSX 386
编译
首先,确保您的系统上已安装 Go。如果没有,您可以按照此处所选操作系统的安装说明进行操作: https: //golang.org/doc/install。
为您当前的CPU架构构建一个二进制文件
$ make build
一次性为所有主流 CPU 架构构建(详情请参阅Makefile )
$ make
DEP
该项目使用Dep进行依赖管理。所有必需的文件都保存在vendor
目录中,但是如果您收到与依赖项相关的错误,只需下载 Dep
$ go get -u github.com/golang/dep/cmd/dep
并从 RTW 目录运行以下命令以下载所有依赖项
$ dep ensure
支持 Go 1.7 及更新版本
在 1.7 之前,该encoding/json
包Encoder
没有转义、和字符的方法&
;<
在>
运行这些竞争测试时,这是为了获得完整 HTML 页面的干净输出所必需的。如果这对您的测试用例来说是一个问题,请提交一个新问题并指出这一点,我会添加一个解决方法(请注意,任何来自服务器的带有这些字符的输出都将返回 unicode 转义)。以下是 Go 1.7 的相关发布详细信息: https: //golang.org/doc/go1.7#encoding_json。
漏洞
竞争条件是一种缺陷,当操作的时间影响其他操作时,会产生意外结果。在多线程应用程序中可以看到一个例子,其中对同一数据执行操作。竞争条件本质上很难测试。
竞争条件是软件开发中众所周知的问题,尤其是在处理快速、多线程语言时。
然而,随着网络速度越来越快,Web 应用程序越来越容易受到竞争条件的影响。这通常是因为遗留代码不是为处理数百或数千个针对相同功能或资源的同时请求而创建的。
通常只有在使用快速、多线程语言并通过快速网络连接生成这些请求时才会发现问题;此时它就变成了客户端应用程序和服务器应用程序之间的网络和逻辑竞争。
这就是Race The Web 的作用所在。该程序旨在通过同时向特定端点发送大量请求来发现 Web 应用程序中的竞争条件。这样做可能会在服务器上引发意外行为,例如重复用户信息、优惠券代码、比特币等。
警告:拒绝服务可能是使用此应用程序的意外副作用,因此请在使用时小心,并始终在服务器所有者和 Web 应用程序所有者的明确许可下执行此类测试。
感谢Josip Franjković就此主题所写的精彩文章,它让我了解到了这个问题。