php中防sql注入,如何在PHP应用中防止SQL注入

SQL注入是一个控制数据库查询的技术,往往会导致丧失机密性。在某些情况下,如果成功执行SELECT 'phpeval(base64_decode("someBase64EncodedDataHere"));' INTO OUTFILE '/var/www/reverse_shell.php'

将导致服务器被攻击者拿下,而代码注入(包括SQL,LDAP,操作系统命令,XPath注入技术)长年保持在OWASP漏洞排名前十。

更多人分享有关于应用安全的知识是一件极好的事。然而不幸的是,网络上流传的大部分东西(尤其是老博客文章,高搜索引擎排名)都已经过时了。虽然是无意的误导,但是却造成了很大的威胁。

如何防止SQL注入

使用预处理语句,也被称为参数化查询。例如:$stmt = $pdo->prepare('SELECT * FROM blog_posts WHERE YEAR(created) = ? AND MONTH(created) = ?');if (!$stmt->execute([$_GET['year'], $_GET['month']])) {

header("Location: /blog/"); exit;

}

$posts = $stmt->fetchAll(\PDO::FETCH_ASSOC);

在PHP应用中预处理语句过滤掉任何SQL注入的可能,无论是什么都需要先传递到$_GET变量。SQL查询语句是攻击者无法改变的(除非你将PDO::ATTR_EMULATE_PREPARES 开启了,这也意味着你还没有真正使用预处理语句)

预处理语句解决应用安全的根本问题:通过发送完全独立的包将操作指令与数据进行单独处理。这和导致堆栈溢出的问题有点类似了。

只要你没有用SQL语句连接user-provided变量和环境变量(并且保证你没有使用emulated prepares),那你就不必担心交叉SQL注入的问题了。

重要说明

预处理语句确保WEB应用与数据库服务之间的交互(即使两者不在同一台机器上,也会通过TLS进行连接)。攻击者还有可能在字段中存储一个payload,这是相当危险的,比如一个存储过程,我们称之为高阶SQL注入。

在这种情况下,我们建议不要编写存储过程,它会制造一个高阶SQL注入点。

关于输出过滤

3c58f45401d9072c6aa8c27e52c2917f.png

应该有人看到过上面这张关于SQL注入攻击的漫画吧,在一些安全会议上甚至都经常被拿来引用,尤其是写给新人的文章中。这张漫画提醒了我们要提高对数据库查询中危险用户输入的意识,但是漫画中的建议却是过滤掉数据库输入,通过对相关问题的理解,我们知道这仅仅是一个折衷的办法。

最好忘记过滤输入

虽然可以在数据发送到数据库之前重写传入的数据流来防止攻击者的攻击,但是这个过程比较难以把控。

除非你想花时间去研究,达到完全掌握所有Unicode格式应用程序,你最好不要尝试过滤你的输入。

此外,改变你的输入数据流可能造成数据损坏。特别是在你正在处理原始二进制文件(图片,加密信息)的时候。

预处理语句能简单粗暴的防止SQL注入

XKCD作者Randall Munroe是个聪明人。如果这幅漫画直到今天才被创作出来,大概会是这个样子的

4ae53b52fee25a13b6bee1c47670a8e1.png

输入仍应进行验证

数据验证与过滤完全不是一回事,预处理语句可以防止SQL注入,然而并不能让你摆脱那些糟糕的数据。大多数情况下,会使用filter_var()$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);

if (empty($email)) {

throw new \InvalidArgumentException('Invalid email address');

}

列和表标识符

当列和表标识符作为查询语句中的一部分,你不能使用参数表示它们。如果你正在开发的这个应用需要一个动态查询结构,请使用白名单。

白名单是一个应用程序逻辑策略,它只允许少数可信的值。相对来说黑名单,仅仅是禁止已知的恶意输入。

大多数情况下,使用白名单比黑名单更安全!$qs = 'SELECT * FROM photos WHERE album = ?';

// Use switch-case for an explicit whitelist

switch ($_POST['orderby']) {

case 'name':

case 'exifdate':

case 'uploaded':

// These strings are trusted and expected

$qs .= ' ORDER BY '.$_POST['orderby'];

if (!empty($_POST['asc'])) {

$qs .= ' ASC';

} else {

$qs .= ' DESC';

}

default:

// Some other value was passed. Let's just order by photo ID in descending order.

$qs .= ' ORDER BY photoid DESC';

}$stmt = $db->prepare($qs)

;if ($stmt->execute([$_POST['album_id']])) {

$photos = $stmt->fetchAll(\PDO::FETCH_ASSOC);

}

使用预处理语句看起来很麻烦?

开发者第一次遇到预处理语句,对于需要写大量的冗余代码而感到沮丧(提取,执行,取回;提取,执行,取回;….令人厌烦)

由此,EasyDB[https://github.com/paragonie/easydb]诞生了。

如何使用EasyDB

这里有两种方法。你可以使用EasyDB包含你的PDO

如果你熟悉PDO构造,你可以使用\ParagonIE\EasyDB\Factory::create()参数来进行替代// First method:

$pdo = new \PDO('mysql;host=localhost;dbname=something', 'username', 'putastrongpasswordhere');

$db = \ParagonIE\EasyDB\EasyDB($pdo, 'mysql');

// Second method:

$db = \ParagonIE\EasyDB\Factory::create('mysql;host=localhost;dbname=something', 'username', 'putastrongpasswordhere');

如果有一个EasyDB对象,你可以开始利用它的快速开发安全数据库应用程序。

预处理语句:安全数据库查询$data = $db->safeQuery(

'SELECT * FROM transactions WHERE type = ? AND amount >= ? AND date >= ?',

[

$_POST['ttype'],

$_POST['minimum'],

$_POST['since']

]

);

从一个数据库表中选择多行$rows = $db->run(

'SELECT * FROM comments WHERE blogpostid = ? ORDER BY created ASC',

$_GET['blogpostid']

);

foreach ($rows as $row) {

$template_engine->render('comment', $row);

}

从数据库表中选择一行$userData = $db->row(

"SELECT * FROM users WHERE userid = ?",

$_GET['userid']

);

向数据库表中插入新的一行$db->insert('comments', [

'blogpostid' => $_POST['blogpost'],

'userid' => $_SESSION['user'],

'comment' => $_POST['body'],

'parent' => isset($_POST['replyTo']) ? $_POST['replyTo'] : null

]);

动态查询中躲避标识符(列/表/视图名)$qs = 'SELECT * FROM some_table';

$and = false;

if (!empty($where)) {

$qs .= ' WHERE ';

foreach (\array_keys($where) as $column) {

if (!\in_array($column, $whiteListOfColumnNames)) {

continue;

}

if ($and) {

$qs .= ' AND ';

}

$qs .= $db->escapeIdentifier($column).' = ?';

$and = true;

}

}$qs .= ' ORDER BY rowid DESC';

// And then to fetch some data

$data = $db->run($qs, \array_values($where);

警告:escapeIdentifier() 方法意味着输入不应该被转义。

安全从开发人员开始!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PHP是一种常用的服务器端脚本语言,用于开发Web应用程序。为了防止SQL注入攻击,我们可以采取以下措施来保护我们的PHP代码。 1. 使用预处理语句:使用预处理语句可以防止SQL注入攻击。可以使用PDO(PHP Data Objects)或者MySQLi(MySQL Improved)扩展来执行预处理语句。通过预处理语句,我们可以将SQL查询的变量参数化,这样就可以防止恶意用户通过注入SQL语句来破坏数据库。 2. 输入验证和过滤:对于任何从用户输入获得的数据,都应该进行验证和过滤。可以使用PHP内置的过滤器函数进行输入验证,例如filter_var()函数。可以使用过滤器检查用户输入是否符合预期的格式,比如邮箱地址、URL等。 3. 使用转义函数:在向数据库插入用户输入的数据之前,应该使用转义函数对输入数据进行转义处理。可以使用PHP的mysqli_real_escape_string()函数或者PDO的quote()方法来转义用户输入的数据。 4. 使用prepared statements:通过使用prepared statements(预编译语句),我们可以将查询和参数分开处理,从而防止注入攻击。可以使用PDO或者MySQLi扩展的预处理语句来实现。 5. 限制数据库用户权限:为数据库用户分配最小必要的权限,避免给用户过多的权限,从而减少数据库被攻击可能性。 总结来说,为了防止SQL注入攻击,在开发PHP代码时应该始终保持警惕,并使用以上措施来保护数据库的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值