如何在难以使用绑定变量的情况下解决SQL注入漏洞?

时间:2014-05-02 20:15:21

标签: php mysql sql sql-injection

我习惯使用参数化语句,但在此语句中,参数的数量可能会根据搜索字符串中的单词数而变化。
所以“食物”= 1个参数,“一些食物”= 2,“大量食物”= 3等等

我基本上设置了一个查询,允许搜索网站,改进功能,我可以搜索列的某些单词而不必完美匹配。

例如,我将俱乐部名称及其城市联合起来;
名称:薄荷俱乐部,
城市:利兹 上校:利兹铸币局

搜索:'Mint Leeds'通常会导致找不到行。但是通过这种修改,它可以工作。

使用未知数量的参数我基本上想知道如何保护我的语句,因为我认为它仍然对sql注入开放。

以下代码:

$keystring = $mysqli->real_escape_string($_POST["s"]);
$key = strtoupper($keystring);

$key_arr = explode(" ", $key);
$num_key = count($key_arr);

if($num_key >= 2){
    $where = '';
    foreach($key_arr as $val){
        $where .= "(CONCAT(name,' ',city) LIKE '%".$val."%') AND ";
    }
    $where = substr($where, 0, -4);
    $clubWhere = $where;
}
else{
    $key = "%".$key."%";
    $clubWhere = "(CONCAT(name,' ',city) LIKE '".$key."')";
}

$search_stmt = $mysqli->prepare("SELECT id, name, type AS 'col3', city AS 'col4', 'Club' AS 'table' FROM studentnights_clubs WHERE ".$clubWhere."
                                    UNION
                                    SELECT id, name, description AS 'col3', image AS 'col4', 'Event' AS 'table' FROM studentnights_events WHERE UCASE(name) LIKE ?
                                    UNION
                                    SELECT id, name, '' AS 'col3', '' AS 'col4', 'Genre' AS 'table' FROM studentnights_music WHERE UCASE(name) LIKE ?
                                    ORDER BY
                                        CASE
                                            WHEN name LIKE ? THEN 1
                                            WHEN name LIKE ? THEN 3
                                            ELSE 2
                                        END
                                    LIMIT 10");
$search_stmt->bind_param('ssss', $key, $key, $key, $key);
$search_stmt->execute();
$search_stmt->store_result();
$search_stmt_num = $search_stmt->num_rows;
$search_stmt->bind_result($id, $name, $col3, $col4, $table);

2 个答案:

答案 0 :(得分:1)

  1. 很遗憾你为这项任务选择了原始的mysqli - 大多数令人厌烦的API曾经存在过。
  2. 对于全文搜索,您应该使用专用的mysql功能或外部搜索引擎,如Sphinx Search。它不仅可以让您免受这些繁琐且不充分的代码的影响,而且它实际上可以与真实数据库一起使用,而不仅仅是测试记录集为100行。
  3. 如果你想坚持不合适的mysql搜索和mysqli api(根据我的经验,PHP用户特别不愿意使用正确的工具)那么至少使用参数然后动态绑定。
  4. 像这样的东西(删除所有无用的分支),

    $where   = '';
    $values  = array();
    foreach(explode(" ", $key) as $val)
    {
        $values[] = "%$val%";
        $where   .= "(CONCAT(name,' ',city) LIKE ?) AND ";
    }
    $where = substr($where, 0, -4);
    

    然后将其他值添加到$ values数组

    $values[] = $key;
    $values[] = $key;
    $values[] = $key;
    $values[] = $key;
    

    然后使用动态绑定,如此处所述https://stackoverflow.com/a/17874410/285587

答案 1 :(得分:-1)

您的$val$key项是用户提供的搜索参数,因此您需要确保其中的垃圾无法通过查询。

一种可能性:准备两个不同的SQL语句,并使用具有适当数量的绑定变量的语句。

另一个:在使用它们构造SQL语句之前清理传入的字符串。您说他们会放置名称,例如'newcastle-upon-tyne''denver''new york'。例如,它们可能不是'new ; drop table super_users;'

所以在这里你可以如何消毒它们。

$val = $mysqli->real_escape_string(preg_replace('[^- A-Za-z0-9]', '', $val ));

这行代码将消除$val中的所有字符,但字母,数字,空格和短划线除外。然后它会将转义符放入字符串中以对mysqli进行清理。

如果您预处理'new ; drop table super_users;',您将获得'new drop table superusers',这对于查询来说足够安全,尽管毫无意义。

您还可以在用户提供的搜索字词中查找禁用的字符,如果存在,则拒绝进行搜索。

看,我们都知道使用绑定变量更安全,但是如果你不能,你仍然可以清理东西。