关于MySQL安全性的困惑:charset,转义字符等

时间:2012-03-05 13:24:57

标签: php mysql sql security

我知道要求在公共网站上清理输入或表单提交的数据。 但是,有很多关于网络安全的文件,我完全不知道要走哪条路。

a)目前,我的MySQL表格采用MyISAM格式,大多数编码在utf8_bin,其他编号在latin1_swedish_ci。我认为utf8_bin更可取,但我可以安全地转换它们吗?

b)目前,我通过POST / GET / REQUEST获得的所有数据都有以下巨大的“转换器”:

foreach($_POST as $k=>$v){
    if(ini_get('magic_quotes_gpc'))
        $_POST[$k]=stripslashes($_POST[$k]);
    $_POST[$k]=htmlspecialchars(strip_tags($_POST[$k]));
    $_POST[$k]=utf8_decode($_POST[$k]);
}

并且最重要的是,在SQL查询中,我使用了mysql_real_escape_string,这使得源代码大量增加,特别是对于大型表单。

有没有办法优化(我真的需要所有这些转换吗?),尤其是,如何确保外国字符集(如中文等)我的表单等不会完全混乱?我必须在显示之前将数据转换回来吗?

4 个答案:

答案 0 :(得分:3)

使用mysql_real_escape_string()清理要附加到SQL查询的数据,并在将数据附加到HTML之前使用htmlspecialchars()清理数据。

答案 1 :(得分:2)

在对数据库的SQL命令中使用它们之前清理输入是必要的,但它不能阻止所有形式的SQL注入。防止这种情况的最佳策略是使用参数化查询,它允许数据库区分数据的意义和命令的意图,因此即使输入错误并且SQL命令出现在数据中也是如此,数据库知道将它们视为数据。

这是一篇关于how to do this with PHP/PDO的好文章。

在优秀的OWASP SQL Injection writeup中有关于此的更多信息。

答案 2 :(得分:1)

您在问题中概述的是与输入编码和输出编码以及数据库编码相关的多个位置。

让我们从最开始,即PHP应用程序的输入,即示例中的$_POST。您可以通过首先注意在运行应用程序的位置正确配置主机来显着减少案例数量:

<?php

    /* Prevent the application to run if magic quotes are enabled. */
    if (ini_get('magic_quotes_gpc')) {
        throw new Exception('Magic Quotes must be disabled.');
    }

然后,您从$_POST数组中选择数据,然后使用它们执行多个翻译:

  1. strip_tags - removes "HTML tags"
  2. htmlspecialchars - encodes HTML special characters
  3. utf8_decode - converts the character-encoding from UTF-8 to ISO-8859-1
  4. 看起来有点你可能只是盲目地在输入数据上抛出各种函数。

    我无法判断你的申请,所以我不能具体说,如果你真的需要strip_tagshtmlspecialchars

    即使strip_tags用于输入过滤,问题是它是否适用于您的案例?假设有一个textfeld,用户想要输入一些可能包含标签的文本,那会不会有问题?为什么删除它?也许用户想要输入该值是有原因的?因此,实际知道为什么以及何时需要在输入数据上运行strip_tags是很好的。

    htmlspecialchars函数通常用于输出,而不是输入,因此我不清楚为什么在这里使用它。

    这对在一起 - strip_tagshtmlspecialchars通常在输出中处理得更好。通过想象中的“我最喜欢的HTML”应用程序的数据示例:

    Request:
    
        $_POST['text']: 'The tag I love most in HTML is <a>!';
    
    
    Input Validation:
    
        // This requires POST
        if ($_SERVER['REQUEST_METHOD'] !== POST) {
            --> Method not allowed.
        }
    
        // Specific values are required
        if (!isset($_POST['text'])) {
            --> Invalid Request
        }
    
        // Some requests are just too large:
        if (strlen($_POST['text']) > 5000) {
            --> the request is invalid. block it.
        }
    
        // The expected input encoding is UTF-8
        // This example is rather broad, you might want to limit
        // it to a subset of unicode characters instead.
        if (!preg_match('/^.*$/su', $_POST['text'])) {
            --> Invalid Request.
        }
    
        // The text field should not be larger than 2500 bytes
        $input['text'] = $_POST['text'];
        if (strlen($input['text']) > 2500) {
           --> give error message to user, Request is Valid,
               but there was a problem what the user did, so
               you need to tell him.
        }
    
    
    Database:
        $db = new DatabaseConnection('Encoding: UTF-8');
        $row = $db->getTable('Texts')->newRow();
        $row['text'] = $input['text'];
        $row->insert();
    
    
    Display the result to the user:
    
        header('Content-Type: text/html; charset=utf-8');
    
        You just posted: <?php echo htmlspecialchars($input['text']); ?>
    

    如此示例所示,您执行的输入验证需要特定于您的案例。您应该知道您期望的输入字符集,然后让您的应用程序处理它。

    接下来,在此示例中,此处不需要使用输入strip_tags

    由于此应用程序适用于正确配置的主机,因此您在此处找不到任何strip_slashes

    长度检查表明,检查的内容多于基础知识。它总是取决于您的需求,输入应始终有限制。在这种情况下,存在硬限制(5000)和软限制,将通知用户。例如。数据库中的列可能具有特定的大小,因此无论如何都无法存储更多。

    数据库就是这样做的。如示例所示,数据仅存储在其中。这就是为什么你应该使用某种类型的数据库层来处理它,所以你不必在脚本的许多地方都这样做。如果您不知道从哪里开始,请使用PDO提供的参数化查询。 PDO是PHP中的数据库抽象,可用于MySQL数据库。

    另一个重要的部分是输出。你没有在你的例子中命名,我把它放在这里,以显示htmlspecialchars所在的位置:用户输入中的标记将在网站上正确显示。


      

    ... utf8_binlatin1_swedish_ci中的其他人。我接受utf8_bin ...

    您在此处列出的是排序规则,唯一的定义是如何对数据进行排序。

    您可能担心列本身的编码,对于文本字段,如果您的应用程序采用UTF-8,则应为UTF-8 - 因此数据库可以存储所有输入。 / p>

    您给出的示例表明您使用的是ISO-8859-1而不是UTF-8,因此您的数据库字段不能为UTF-8 - 但它们可以。

    您可以在数据库列中使用任何编码,只要它允许您存储输入编码的数据而不会丢失。在您的示例中,您可以将ISO-8859-1输入文本存储到UTF-8数据库列中。

答案 3 :(得分:0)

您对数据的处理过多。您应该仅为某些用法修改字符串,因为某些操作在某些情况下是无用的。

  • 从用户那里获取数据:filter_input因为它可以轻松处理魔术引号
  • 将数据作为HTML架构的一部分输出:htmlentities以删除每个特殊字符
  • 将数据存储到数据库:使用参数化查询PDO
  • 使用一些数据作为标题(例如send emails或重定向您的用户):使用str_replace删除\ r和\ n
  • 的出现次数
  • 在输出json时,您有json_encode来帮助