SQL注入
使用准备好的语句和参数化查询。
这些是由数据库服务器与任何参数分别发送到和解析的SQL语句。这样,攻击者不可能注入恶意SQL。
基本上有两个选择可以实现:
- 使用PDO(PHP数据对象):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
- 使用mysqli(改进的mysql):
$stmt = $dbconnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);//'s' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// do sth with the rows
}
如果与DB的连接不同于MySQL,则有一个特定于驱动程序的第二个选项,您可以参考(例如,Postgres的PG_EXECUTE和PG_PREPARE())。 PDO是通用的。
准备好的语句可以用于动态查询吗?
虽然您仍然可以将准备的语句用于查询参数,但动态查询本身的结构不能参数化,并且某些查询功能无法参数化。
对于这些特定方案,最好的办法是使用限制可能值的白名单过滤器。
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
目录遍历和代码注入
目录遍历(路径遍历)
是指影响文件系统的攻击。在这种类型的攻击中,经过身份验证或未经验证的用户可以请求,查看或执行他们无法访问的文件。
不安全的代码样本
在下面的示例中,脚本将未验证/未设置的HTTP请求值直接传递给include()php函数。这意味着脚本将尝试包含任何路径/文件名作为参数:
$file = $_GET['file'];
include($file);
例如,如果您通过 /etc /passwd作为参数,则此文件适用于所有用户。因此,脚本将返回文件的内容,其中包含有关所有系统用户的信息:
安全代码样本
根据特定情况,可能会以不同的方式缓解这种漏洞。但是,最常见和通用的方法是使用basename()
和realpath()
函数。
basename()
函数仅返回给定的文件名
path/filename: basename(“../../../etc/passwd”) = passwd.
realpath()
函数返回规范化的绝对路径名,但仅当文件存在以及运行脚本是否在层次结构中的所有目录上具有可执行权限:realpath(“../../../etc/passwd”) = /etc/passwd
。
$file = basename(realpath($_GET['file']));
include($file);
现在,如果我们请求与上述相同的文件,我们会得到一个空的响应。
代码注入/执行在PHP代码注入攻击的情况下,攻击者会利用包含系统函数/呼叫的脚本来读取或执行远程服务器上的恶意代码。这是具有后门外壳的代名词,在某些情况下也可以使特权升级。
不安全的代码样本
在此示例中,脚本使用exec()函数执行ping命令。但是,主机是动态的(通过HTTP GET请求传递):
exec("ping -c 4 " . $_GET['host'], $output);
echo "<pre>";
print_r($output);
echo "</pre>";
通过https://example.com/file.php?host=www.google.com//返回ping google.com命令的输出。
此片段具有代码注入漏洞。它允许攻击者使用分号传递多个命令到该函数。在Linux中,此定界符用于执行多个命令内联。 https://example.com/file.php?host=www.google.com;whoami//
安全代码样本
您可以在PHP应用程序中使用两种功能,可以帮助硬命令行调用,例如exec()
,Shell_exec()
,passthru()
和system()
:escapeshellcmd() and escapeshellarg()
。 escapeshellcmd()
function逃脱了字符串中可能用于执行任意命令的字符。通过在他们面前包括后斜线来逃脱以下字符:|*?~
<>^()[] {} $ \,\ x0a和\ xff. Single and double quotes are escaped only if they are not paired. For example,
escapeshellcmd(ping -c 4 www.google.com;ls-lahâ)= ping -c c 4 www.google.com\;ls -lah.`
escapeshellarg()
函数在字符串周围添加单引号,并逃脱任何现有的单个引号。结果,整个字符串作为单个参数传递给shell命令。
注意:escapeshellcmd()
和escapeshellarg()
功能可能对不同的操作系统行为不可预测,尤其是在Windows上。
`
//#1限制多个命令//
exec(Escapeshellcmd(“ Ping -c 4”。$ _GET ['host']),$ output)
//#2限制多个命令和多个参数//
exec(EscaPeshellCMD(“ Ping -c 4”。Escapeshellarg($ _ get ['host'])),$ output);
`冷17
注意:版本第一保证用户不能运行多个命令。但是,用户仍然可以使用同一命令的多个参数。在第二版中,用户无法传递多个命令或参数。
为了避免安全问题,我们建议您在PHP配置中禁用exec(), shell_exec(), passthru(), and system()
功能,除非绝对有必要使用它们。您还可以创建接受命令/参数的白名单。