1.使用一个SQL注射备忘单
一个基本的原则就是,永远不要相信用户提交的数据。
另一个规则就是,在你发送或者存储数据时对它进行转义(escape)。
可以总结为:filter input, escape output (FIEO). 输入过滤,输出转义。
通常导致SQL注射漏洞的原因是没有对输入进行过滤,如下语句:
复制代码 代码如下:
<?php
$query = "SELECT *
FROM users
WHERE name = '{$_GET['name']}'";
在这个例子中,$_GET['name']来自用户提交的数据,既没有进行转义,也没有进行过滤~~
对于转义输出,你要记住用于你程序外部的数据需要被转义,否则,它可能被错误地解析。
相反,过滤输入能确保数据在使用前是正确的.
对于过滤输入,你要记住,在你程序外部的原始数据需要被过滤,因为它们是不可信任的。
如下例子演示了输入过滤和输出转义:
复制代码 代码如下:
<?php
// Initialize arrays for filtered and escaped data, respectively.
$clean = array();
$sql = array();
// Filter the name. (For simplicity, we require alphabetic names.)
if (ctype_alpha($_GET['name'])) {
$clean['name'] = $_GET['name'];
} else {
// The name is invalid. Do something here.
}
// Escape the name.
$sql['name'] = mysql_real_escape_string($clean['name']);
// Construct the query.
$query = "SELECT *
FROM users
WHERE name = '{$sql['name']}'";
?>
另一个有效防止SQL注射的方法是使用prepare 语句,如:
复制代码 代码如下:
<?php
// Provide the query format.
$query = $db->prepare('SELECT *
FROM users
WHERE name = :name');
// Provide the query data and execute the query.
$query->execute(array('name' => $clean['name']));
?>
2.了解比较运算符之间的不同
例如,你使用strpos() 来检测在一个字符串中是否存在一个子串 (如果子串没有找到,函数将返回 FALSE ), 结果可能会导致错误:
复制代码 代码如下:
<?php
$authors = 'Chris & Sean';
if (strpos($authors, 'Chris')) {
echo 'Chris is an author.';
} else {
echo 'Chris is not an author.';
}
?>
上例中,由于子串处于最开始的位置,因此strpos() 函数正确地返回了0,表明子串处于字符串中最开始的位置。然后,因为条件语句会把结果当成Boolean(布尔)类型的,因此 0 就被PHP给计算成了 FALSE,最终导致条件语句判断失败。
当然,这个BUG可以用严格的比较语句来修正:
复制代码 代码如下:
<?php
if (strpos($authors, 'Chris') !== FALSE) {
echo 'Chris is an author.';
} else {
echo 'Chris is not an author.';
}
?>
3.减少else(Shortcut the else)
记住,在你使用变量前总是要先初始化它们。
考虑如下一个用来根据用户名来检测用户是否是管理员的条件语句:
复制代码 代码如下:
<?php
if (auth($username) == 'admin') {
$admin = TRUE;
} else {
$admin = FALSE;
}
?>
这个看起来似乎足够安全,因为看一眼就很容易理解。想象一下有一个更复杂一点的例子,它为name和email同时设置变量,为方便起见:
复制代码 代码如下:
<?php
if (auth($username) == 'admin') {
$name = 'Administrator';
$email = 'admin@example.org';
$admin = TRUE;
} else {
/* Get the name and email from the database. */
$query = $db->prepare('SELECT name, email
FROM users
WHERE username = :username');
$query->execute(array('username' => $clean['username']));
$result = $query->fetch(PDO::FETCH_ASSOC);
$name = $result['name'];
$email = $result['email'];
$admin = FALSE;
}
?>
因为 $admin 还是明确地被设置为TRUE or FALSE,似乎一切都完好。但是,如果另一个开发者后来在代码里加了一个elseif语句,很可能他会忘记这回事:
复制代码 代码如下:
<?php
if (auth($username) == 'admin') {
$name = 'Administrator';
$email = 'admin@example.org';
$admin = TRUE;
} elseif (auth($username) == 'mod') {
$name = 'Moderator';
$email = 'mod@example.org';
$moderator = TRUE;
} else {
/* Get the name and email. */
$query = $db->prepare('SELECT name, email
FROM users
WHERE username = :username');
$query->execute(array('username' => $clean['username']));
$result = $query->fetch(PDO::FETCH_ASSOC);
$name = $result['name'];
$email = $result['email'];
$admin = FALSE;
$moderator = FALSE;
}
?>
如果一个用户提供一个能够触发elseif条件的用户名(username), $admin 没有被初始化,这可能会导致不必要的行为,或者更糟糕的情况,一个安全漏洞。另外,一个类似的情况对于 $moderator 变量来说同样存在,它在第一个条件中没有被初始化。
通过初始化$admin 和 $moderator ,这是完全很容易避免这一情况的发生的:
复制代码 代码如下: