Note: the author published this article earlier, some methods may not be the best solution, but attacks against this vulnerability are still visible, such as the early: qqmail email leak vulnerability. Until now, when you open an external chain in some mailboxes, you will still have a security warning. The following is an introduction to the principle of this attack and prevention methods.
Not long ago, I wrote an article, "a subtle JSON vulnerability", in which I mentioned that this vulnerability may lead to the disclosure of sensitive information. According to the characteristics of the vulnerability, the JavaScript array constructor is overridden to steal (expose) the returned array of JSON, which most browsers can't prevent.
However, by communicating with Microsoft's Scott Hanselman, I learned that another approach might affect more browsers. At last week's Norwegian developer conference, I gave a demonstration of the vulnerability of Jason hijacking.
Before I go any further, let me first say the possible impact of this loophole.
This vulnerability occurs when the JSON service is exposed first, and it returns sensitive data; it returns a JSON array; it responds to a get request; the browser that sent the request has JavaScript enabled and supports the definesetter method.
If we do not use JSON to send sensitive data, or only respond to message requests, then our website does not have this vulnerability.
I don't like to use flow chart to show this process. I will try to use chart to describe it. In the screenshot on the first page, we can see that the unknown victim logs into the vulnerability website, which returns an identity authentication cookie.
We've all probably received spam with links, and the sender claimed that there was a funny video. A lot of spam is sent by people with ulterior motives.
But in fact, these links point to the bad guys' own websites. When we click on the link, the next two steps will proceed quickly. First, our browser sends requests to these sites.
Second, those sites respond to some HTML that contains JavaScript. These JavaScript will have a script tag. When the browser detects the script tag, it will send another get request to those vulnerable websites to download the script, carrying the authentication cookie.
In this way, the bad guys pretend to be the victim's browser and use their identity to issue a JSON request containing sensitive data. Next, load JSON as executable JavaScript so that the hackers can get the data. To deepen our understanding, we can look at the actual code of an attack. If the vulnerability website returns a JSON response with sensitive data, send it as follows:
[Authorize]
publicJsonResultAdminBalances(){
varbalances=new[]{
new{Id=1,Balance=3.14},
new{Id=2,Balance=2.72},
new{Id=3,Balance=1.62}
};
returnJson(balances);
}
It should be noted that the above demonstration is not specifically for asp.net or asp.net MVC. I just happened to use asp.net MVC to demonstrate this vulnerability. If this is a method of homecontroller, we send a get request to / home / adminbalances and return the following JSON text:
[{“Id”:1,”Balance”:3.14},{“Id”:2,”Balance”:2.72},{“Id”:3,”Balance”:1.62}]
Note that I defined this method using the authorize attribute to verify the identity of the requester. So an anonymous get request will not get sensitive data. Important: This is a JSON array. The text containing the JSON array is a valid JavaScript script and can be executed. A script that contains only JSON objects is not a valid JavaScript executable. For example, if we have a JavaScript document that contains the following JSON Code:
{“Id”:1,”Balance”:3.14}
And there is a script label that points to this document:
<script src=”http://example.com/SomeJson”></script>
In this way, we get a JavaScript error in the HTML page. However, if there is an unfortunate coincidence, if we have a script tag that points to a document that contains only a JSON array, then the tag will be mistaken as valid JavaScript and the array will take effect. Let's take a look at the HTML pages on the servers of those with ulterior motives.
Note * here we can see that using JSON object instead of JSON array to return your data can prevent this vulnerability to some extent.
<html>
...
<body>
<scripttype="text/javascript">
Object.prototype.__defineSetter__('Id',function(obj){alert(obj);});
</script>
<scriptsrc="http://example.com/Home/AdminBalances"></script>
</body>
</html>
What do you see? The hacker is changing the prototype of the object, using the special method of definesetter to override the default behavior of JSON object.. In this example, when an appropriately named ID can be set to any object at any time, an anonymous function will be called, which will display the property value using the alert function. Note that the script just sends the data back to the bad guys, not sensitive data. As mentioned before, the bad guy needs to make us visit his malicious webpage shortly after logging into the vulnerable website and while the session is still valid. It is typical that phishing attacks are carried out through emails with malicious website links.
If you still click the link to log in to the original website, the browser will load the script referenced in the script tag and send your authentication cookie to the website. Until we connect to the original website, we will send a valid authentication request for JSON data and receive the valid data in response to our browser. These words may sound familiar, because it is a real variant of forgery cross station request, which I wrote before.
Because definesetter is an invalid method on IE8, no phenomenon can be seen on IE8. I've tried it on both chrome and Firefox, all right. It's also easy to avoid this vulnerability: either never send JSON arrays, or just access HTTP post to get the data you need. For example: in asp.net MVC, you can use acceptverb attribute to implement:
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
publicJsonResultAdminBalances(){
varbalances=new[]{
new{Id=1,Balance=3.14},
new{Id=2,Balance=2.72},
new{Id=3,Balance=1.62}
};
returnJson(balances);
}
One problem with this method is that many JS libraries, such as jQuery, send JSON requests by get instead of post by default. For example, $. Getjson initiates a get request by default. So, when doing this JSON access, we need to be sure that we are using the client library to initiate post requests. Asp.net and WCF JSON servers actually use the "d" attribute in objects to wrap their JSON, which I discussed in another article:
It seems strange that data must be obtained through these attributes, but this needs to be implemented through a client agent to remove the "d" attribute so as not to affect the end user. Under asp.net MVC, the vast majority of developers do not generate client-side agents, but use jQuery and other similar libraries, which makes it awkward to use the "d" attribute.
Note: in fact, the method of MVC is a little complicated. Here we can see that the precondition of JSON hijacking vulnerability is to execute JSON return object on the browser of the victim. In fact, Google uses a more intelligent method to prevent the hacker from running this script by adding the "dead cycle" command. See this article: why does Google's JSON response start with while (1)?
How about checking the HTTP header?
Some people may have questions: "why not check the JSON service with a special header before responding to a get request? Just like x-requested-with: XMLHttpRequest or content type: application / JSON ". I think this might be a transition, because most client libraries send one or two headers, but browsers respond to get requests for script tags differently.
The problem is: at some time in the past, users may issue legitimate JSON get requests. In this case, the vulnerability may be hidden between the normal requests of the user's browser. Also in this case, when the browser issues a get request, the request may be cached in the buffer of the browser and the proxy server. We can try to set the no cache header so that we trust the browser and all proxy servers to cache correctly and trust users not to be accidentally overwritten.
Of course, if we use SSL to provide JSON text, this specific caching problem will be easily solved.
Note: we can see here that it is better not to cache your Ajax requests, but at present, it seems that all JS libraries do not enable cache by default.
What's the real problem?
In an article published by Mozilla Developer Center, object and array initialization settings should not call setters when assigning values. I agree with that. Despite the comments, maybe browsers should not actually execute scripts.
But at the end of the day, assigning responsibility doesn't make your site more secure. These browser quirks will come up from time to time. We as web developers need to solve these problems. Chrome 2.0.172.31 and Firefox 3.0.11 also have this weakness. IE8 doesn't have this problem because it doesn't support this method, and I haven't tried it in IE7 or IE6.
In my opinion, under the current client library, the default way to access JSON safely should be post, and we should choose get instead of other ways. What do you think? How do other platforms you know solve this problem? I'd like to hear from you.
Original text: JSON hijacking translation: Tianyi & Ting proofreading annotation: newghost from: ourjs