JSON Hijacking and How ASP.NET AJAX 1.0 Avoids these Attacks

Recently some reports have been issued by security researchers describing ways hackers can use the JSON wire format used by most popular AJAX frameworks to try and exploit cross domain scripts within browsers.  Specifically, these attacks use HTTP GET requests invoked via an HTML <script src=""> include element to circumvent the "same origin policy" enforced by browsers (which limits JavaScript objects like XmlHttpRequest to only calling URLs on the same domain that the page was loaded from), and then look for ways to exploit the JSON payload content.

ASP.NET AJAX 1.0 includes a number of default settings and built-in features that prevent it from being susceptible to these types of JSON hijacking attacks.  Below are some details of how these attacks are mitigated:

ASP.NET AJAX Web Methods do not enable HTTP GET requests by default

Script files loaded via an HTML <script src=""> element within a browser can only be retrieved via HTTP GET verb requests.

By default ASP.NET AJAX's web services layer does not allow web methods to be invoked via the HTTP GET verb. For example, assume a developer writes a web service method like below:

[WebMethod]
public StockQuote[] GetQuotes(string symbol) {

}

ASP.NET will only allow the above GetQuotes method to be called via the HTTP POST verb, and will reject all attempts to invoke the method via an HTTP GET verb.

To make an ASP.NET AJAX web-method callable via HTTP GET-access, a developer must explicitly attribute each method using ASP.NET's ScriptMethod attribute (and set the UseHttpGet property to true):

[WebMethod] 
[ScriptMethod(UseHttpGet
=true)] 

public StockQuote[] GetQuotes(string symbol) { 


Although this type of modification is easy to make, it requires a developer to intentionally GET enable a web service. ASP.NET AJAX web services can never be non-deliberately GET enabled, and the ASP.NET AJAX documentation explicitly recommends against GET enabling web-service end points for a number of reasons (risk of url tampering being one of them).

Note: the ASP.NET AJAX "UpdatePanel" control, as well as the other server controls that ship with ASP.NET AJAX 1.0, do not use HTTP GET and instead use HTTP POSTs when doing asynchronous postbacks.

ASP.NET AJAX Content-Type Header Validation

There is a built-in validation layer of protection that ASP.NET enforces for both GET and POST based ASP.NET AJAX web methods, which is that regardless of the HTTP verb being used, ASP.NET always requires that the HTTP Content-Type header is set to the value application/json. It this content type header is not sent, ASP.NET AJAX will reject the request on the server.

Using the stock quote method shown earlier, an HTTP trace of an ASP.NET AJAX GET invocation must look like the following:

GET /StockService/Stock.asmx/GetQuotes?symbol=%22msft%22 HTTP/1.1 
Accept: */* 
Accept-Language: en-us,fr;q=0.5 
Referer: http://xxxxxx/StockService/test.aspx 
Content-Type: application/json; charset=utf-8 
UA-CPU: x86 
Accept-Encoding: gzip, deflate 
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2) 
Host: xxxxxx 
Proxy-Connection: Keep-Alive 

Note that even though the above is a GET request, the client-side ASP.NET AJAX JSON stack still inserts a Content-Type HTTP header that tells the server to consider this an AJAX web service request. The server-side web services stack for ASP.NET AJAX 1.0 always checks for this specific content type, and if it is not found it will reject the request.

If a malicious developer attempted a cross site request forgery attack using HTTP GETs against this web service, they might include a script tag in their page like the following:

<script type="text/javascript" src="http://contoso.com/StockService/Stock.asmx/GetQuotes?symbol=msft" /> 

However, browsers will not set the Content-Type to application/json when parsing a <script src=""> element and making the request. As a result when ASP.NET receives a request made from a <script /> include, it will not recognize it as a request to an ASP.NET AJAX web service, and it will result in an error from ASP.NET stating that it does not recognize the requested URL.  This will prevent JSON Hijacking attempts (even if you have the GET verb enabled for a web method).

Summary

ASP.NET AJAX 1.0 by default only allows the HTTP POST verb to be used when invoking web methods using JSON, which means you can't inadvertently allow browsers to invoke methods via HTTP GET.

ASP.NET AJAX 1.0 requires a Content-Type header to be set to "application/json" for both GET and POST invocations to AJAX web services.  JSON requests that do not contain this header will be rejected by an ASP.NET server.  This means you cannot invoke an ASP.NET AJAX web method via a <script src=""> include because browsers do not allow append custom content-type headers when requesting a JavaScript file like this.

Hope this helps,

Scott

23 Comments

  • Come on. Those are really trivial barriers to get around.

    I could write a simple HTTP app in under an hour that could do JSON queries by POST rather than GET and use a custom Content-Type header.

    That is hardly security.

  • Hi Scott,

    Thanks for posting this topic. I've asked about it in a couple different forums, but most people just tell me to not worry about it.

    I have a stupid question as I've only developed two webservices applications. I thought by decorating the "[WebMethod]" attribute in any .asmx method, then the method is available to the public, so if I add "web reference" of the url in my visual studio, I can do whatever i want with this remote services? or is that not the case?

    for stuff like read only webservices, I'm not so worry about them, but my ajax application somtimes SAVES data back to the database, those are the ones I'm really worried about. Don't want other people randomly saving bad data and mess up or even crash my database.



  • Hi Liming,

    If you expose capabilities via web-services, you do want to be very careful about who you allow to call them - since it does make them public for remote users to access.

    You can use authentication and authorization features within .NET to grant/deny users access to these methods - I'd definiely make sure you are doing this for any sensitive data, or any update operations.

    Thanks,

    Scott

  • programmer:
    "I could write a simple HTTP app in under an hour that could do JSON queries by POST rather than GET and use a custom Content-Type header."

    well... you then just would have to distribute that app to the users you are going to scam, and then get them to ditch their favourite browser in favour of your app before they log on to secret golden site. Why not just ask them for the secret data and save yourself an hour of programming :-)

  • Hello Scott and Jonas,

    Thanks for clearing that up.

    Some of my ajaxed app, the ajax operation restrict operation based on roles in my web UI layer, but not directly in the webservices layer. So from UI point of view, it's secure, but since anybody can access the webservices directly, I just had to strip off the ajax for this reason.

    I guess my architecture is just wrong to begin with...

    Jonas, I'm looking at your articles now, hopefully, I can solve this issue and get back on the ajax wagon.

    Thanks again.

  • Actually you don't even need to write an app, just download HTTP Fiddler, listen to a request and then alter it. These are trivial checks, what is needed is signature/hash checking or similar.

  • PB - please read my response above to programmer.

    JSON hijacking exploits are not server exploits. They are browser exploits with how browsers handle/interpret client-side javascript.

    This means you *can't* use fiddler or a separate client .exe to exploit JSON hijacking. You must cause a user to run javascript in their browser.

    You can't do this via elements and ASP.NET AJAX because of the header mitigation used above. The browser will not allow a hacker to send custom http headers in the sandbox using this element.

    Hope this helps,

    Scott

  • pb: again, that's not an exploit. It requires you convince victim X to download and install fiddler, run it, and proceed with the spoof.

  • I kind of figured that this would be handled for me. The last thing that Microsoft wants is to open up every new ASP.NET website to hijacking after telling everyone to start using AJAX. I'm glad to see it brought up though, thanks!

  • Ryan: it *is* being handled for you.

  • Hi JoJo,

    As best as we can tell, the fortify folks looked at a year old version of Atlas when they wrote that article (when early builds of Atlas didn't have the http header validation feature).

    ASP.NET AJAX 1.0 is not vulnerable.

    Thanks,

    Scott

  • Thats great news and thanks for the quick response.

  • Hi Bertrand, that's what I meant. My comment was positive, as in: I figured it was handled for me and I am glad to know I was right. Sorry for the confusion. :)

  • You misunderstand - I am not talking about attacks on a user of the app, I am talking about attacks on the app itself - Fiddler allows the malicious person to browse to your app, listent to the HTTP stream and change it. Checking user agents and anything involved in the HTTP stream will not stop this from occurring, it has no idea whether or not IE or Fiddler is making the request.

  • Hi PB,

    This attack is a *client-side* javascript issue. The only way a user could use Fiddler to exploit the problem would be to modify an HTTP stream to cause a cross site scripting issue within their own browser. There is no way they could use this to cause a problem in another users browser.

    Hope this helps,

    Scott

  • i have written some Winforms frontend to use JSON. It's not a big deal
    http://weblogs.asp.net/hpreishuber/archive/2007/01/19/call-page-webmethod-from-a-winforms-client.aspx

  • So if I have in my web.config (I'm using asp .net 2.0 memberships and roles) then I need not fear unauthenticated users having access to my web services?

    Would placing my .asmx files into folders with other role restrictions make them also apply to the web service itself?

    I realize that this doesn't prevent XSS attacks, but will keep external users from being able to probe my web services at will, correct?

  • Hi Scott,

    Does ASP.NET AJAX work support cross-domain request?

    Regards,
    Khyati

  • Hi Khyati,

    No - AJAX frameworks (including ASP.NET) can't bypass the browser's built-in Javascript sandboxing. This means you can't do cross-domain calls from the same sandbox.

    Hope this helps,

    Scott

  • Hi Abishek,

    This exploit requires that you request the url outside of the Javascript sandbox in order to steal information.

    While you could write code to make a request like above (ASP.NET AJAX has a built-in library to-do that as well), it would run in the context of the site sandbox - which means you couldn't exploit anything.

    The particular JSON hijacking attacks that people are doing above involve using elements to try and circumvent the sandbox using standard GET requests.

    Hope this helps,

    Scott

  • while i agree that this is a good security feature, is there anyway to override it such that I can allow GET calls without a content-type header? i'm using Flash to make the call and it does not support adding content-type headers so all my calls are failing. I'm not returning any sensitive data and I feel comfortable with the security issues.

  • If I set up my .NET web service to return json and callback functions, like Yahoo (and google maps?) does, and then use the dynamic script tag, does that leave me vulnerable?

  • @imran - fscommand to a javascript function that makes a correct call?

Comments have been disabled for this content.