8

所以我是一个经验丰富的 php 开发人员,自 2007 年以来一直在“做这该死的事”;但是,在保护我的应用程序方面,我仍然相对不成熟。在某种程度上,我并不真正了解我知道的一切,我可以而且应该。

我已经拿起了Securing PHP Web Applications并且正在阅读我的方式来测试它。我有一些与数据库查询相关的一般 SO 组的问题(主要在 mysql 下):

创建将数据放入数据库的应用程序时,mysql_real_escape_string 和对输入数据的常规检查(is_numeric 等)是否足够?与sql注入不同的其他类型的攻击呢?

有人可以用比你制作它们并调用它们更多的信息来解释存储过程和准备好的语句。我想知道它们是如何工作的,在幕后进行了哪些验证。

我在 php4 绑定环境中工作,而 php5 暂时不是一个选项。以前有没有其他人担任过这个职位,当所有酷孩子都在使用那个甜蜜的新 mysqli 界面时,你做了什么来保护你的应用程序?

人们发现有哪些普遍的良好实践是有利的,强调创建能够承受升级和可能迁移的基础架构(例如将 php4 迁移到 php5)。

注意:已经四处搜索找不到任何与 php-mysql 安全性类似的东西。

4

6 回答 6

9

具有 owasp 链接的 Javier 的答案是一个好的开始。

您还可以做更多的事情:

  1. 关于 SQL 注入攻击,您可以编写一个函数,从输入中删除常见的 SQL 语句,如“DROP”或“DELETE * WHERE”,如下所示:

    *$sqlarray = array(" DROP ","or 1=1","union select","SELECT * FROM","select host","create table","FROM users","users WHERE");*

    然后编写将根据该数组检查您的输入的函数。确保 $sqlarray 中的任何内容都不是您的用户的常见输入。(不要忘记在此使用 strtolower,谢谢 lou)。

  2. 我不确定 memcache 是否适用于 PHP 4,但您可以通过在 Y 时间段内仅允许某个远程 IP 访问 process.php 页面 X 次来使用 memcache 设置一些垃圾邮件保护。

  3. 特权很重要。如果您只需要插入权限(例如,订单处理),那么您应该使用只有插入权限和可能选择权限的用户登录到订单处理页面上的数据库。这意味着即使 SQL 注入通过,他们也只能执行 INSERT / SELECT 查询,而不能删除或重组。

  4. 把重要的php处理文件放在/include等目录下。然后禁止所有 IP 访问该 /include 目录。

  5. 在用户会话中使用用户的代理 + remoteip + 你的 salt 放置一个加盐的 MD5,并在每次页面加载时验证其 cookie 中是否存在正确的 MD5。

  6. 禁止某些标头 ( http://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST )。禁止 PUT(如果您不需要文件上传)/TRACE/CONNECT/DELETE 标头。

于 2009-05-05T23:56:53.260 回答
7

我的建议:

  1. 放弃 mysqli 以支持PDO(使用 mysql 驱动程序)
  2. 使用 PDO 参数化的预处理语句

然后,您可以执行以下操作:

$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 );

优点:

  1. 不再需要手动转义,因为 PDO 为您完成了这一切!
  2. 突然切换数据库后端相对容易。

缺点:

  • 我想不出任何。
于 2009-05-06T11:31:39.907 回答
3

我通常不使用 PHP,因此无法针对您的要求提供专门的建议,但我建议您查看 OWASP 页面,尤其是前 10 个漏洞报告:http ://www.owasp.org /index.php/Top_10_2007

在该页面中,对于每个漏洞,您都会获得一个列表,其中列出了您可以采取哪些措施来避免不同平台(.Net、Java、PHP 等)中的问题。

关于准备好的语句,它们的工作原理是让数据库引擎知道在特定查询期间需要多少参数和期望的类型,使用这些信息,引擎可以理解哪些字符是实际参数的一部分,而不是应该被解析为SQL 像一个 '(撇号)作为数据的一部分,而不是一个 ' 作为字符串分隔符。抱歉,我无法提供更多针对 PHP 的信息,但希望这会有所帮助。

于 2009-05-05T21:42:08.297 回答
2

AFAIK,PHP/mySQL 通常没有参数化查询。

使用sprintf()withmysql_real_escape_string()应该可以很好地工作。如果您使用适当的格式字符串sprintf()(例如整数的“%d”),您应该是非常安全的。

于 2009-05-06T12:53:27.310 回答
1

mysql_real_escape_string我可能错了,但在用户提供的数据上使用还不够吗?

除非它们是数字,在这种情况下,您应该确保它们实际上是数字,而不是使用例如ctype_digitis_numericsprintf(使用%d%u强制输入数字)。

此外,为您的 php 脚本设置一个只能选择、插入、更新和删除的 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";
    }
于 2009-05-06T08:54:57.710 回答
0

对涉及写入数据库的任何活动使用存储过程,并对所有选择使用绑定参数。

于 2009-05-06T08:47:28.040 回答