在PHP中保护SQL查询的最佳方法

8

如果我使用PHP在MySQL数据库上运行查询,如下所示:

$query="SELECT * FROM tablename";

如何防止SQL注入这样的攻击?我听说过一些转义方法,但它们会不会在查询中留下斜杠呢?


4
根据定义,这个查询不会受到SQL注入的影响。 - zerkms
4
“使用PDO并使用预处理语句而不是将字符串粘在一起”是一个有效的答案吗? - mu is too short
SQL注入只有在存在WHERE子句时才会发生。 假设您的查询语句是: SELECT * FROM tablename WHERE id='$_POST['id']'假设我在 id 的表单字段中输入 ' OR '1'='1。 现在,您的SQL查询将变为: SELECT * FROM tablename WHERE id='' OR '1' = '1' 这将始终返回所有字段。 - Pranav Hosangadi
在这种情况下,包括一个分号更安全吗?因为在分号之后添加OR会使查询无效。 - undefined
7个回答

14

你在问题中展示的查询没有使用用户提供的值,因此不存在SQL注入的情况,但在一般情况下:-

首先,在使用查询之前,必须验证所有用户输入(用户名、电子邮件等)。例如:如果您只允许用户名中包含字母数字字符,则必须检查输入是否实际为字母数字字符,然后才能继续形成数据库查询,并且您还必须检查所有输入的大小。

之后,在我看来,预处理语句是防止SQL注入的最佳选择。

mysql_real_escape_string()的问题:

由于mysql_real_escape_string()根据默认字符集转义字符,因此它比addslashes()函数更好,并且正确地净化由于多字节字符集的滥用而引起的SQL注入,但在另一篇文章这里,说明了一种解决方法,即仍然可以进行注入攻击。

解决方案:

因此,防止SQL注入的适当和更好的方式是使用预处理语句。它是一种技术,在插入用户输入(参数)之前预编译SQL语句,并将其视为可重用的SQL模板。因此,它将用户输入与实际SQL代码分离,并且SQL解析器永远不会解析用户输入。

除了安全性外,它还优化了SQL查询的速度。它有助于需要使用不同用户输入多次运行相同查询的情况。

您可以参考PHP手册以获取实现详细信息。


2
当我提出这个问题时,我在SQL方面的经验远不如现在。现在我学到了很多,我知道你的答案是最好的答案。我不确定为什么它得分那么低,但它非常正确。此外,PHP中的mysql_*扩展现在已被弃用。不仅如此,在我使用SQL的任何其他语言中,准备好的语句似乎都是最好的解决方案。 - muttley91
1
是的,我也对那个负面投票没有任何口头反对感到惊讶。不管怎样,谢谢! - zixtor
@zixtor,他没有指定使用哪个数据库。并不是每次都可以使用预处理语句。这个Mssql API http://php.net/manual/en/book.mssql.php 没有任何预处理语句。在这种情况下该怎么办? - scarface

8

你不应该使用 select *,而是只获取你需要的字段。

你需要转义用户输入的文本或使用从中派生的数据。

你需要使用 mysql_real_escape_string()


1
那么,如果传递的查询始终是恒定的且用户无法修改,这是否意味着它是安全的? - muttley91
1
如果您确定没有任何字段被篡改,那么是的,我想是这样的! - Lee Armstrong
兄弟,如果你正在使用mysql_real_escape_string(),SQL注入也是可能的。 - Davinder Kumar

1

当你像这样制作时,就可以进行SQL注入

$query="SELECT * FROM tablename WHERE Name LIKE '" . $_GET["name"] . "'";

攻击者可以在get参数名称中简单地放置SQL注入 - 例如像 "' OR 1 OR '' = '" 这样的内容

确保每个get或post参数都通过mysql_real_escape_string或至少addslashes + intval进行传递。

$query="SELECT * FROM tablename WHERE Name LIKE '" . mysql_real_escape_string( $_GET["name"] ) . "'";

1
从您的查询中,我看到没有安全问题。
但是,假设您想在查询中涉及GET参数。 错误的方法
$query="SELECT * FROM tablename WHERE id = ".$_GET['id'] 

在这里,有可能会有人更改查询语句。
因此,您可以使用mysql_real_escape_string
正确的方法

 $query="SELECT * FROM tablename WHERE id = '".mysql_real_escape_string($_GET['id'])."'";

这样做可以保护用户发送的参数。

但是,您应该始终验证来自用户的每个参数,并在此基础上通过上述常见方式进行安全保护。


1

首要建议,永远不要选择 * (全部),只选择必需的字段。如果所有字段都是必需的,则单独选择。这样,当其他开发人员继续项目时,他们会更快地了解情况。其次,为了保证查询的安全性,使用 mysql_real_escape_string();函数。如果传递的是 HTML,请使用htmlentities();函数。


1
如果有40个字段,那么阅读所有这些字段肯定不会很快;-)。他们会更快地了解发生了什么。 - zerkms
我可以问一下如何编写代码以选择特定列 "column1,column2,column3" 吗?我已尝试使用逗号,但返回了错误:S - muttley91
SELECT column1,column2,column3 FROM table WHERE column_name='' - Grigor

0

我已经使用了这段代码,请看一下它是否正确,现在是否无法注入

据我所知:如果我们运行插入查询,注入代码就可以被注入。请纠正我,我不是很有教育背景的程序员。

$rs=mysql_query("Select * from subcat where CATID='".mysql_real_escape_string($_GET['cat'])."' order by ID ASC");
while($row=mysql_fetch_array($rs))
    {

    echo '<td align="left" style="text-decoration:none;padding-left:1px;    "><a href="detail.php?product='.$row['SUBCAT'].'" style="text-decoration:none;color:#000">'.$row['HEADING'].'</a>';
    echo '<td align="CENTER" style="text-decoration:none;padding-left:1px"><a href="detail.php?product='.$row['SUBCAT'].'" style="text-decoration:none;color:#000;">BUY NOW</a>';
    echo '<td align="CENTER" style="text-decoration:none;padding-left:50px"><a href="detail.php?product='.$row['SUBCAT'].'" style="text-decoration:none;font-weight:bold;color:#000">Rs.'.$row['PRICE'].'</a>';
    echo '<tr><td colspan=5 style="border-bottom:1px #232323 solid;">';
    }

这是一个答案还是一个问题?问题不应该出现在答案部分。另外,mysql_已经被弃用,应该使用mysqli_或PDO代替。 - John Dvorak

0

这是一个非常古老的帖子,但为了记录那些被搜索引擎转移的其他人:PDO Prepared Statements 是我的建议。

$connection = new PDO("mysql:host=" . _DBHOST_ . ";dbname=" . _DB_, _DBUSER_, _DBPASS_);

$statement = $connection->prepare("SELECT * FROM tablename WHERE id = :tid");
$statement->execute([':tid' => $_GET['id']]);

$result = $statement->fetch(); // or if you want multiple: $statement->fetchAll() 

好的一点是,你可以构建一些包装类和函数来大大简化你的工作。

话虽如此,现今通常一个好的框架(如symfony)已经为你完成了大部分的工作。如果你只需要数据库组件,你也可以通过composer将ORM数据库层整合到你的项目中:https://www.doctrine-project.org/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接