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. SpringBoot2.0之整合Kafka

    maven依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www. ...

  2. DESeq2 install --- 如何安装R包("RcppArmadillo")?

    安装R包("RcppArmadillo")失败,导致依赖该包的DESeq2 无法使用: 首先对gcc,g++升级至4.7, 但依然报错,还是安装不了RcppArmadillo: 报 ...

  3. Kafka详解三:开发Kafka应用

    问题导读 1.Kafka系统由什么组成?2.Kafka中和producer相关的API是什么? 一.整体看一下Kafka        我们知道,Kafka系统有三大组件:Producer.Consu ...

  4. BASE64Encoded() 方法报错说方法未定义

    代码: String enParams = new BASE64Encoder().encode(strParams.getBytes()); 出错,显示方法未定义 解决方法:项目右键——>pr ...

  5. VC 模拟CMD 匿名管道

    #include "stdafx.h" #include <Windows.h> #include <stdio.h> #include <stdli ...

  6. SBT搭建Spark

    http://www.cnblogs.com/yongjian/p/6211007.html http://www.aboutyun.com/thread-8587-1-1.html http://b ...

  7. Spring初学之泛型依赖注入

    主要讲泛型依赖注入,所以核心在java文件,配置文件中只需配置扫描包即可,如下: <?xml version="1.0" encoding="UTF-8" ...

  8. poj 2115 C Looooops 扩展欧几里德

    C Looooops Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 23616   Accepted: 6517 Descr ...

  9. AOP理解,待细看

    http://jinnianshilongnian.iteye.com/blog/1474325

  10. Java日期时间输出格式优化

    使用printf格式化日期 printf 方法可以很轻松地格式化时间和日期.使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾. 转  换  符 说    明 示    例 c 包括全部 ...