我最近参与了一个需要与PHP / SQL Server集成的项目。我正在寻找最快,最简单的函数来防止SQL Server上的SQL注入,因为我更喜欢MySQL,而且预计不会有更多与SQL Server相关的项目。
这个功能是否足够?
$someVal = mssql_escape($_POST['someVal']);
$query = "INSERT INTO tblName SET field = $someVal";
mssql_execute($query);
function mssql_escape($str) {
return str_replace("'", "''", $str);
}
如果没有,我应该采取哪些额外步骤?
编辑:
我在Linux服务器上运行 - sqlsrv_query() only works if your hosting environment is windows
答案 0 :(得分:11)
最佳选择:不要使用连接在一起的SQL语句 - 使用参数化查询。
E.g。 不创建类似
的内容string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(" + value1 + ", " + value2 + ")"
或类似的东西,然后尝试通过替换单引号或其他东西来“消毒”它 - 你永远不会抓住所有东西,总会找到一种绕过你的“安全防护”的方法。
相反,请使用:
string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(@value1, @value2)";
然后在执行此INSERT语句之前设置参数值。这是避免SQL注入的唯一可靠方法 - 使用它!
更新:如何使用PHP的参数化查询 - 我found something here - 这有帮助吗?
$tsql = "INSERT INTO DateTimeTable (myDate, myTime,
myDateTimeOffset, myDatetime2)
VALUES (?, ?, ?, ?)";
$params = array(
date("Y-m-d"), // Current date in Y-m-d format.
"15:30:41.987", // Time as a string.
date("c"), // Current date in ISO 8601 format.
date("Y-m-d H:i:s.u") // Current date and time.
);
$stmt = sqlsrv_query($conn, $tsql, $params);
所以看起来你不能使用@ value1,@ value2之类的“命名”参数,而只是使用问号?对于每个参数,您基本上只需创建一个参数数组,然后将其传递给查询。
本文Accessing SQL Server Databases with PHP也可能有所帮助 - 它有一个类似的如何使用参数化查询插入数据的示例。
更新:在您发现自己使用Linux之后,此方法不再适用。相反,您需要在PHP中使用备用库来调用数据库 - 类似于PDO。
PDO既可以在任何* nix类型的操作系统上运行,也可以在各种数据库(包括SQL Server)上运行,并且它也支持参数化查询:
$db = new PDO('your-connection-string-here');
$stmt = $db->prepare("SELECT priv FROM testUsers WHERE username=:username AND password=:password");
$stmt->bindParam(':username', $user);
$stmt->bindParam(':password', $pass);
$stmt->execute();
答案 1 :(得分:3)
转义为转义符号的字符串替换足以防止SQL注入攻击向量。
这仅适用于QUOTED_IDENTIFIER为ON时的SQL Server,以及当您没有对转义字符串执行stoopid操作时,例如截断它或将转义后的Unicode字符串转换为8位字符串。特别是,您需要将确定 QUOTED_IDENTIFIER设置为ON。通常这是默认值,但它可能取决于您在PHP中使用的库来访问MSSQL。
参数化是一种最佳实践,但是对于转义引号以防止SQL注入没有任何本质上的不安全因素,并且要小心谨慎。
转义字符串的rel问题不是替换的功效,而是忘记每次更换的可能性。
也就是说,您的代码会转义该值,但不会将值包装在引号中。你需要这样的东西:
function mssql_escape($str) {
return "N'" + str_replace("'", "''", $str) + "'";
}
上面的N
允许您传递更高的Unicode字符。如果这不是问题(即,您的文字字段为varchar
而不是nvarchar
),则可以删除N
。
现在,如果你这样做,有一些警告:
同样,就像其他人所说的那样,参数化查询更安全 - 不是因为转义引号不起作用(除了上面提到的那样),但是因为它更容易在视觉上确保你不会忘记逃避某些事情。
答案 2 :(得分:2)
不,这还不够。据我所知,字符串替换在一般情况下(在任何平台上)都不可能足够。
为了防止SQL注入,所有查询都需要参数化 - 作为参数化查询或带参数的存储过程。
在这些情况下,数据库调用库(即ADO.NET和SQL Command)将参数与查询分开发送,服务器将应用这些参数,从而消除了以任何方式更改实际SQL的能力。除了注入之外,这还有许多好处,包括代码页问题和日期转换问题 - 如果服务器不希望它们以客户端的方式完成,那么对字符串的任何转换都可能会有问题。
答案 3 :(得分:2)
我部分不同意其他海报。如果通过使引号加倍的函数运行所有参数,则应防止任何可能的注入攻击。实际上在实践中,更频繁的问题不是故意的破坏,而是因为一个值合法地包含单个引用而破坏的查询,例如名为“O'Hara”的客户或“不要在9:00之前调用Sally”的注释字段。无论如何,我确实一直逃避这种情况并且从未遇到过问题。
一个警告:在某些数据库引擎上,除了单引号外,还可能存在其他危险字符。我知道的唯一例子是Postgres,其中反斜杠是魔术。在这种情况下,您的转义函数也必须加倍反斜杠。查看文档。
我没有反对使用预准备语句,对于简单的情况,唯一改变的是参数的值,它们是一个很好的解决方案。但我经常发现我必须根据程序中的条件来构建查询,比如参数X不是null,那么我不仅需要将它添加到where子句中,而且我还需要一个额外的连接来获取价值我真的需要测试。准备好的陈述无法处理。当然,您可以将SQL构建为片段,将其转换为预准备语句,然后提供参数。但这只是一个没有明显收获的痛苦。
这些天我主要用Java编写代码,允许函数重载,也就是说,根据传入参数的类型有多个实现。所以我例程编写了一组函数,我通常将其命名为“q”代表“quote”,它返回给定的类型,适当引用。对于字符串,它会使任何引号都加倍,然后在整个事物周围打出引号。对于整数,它只返回整数的字符串表示。对于日期,它将转换为JDBC(Java SQL)标准日期格式,然后驱动程序将其转换为正在使用的特定数据库所需的任何格式。等等(在我当前的项目中,我甚至将数组作为传入类型包含在内,我将其转换为适合在IN子句中使用的格式。)然后,每次我想在SQL语句中包含一个字段时,我只是写“ q(X)”。由于这是在必要时打上引号,我不需要额外的字符串操作来引用引号,因此它可能就像不进行转义一样容易。
例如,易受伤害的方式:
String myquery =“select customer from customer where customercode ='”+ custcode +“'”;
安全的方式:
String myquery =“从客户中选择姓名,其中customercode =”+ q(custcode);
正确的方式不是以错误的方式打字,所以很容易养成良好的习惯。