所以我是一个有点经验丰富的php开发人员,自2007年以来一直在'做该死的事';但是,在保护我的应用程序方面,我仍然相对较差。我不知道我知道的所有事情,我可以而且应该这样做。
我已经选择了Securing PHP Web Applications并正在阅读我的方式来测试一路上的事情。我对与数据库查询有关的一般SO组有一些问题(主要是在mysql下):
创建将数据放入数据库的应用程序时,mysql_real_escape_string和输入数据上的常规检查(is_numeric等)是否足够?那些与sql注入不同的其他类型的攻击呢。
有人可以解释存储过程和准备好的语句,而不是 - 你制作它们并调用它们。我想知道它们是如何工作的,幕后的验证是什么。
我在php4绑定环境中工作,php5暂时不是一个选项。之前有没有其他人一直在这个位置,你做了什么来确保你的应用程序,而所有酷孩子都在使用这个甜蜜的新mysqli界面?
人们发现有哪些一般的良好做法是有利的,强调创建一个能够承受升级和可能的迁移的基础设施(比如将php4移到php5)。
注意:有一个搜索周围找不到类似于这个打到php-mysql安全性的东西。
答案 0 :(得分:9)
Javier的答案有owasp链接是一个好的开始。
还有一些事情可以做得更多:
关于SQL注入攻击,您可以编写一个函数来删除输入中的常见SQL语句,如“DROP”或“DELETE * WHERE”,如下所示:
* $ sqlarray = array(“DROP”,“或1 = 1”,“union select”,“SELECT * FROM”,“select host”,“create table”,“FROM users”,“users WHERE” ); *
然后编写将根据此数组检查输入的函数。确保$ sqlarray中的任何内容都不是来自用户的常见输入。 (别忘了在这上面使用strtolower,谢谢你)。
我不确定memcache是否适用于PHP 4,但你可以通过允许某个远程IP访问process.php页面X在Y时间段内的数量来实现对memcache的一些垃圾邮件防护
特权很重要。如果您只需要插入权限(例如,订单处理),那么您应该使用只有插入且可能具有选择权限的用户登录订单处理页面上的数据库。这意味着即使SQL注入完成,它们也只能执行INSERT / SELECT查询而不能删除或重构。
将重要的php处理文件放在/ include等目录中。然后禁止所有IP访问该/ include目录。
在用户的会话中使用用户的代理+ remoteip + salt加盐腌MD5,并在每个页面加载时验证正确的MD5在其cookie中。
禁止使用某些标题(http://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST)。禁止PUT(如果您不需要文件上传)/ TRACE / CONNECT / DELETE标题。
答案 1 :(得分:6)
我的建议:
然后您可以执行以下操作:
$pdo_obj = new PDO( 'mysql:server=localhost; dbname=mydatabase',
$dbusername, $dbpassword );
$sql = 'SELECT column FROM table WHERE condition=:condition';
$params = array( ':condition' => 1 );
$statement = $pdo_obj->prepare( $sql,
array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );
$statement->execute( $params );
$result = $statement->fetchAll( PDO::FETCH_ASSOC );
<强>优点:强>
<强>缺点:强>
答案 2 :(得分:3)
我通常不使用PHP,因此我无法提供专门针对您的要求的建议,但我建议您查看OWASP页面,特别是前10个漏洞报告:http://www.owasp.org/index.php/Top_10_2007 < / p>
在该页面中,针对每个漏洞,您将获得可以避免不同平台(.Net,Java,PHP等)中的问题的列表。
关于准备好的语句,它们的工作原理是让数据库引擎知道在特定查询期间有多少参数和类型,使用这些信息引擎可以理解哪些字符是实际参数的一部分,而不是应该被解析为SQL,就像'(撇号)作为数据的一部分而不是'作为字符串分隔符。抱歉,我无法提供针对PHP的更多信息,但希望这会有所帮助。
答案 3 :(得分:2)
AFAIK,PHP / mySQL通常没有参数化查询。
将sprintf()
与mysql_real_escape_string()
一起使用应该会很好用。如果您为sprintf()
使用适当的格式字符串(例如,对于整数使用“%d”),那么您应该非常安全。
答案 4 :(得分:1)
我可能错了,但是对用户提供的数据使用mysql_real_escape_string
是否足够?
除非它们是数字,否则您应该确保它们实际上是数字,而不是使用例如ctype_digit
或is_numeric
或sprintf
(使用%d
或%u
强制输入数字。)
另外,为你的php脚本提供一个只能选择SELECT,INSERT,UPDATE和DELETE的serarate mysql用户可能是一个好主意......
来自php.net的示例
示例#3“最佳实践”查询
在每个变量周围使用mysql_real_escape_string()可以防止SQL注入。此示例演示了查询数据库的“最佳实践”方法,与Magic Quotes设置无关。
现在可以正确执行查询,并且SQL注入攻击无效。
<?php if (isset($_POST['product_name']) && isset($_POST['product_description']) && isset($_POST['user_id'])) { // Connect $link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password'); if(!is_resource($link)) { echo "Failed to connect to the server\n"; // ... log the error properly } else { // Reverse magic_quotes_gpc/magic_quotes_sybase effects on those vars if ON. if(get_magic_quotes_gpc()) { $product_name = stripslashes($_POST['product_name']); $product_description = stripslashes($_POST['product_description']); } else { $product_name = $_POST['product_name']; $product_description = $_POST['product_description']; } // Make a safe query $query = sprintf("INSERT INTO products (`name`, `description`, `user_id`) VALUES ('%s', '%s', %d)", mysql_real_escape_string($product_name, $link), mysql_real_escape_string($product_description, $link), $_POST['user_id']); mysql_query($query, $link); if (mysql_affected_rows($link) > 0) { echo "Product inserted\n"; } } } else { echo "Fill the form properly\n"; }
答案 5 :(得分:0)
对任何涉及写入数据库的活动使用存储过程,并对所有选择使用绑定参数。