referer:http://balpha.de/2013/02/plain-text-considered-harmful-a-cross-domain-exploit/

Data from around the world

The same origin policy prevents a website's JavaScript from seeing the result of a request made to a different domain. This is essential because that request would send along any cookies stored for that domain. If you happen to be authenticated on the other site, and visit a malicious site, then the evil page could request, say, your account balance summary from the other site.

Note that the same origin policy doesn't necessarily prevent the request per se – it just prevents the response from being accessible. A malicious site can e.g. just redirect your browser, or submit a form, or include an image or an iframe – in all those cases a request is made to your site; the evil site just doesn't see the response. It doesn't have access to the iframe's DOM, painting a cross-domain image to a canvas will taint it, and so on. In some cases, it's possible for a site to say that it's okay to see the data on a different domain, but this isn't the default.

Let's look at <script> elements now. If such an element's src attribute is set, then the browser will load the script and execute it in the current page. It will do this regardless of origin (otherwise, you e.g. couldn't serve jQuery from Google's CDN). It's not possible for a site to read the content of a script that was loaded from a different domain, but any side effects of executing the script are very visible – like the jQuery object suddenly existing in your page.

This cross-domain script loading is what JSONP utilizes. If you have, say, a public API where you're entirely okay with the data being accessible cross-domain, you can enable other sites' JavaScript to use your data by returning JSONP. This essentially means you're returning JavaScript (which, as explained above, will be executed despite the different origin). The consuming site lets you know that it has prepared a function for your script to call, and what that function's name is. You return a JavaScript file that calls this very function with your data.

function theCallback(data) {
alert("The temperature is " + data.temperature_celsius + " °C");
}
var script = document.createElement("script");
script.src = "http://some-weather-service.com/api?city=Boston&callback=theCallback";
document.head.appendChild(script);

The callback parameter tells the weather service what function to call in its JSONP response, and thus the API's response looks like this:

theCallback({"temperature_celsius":20,"temperature_fahrenheit":68})

which makes your site work as intended.

As an aside: You should only use JSONP APIs from sites that you trust, since you're allowing those sites to send you arbitrary JavaScript that you will happily execute on your own domain.

My precious

So we've established that it's possible to execute JavaScript that comes from a different domain; a site that wants to allow cross-origin access can send valid JavaScript that any site can thus embed. Now let's look at the case where the site doesn't want to allow this; it only wants the data to be accessible to requests coming from its own site.

As far as simple reading of the request's response goes, same-origin takes care of this. But you obviously also want to prevent the above “execute as JavaScript” trick from working. The lowest-hanging fruit is just making sure that your response doesn't look like a function call. However, there is such a thing as a function call in disguise – Phil Haack wrote a blog post and a more detailed follow-up on this topic, which will tell you why you shouldn't return JSON responses whose outer wrapper is an array.

But there's an even more subtle vulnerability which appears when you don't return JSON at all. Here's a real-life example I found (it was fixed in the meantime). Assume your site uses a CSRF token, and you want to make sure that a browser tab always has the up-to-date token, even when it changes. This could be because you cycle the token at regular intervals or just because in a different browser tab your user logged out and logged in again. That's why you have a route that returns the current CSRF token for the active session, so the browser can regularly poll for it.

Same-origin policy prevents other sites from seeing this token in any way, since you neither wrap it in a function call, nor in an array – in fact, you wrap it in nothing at all; you just return the token as a plain text. Your token is a 24-digit hexadecimal number, like b2039487e51a6bfdc7299de0, and these 24 characters are all that your route returns. Nothing in there that can cause a function to be executed, so even if a malicious site included

<script src="http://your-awesome-site.com/current-csrf-token"></script>

in its HTML, it wouldn't have access to the token.

Right? Nope.

Not as hidden as you'd think

Let's see what happens when a browser executes a “script” that contains nothing more than b2039487e51a6bfdc7299de0. If you try it out, you'll see that not a lot happens, except that the JavaScript console will show an error:

ReferenceError: b2039487e51a6bfdc7299de0 is not defined

The first thing to note is that this is not a syntax error. Because b2039487e51a6bfdc7299de0 is a valid JavaScript identifier, this 24-character long “script” is syntactically valid JavaScript, and the browser happily runs it. So what happens when the JavaScript engine encounters an identifier? It will look for a variable in the current scope by this name. Since the script is executed in a global context, the current scope is the window object. The engine checks whether window["b2039487e51a6bfdc7299de0"] exists, which it obviously doesn't, and thus throws an exception.

This looks like something that the malicious site cannot gain any information from, but unfortunately in Firefox, it can. Firefox supports Proxy objects by default. Chrome also supports them, but only if you enable experimental JavaScript (and with a slightly different syntax than Firefox – point being: this exploit can be made to work in Chrome as well, but currently only for users who have the experimental JS features turned on).

Proxy objects are essentially property accessors on speed. If you're used to Python, think “overriding __getattr__.” If you're used to C#, think “TryGetMember on DynamicObject.” Together with prototype magic, they enable us hook into the “looking for a variable in the current scope” process. If the user visits the following page in Firefox, the JavaScript can identify the token:

<html>
<head>
<script type="text/javascript">
window.onload = function () {
window.__proto__ = new Proxy(window.__proto__, {
has: function (target, name) {
if (/^[a-f0-9]{24}$/.test(name))
alert("Your CSRF token on your-awesome-site.com is " + name);
return name in target;
}
});
var s = document.createElement("script");
s.src = "http://your-awesome-site.com/current-csrf-token";
document.head.appendChild(s);
}
</script>
</head>
<body></body>
</html>

– and an alert is just the least evil thing the site can do with the token.

Observant readers may have noticed that the above only works if the token starts with a letter, since only then is it a valid identifier. But for a random hexadecimal digit that's six out of sixteen, thus a 37.5% chance. That's not too shabby. And of course it depends on what kind of data your plain-text API returns.

If the return values are predictable, and the evil site only cares about a yes-or-no question, it doesn't even need proxy objects. For example, let's say a similar route returns the user name of the currently logged-in user, and all the malicious site wants to know is whether a visitor is logged in as “balpha” on your-awesome-site.com. For that, they would just have to create a property called "balpha" with a get accessor on the global object. These are supported by all major browsers.

What's to take away from this? If you respond with plain text data to a GET request, don't be too sure that the same origin policy will save you. Either wrap your data in a JSON object (not an array!), or only respond to POSTrequests with such data (unless you're a REST zealot enthusiast, in which case POST won't always be an option), or both. Other possible mitigations include the while(1) trick, or at the very least making sure that your secret payload is not syntactically valid JavaScript.

Plain text considered harmful: A cross-domain exploit的更多相关文章

  1. 关于ajax跨域请求(cross Domain)

    Cross Domain AJAX主要就是A.com网站的页面发出一个XMLHttpRequest,这个Request的url是B.com,这样的请求是被禁止的,浏览器处于安全考虑不允许进行跨域访问, ...

  2. 如何撤销 PhpStorm/Clion 等 JetBrains 产品的 “Mark as Plain Text” 操作 ?

    当把某个文件“Mark as Plain Text”时,该文件被当做普通文本,就不会有“代码自动完成提示”功能,如下图所示: 但是呢,右键菜单中貌似没有 相应的撤销 操作, 即使是把它删除,再新建一个 ...

  3. Jade之Plain Text

    Plain Text jade提供了3种得到纯文本的方法. Piped Text 添加纯文本的一个最简单的方法就是在文本最前面加|符号即可. jade: p | It must always be o ...

  4. Insert Plain Text and Images into RichTextBox at Runtime

    Insert Plain Text and Images into RichTextBox at Runtime' https://www.codeproject.com/Articles/4544/ ...

  5. [cross domain] four approachs to cross domain in javascript

    four approachs can cross domain in javascript 1.jsonp 2.document.domain(only in frame and they have ...

  6. [Regular Expressions] Find Plain Text Patterns

    The simplest use of Regular Expressions is to find a plain text pattern. In this lesson we'll look a ...

  7. How to return plain text from AWS Lambda & API Gateway

    With limited experience in AWS Lambda & API Gateway, it's struggling to find the correct way to ...

  8. 前端开发各种cross之cross domain

    作为一个苦逼前端开发工程师,不得不面对各种cross,比如面对五花八门的浏览器我们必须cross browser,面对各种终端,我们必须cross device,在这么多年的前端开发经历中,在不同的域 ...

  9. text/plain && text/html

    text/plain和text/html都是Content-Type; text/plain : 页面以文本形式输出 text/html:  页面以html格式输出

随机推荐

  1. 把已安装的wampserver移动到不同目录使用应注意的问题

    很多时候需要把已安装的wampserver移动到不同目录使用,此时应注意几个问题: 1.修改D:\wamp64\bin\apache\apache2.4.9\conf目录下的httpd.conf文件( ...

  2. 小组E-R图的改进 2.0版本

    上图是我对我们小组的数据库E-R图的改进 我在出勤上加了一个学生留言,因为我们组在需求分析中说明了有个教师与学生通过此软件交流的功能,教师可以通过这个功能给学生留言,学生也可以通过此功能请假或者说明旷 ...

  3. linux-RabbitMQ安装命令

    一.RabbitMQ 1.安装配置epel源    $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.no ...

  4. Python之Django总结

    一.Django 总结django知识点   一.视图函数:     请求对象-----------request:           1.HttpRequest.body:         请求原 ...

  5. 调试OpenStack时遇到的主要问题(by quqi99)

    作者:张华  发表于:2014-11-09版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 ( http://blog.csdn.net/quqi99 ) 今天想 ...

  6. css3表格样式

    <caption>  关于表格存储内容的描述或总结. 1.border-spacing:0;border-spacing 属性设置相邻单元格的边框间的距离(仅用于“边框分离”模式). 2. ...

  7. dom 兼容性问题1_节点部分

    AS : ECMAScript xml . html js组成: 1,ECMAScript : 是Javascript的核心标准.同时也是一个解释器. 2,DOM: document object m ...

  8. 使用log4j的邮件功能

    Log4j的邮件功能能够为我们做这样的事情----当程序运行完的时候,或者正在运行也是可以的,它将程序的日志通过邮件的方式发到你的邮箱上. 这样,对于程序运行的控制就不用每次都跑到机器上去看日志文件这 ...

  9. 清新大气的ListView下拉上拉刷新--第三方开源--PullDownListView

    下载地址:https://github.com/guojunyi/PullDownListView 使用: xml: <com.pulldownlistview.PullDownListView ...

  10. nyoj-130-相同的雪花(hash)

    题目链接 /* Name:NYOJ-130-相同的雪花 Copyright: Author: Date: 2018/4/14 15:13:39 Description: 将雪花各个分支上的值加起来,h ...