I am testing web application for which business actions are done by sending JSON requests like for example:

POST /dataRequest HTTP/1.1
Host: test.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 
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


I made the HTML auto-submit page like this

<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'>

The problem is that it will be send with header Content-Type: text/plain, but the server only accepts Content-Type: application/json; charset=utf-8.

I've read the discussion CSRF with JSON POST where one of the comments states:

Use something like this: var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'}); to generate a JSON blob and it'll send perfectly. CSRF in seconds!

But I have no idea how to use this approach.

Is this application vulnerable to CSRF attack?

  • None of what you've posted looks relevant to xsrf. Are you even using cookie auth? What happens if you set the encType to application/json? Have you looked at any of the mitigation here. owasp.org/index.php/…
    – ste-fu
    Oct 1, 2017 at 19:33

3 Answers 3


Is this application vulnerable to CSRF attack?

Yes, it's vulnerable. The prerequisite, however, here is Flash. With the help of Flash, it's possible to forge a Content-type header with any arbitrary value. What you need to do is POST a request to your own domain, and then issue a 307 redirect. Please refer to below screenshot:

x-domain application/json request

For more information, please refer to this cm2.pw article.

Use something like this: var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'}); to generate a JSON blob and it'll send perfectly. CSRF in seconds!

This, afaik, is already fixed in modern browsers. However, it still works in IE with file URI.

Since it's still getting a lot of views lately, I'm adding links to fixes; https://bugzilla.mozilla.org/show_bug.cgi?id=1436241 https://bugs.chromium.org/p/chromium/issues/detail?id=332023

For some reason, chromium fix does not work and still allow sending POST request with custom Content-type headers.

  • This is a great point that I completely missed in my answer. Thanks for pointing it out!
    – Anders
    Jan 4, 2018 at 19:58
  • 1
    @Anders What's still unclear to me - isn't this Flash behavior fixed by now?
    – Arminius
    Jan 4, 2018 at 20:09
  • @Arminius Don't know, but there is still old Flash and old browsers out there. I think that is enough to merit swift action for OP.
    – Anders
    Jan 4, 2018 at 20:13
  • 1
    @Arminius It's not. Only the behavior allowing to set arbitrary HTTP Headers was fixed. Jan 5, 2018 at 2:47
  • Another example about the Flash CSRF attack can be found in a blog by Appsecco; source code available on GitHub. Sep 5, 2019 at 20:21

Warning: This answer may be wrong and to optimistic. See 1lastBr3ath's answer above (concerning flash) and jub0bs comment below (concerning a clever workaround).

No, I don't think the application is vulnerable.

You can change the Content-Type header, e.g. using the fetch API. However, there are only three values that you can use for cross domain requests:


If you change it to anything else, such as application/json, the browser will first make an OPTIONS request to server, to see if it allows that header to be changed. This behaviour is part of CORS, and it is designed to limit what cross domain requests you can make with JavaScript to the old fashioned ones you could do with simple HTML. So unless the server specifically allows any domain to set this header (which would be a stupid thing to do), you are out of luck.

Note, however, that this seems to be a case of "security by accident". I would rely on something stronger for my CSRF protection (and perhaps they do, once you get past the content type hurdle). What happends if someday someone thinks it would be nice if the server accepts other content types and removes that limitation? With this configuration, it would be easy to accidentally open up a security hole.

  • 2
    However, there are only three values [...] CORS is actually more permissive than meets the eye. In particular, it breaks some pre-CORS assumptions about the possible Content-Type values that a simple request can carry. For instance, browsers will happily send a no-CORS request with content type text/plain; application/json; see untruetauttriangle.jub0bs.repl.co. If the server only checks whether application/json is a substring of the Content-Type header value, the attack would be successful.
    – jub0bs
    Aug 30, 2021 at 9:28
  • 1
    @jub0bs That is very interesting - I did not know that. Will read up on this, sounds fashinating. Updated the warning at the top.
    – Anders
    Aug 31, 2021 at 16:49
  • 3
    For more about this, see section 4.1 in usenix.org/system/files/conference/usenixsecurity18/…
    – jub0bs
    Sep 1, 2021 at 12:38

An another way to achieve this is by taking advantage of the fact that most of json parsers respect the use of comments. So by creating a simple html form with a hidden input, you can put the json data as name of the input element in order to get it posted to the body. In this senario the only problem is that when the form is submitted the body of the post will have the '=' (from the input's name=value format). So to avoid that and make the json valid again u can add a comment indicator at the end of the name (your json data). This way the '=' character will be commented when parsed.

Here is an example:

  <script>history.pushState('', '', '/')</script>
    <form action="https://www.example.com/" method="POST" enctype="text/plain">
      <input type="hidden" name='{"name":"value"}//' value="" />
  • The question was not about making the parser validate the json but how to send it such as the browser set the Content-Type: application/json header in the request. I've tested your solution and despite being valid json and having set enctype to application/json it still send it as text/plain.
    – Xavier59
    Jan 8, 2019 at 8:40
  • There are no comments in JSON, it should be: <input type="hidden" name="&#123;&quot;test&quot;&#58;&quot;X" value="&quot;&#125;" /> or, <input type="hidden" name="&#123;&quot;test&quot;&#58;&quot;X&quot;&#44;&quot;ignore&quot;&#58;&quot;" value="&quot;&#125;" /> Reference: gracefulsecurity.com/csrf-vs-json
    – aDoN
    Sep 17, 2019 at 14:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .