这足以防止使用 SQL Server 时的查询注入吗?

发布于 2024-08-28 11:13:08 字数 472 浏览 6 评论 0原文

我最近接手了一个项目,需要与 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() 仅在您的托管环境是 Windows 时才有效

I have recently taken on a project in which I need to integrate with PHP/SQL Server. I am looking for the quickest and easiest function to prevent SQL injection on SQL Server as I prefer MySQL and do not anticipate many more SQL Server related projects.

Is this function sufficient?

$someVal = mssql_escape($_POST['someVal']);

$query = "INSERT INTO tblName SET field = $someVal";

mssql_execute($query);

function mssql_escape($str) {
    return str_replace("'", "''", $str);
}

If not, what additional steps should I take?


EDIT:
I am running on a Linux server - sqlsrv_query() only works if your hosting environment is windows

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

戴着白色围巾的女孩 2024-09-04 11:13:08

最佳选择:不要使用连接在一起的 SQL 语句 - 使用参数化查询。

例如,不要创建类似的东西

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 的参数化查询 - 我在这里找到了一些东西 - 这有帮助吗?

$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这样的“命名”参数,而是只使用问号?对于每个参数,您基本上只需创建一个参数数组,然后将其传递到查询中。

本文使用 PHP 访问 SQL Server 数据库可能也有帮助 - 它有一个类似的示例,说明如何使用参数化查询插入数据。

更新:在您表明自己使用的是 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();

The best option: do not use SQL statements that get concatenated together - use parametrized queries.

E.g. do not create something like

string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(" + value1 + ", " + value2 + ")"

or something like that and then try to "sanitize" it by replacing single quotes or something - you'll never catch everything, someone will always find a way around your "safe guarding".

Instead, use:

string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(@value1, @value2)";

and then set the parameter values before executing this INSERT statement. This is really the only reliable way to avoid SQL injection - use it!

UPDATE: how to use parametrized queries from PHP - I found something here - does that help at all?

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

So it seems you can't use "named" parameters like @value1, @value2, but instead you just use question marks ? for each parameter, and you basically just create a parameter array which you then pass into the query.

This article Accessing SQL Server Databases with PHP might also help - it has a similar sample of how to insert data using the parametrized queries.

UPDATE: after you've revealed that you're on Linux, this approach doesn't work anymore. Instead, you need to use an alternate library in PHP to call a database - something like PDO.

PDO should work both on any *nix type operating system, and against all sorts of databases, including SQL Server, and it supports parametrized queries, too:

$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();
赢得她心 2024-09-04 11:13:08

不,这还不够。据我所知,一般情况下(在任何平台上)字符串替换永远都不够。

为了防止 SQL 注入,所有查询都需要参数化 - 要么作为参数化查询,要么作为带有参数的存储过程。

在这些情况下,数据库调用库(即 ADO.NET 和 SQL Command)将与查询分开发送参数,并且服务器应用它们,这消除了以任何方式更改实际 SQL 的能力。除了注入之外,这还有很多好处,其中包括代码页问题和日期转换问题 - 就此而言,如果服务器不希望它们按照客户端的方式完成,则任何字符串转换都可能会出现问题。

No, it's not sufficient. To my knowledge, string replacement can never really be sufficient in general (on any platform).

To prevent SQL injection, all queries need to be parameterized - either as parameterized queries or as stored procedures with parameters.

In these cases, the database calling library (i.e. ADO.NET and SQL Command) sends the parameters separately from the query and the server applies them, which eliminates the ability for the actual SQL to be altered in any way. This has numerous benefits besides injection, which include code page issues and date conversion issues - for that matter any conversions to string can be problematic if the server does not expect them done the way the client does them.

_畞蕅 2024-09-04 11:13:08

我部分不同意其他海报。如果您通过双引号的函数运行所有参数,这应该可以防止任何可能的注入攻击。实际上,在实践中,更常见的问题不是故意破坏,而是由于值合法地包含单引号而导致查询中断,例如名为“O'Hara”的客户或“不要在 9:00 之前致电 Sally”的评论字段。不管怎样,我一直都是这样逃跑的,从来没有遇到过问题。

需要注意的是:在某些数据库引擎上,除了单引号之外,还可能存在其他危险字符。我知道的唯一例子是 Postgres,其中反斜杠很神奇。在这种情况下,您的转义函数也必须使用双反斜杠。检查文档。

我并不反对使用准备好的语句,对于简单的情况,唯一改变的是参数的值,它们是一个很好的解决方案。但我经常发现我必须根据程序中的条件分段构建查询,例如如果参数 X 不为空,那么我不仅需要将其添加到 where 子句,而且还需要一个额外的联接来访问我真的需要测试的值。准备好的语句无法处理这个问题。当然,您可以分段构建 SQL,将其转换为准备好的语句,然后提供参数。但这只是一种痛苦,没有明显的收获。

这些天我主要用Java编写代码,允许函数重载,也就是说,根据传入参数的类型有多种实现。因此,我通常会编写一组函数,我通常将其简单地命名为“q”(表示“引用”),它们返回给定的类型,并进行适当的引用。对于字符串,它将双引号,然后在整个内容周围加上引号。对于整数,它仅返回整数的字符串表示形式。对于日期,它转换为 JDBC (Java SQL) 标准日期格式,然后驱动程序应将其转换为所使用的特定数据库所需的任何格式。等等(在我当前的项目中,我什至将数组作为传入类型包含在内,并将其转换为适合在 IN 子句中使用的格式。)然后每次我想在 SQL 语句中包含字段时,我只需编写“ q(x)”。由于这是在必要时加上引号,因此我不需要额外的字符串操作来加上引号,因此它可能与不进行转义一样简单。

例如,易受攻击的方式:

String myquery="select name from customer where customercode='"+custcode+"'";

安全的方法:

String myquery="select name from customer where customercode="+q(custcode);

正确的方法并不比错误的方法更费力,所以很容易养成一个好习惯。

I partially disagree with other posters. If you run all your parameters through a function that double the quotes, this should prevent any possible injection attack. Actually in practice the more frequent problem is not deliberate sabotague but queries that break because a value legitimately includes a single quote, like a customer named "O'Hara" or a comment field of "Don't call Sally before 9:00". Anyway, I do escapes like this all the time and have never had a problem.

One caveat: On some database engines, there could be other dangerous characters besides a single quote. The only example I know is Postgres, where the backslash is magic. In this case your escape function must also double backslashes. Check the documentation.

I have nothing against using prepared statements, and for simple cases, where the only thing that changes is the value of the parameter, they are an excellent solution. But I routinely find that I have to build queries in pieces based on conditions in the program, like if parameter X is not null then not only do I need to add it to the where clause but I also need an additional join to get to the value I really need to test. Prepared statements can't handle this. You could, of course, build the SQL in pieces, turn it into a prepared statement, and then supply the parameters. But this is just a pain for no clear gain.

These days I mostly code in Java that allows functions to be overloaded, that is, have multiple implementations depending on the type of the passed in parameter. So I routine write a set of functions that I normally name simply "q" for "quote", that return the given type, suitably quoted. For strings, it doubles any quote marks, then slaps quote marks around the whole thing. For integers it just returns the string representation of the integer. For dates it converts to the JDBC (Java SQL) standard date format, which the driver is then supposed to convert to whatever is needed for the specific database being used. Etc. (On my current project I even included array as a passed in type, which I convert to a format suitable for use in an IN clause.) Then every time I want to include a field in a SQL statement, I just write "q(x)". As this is slapping quotes on when necessary, I don't need the extra string manipulation to put on quotes, so it's probably just as easy as not doing the escape.

For example, vulnerable way:

String myquery="select name from customer where customercode='"+custcode+"'";

Safe way:

String myquery="select name from customer where customercode="+q(custcode);

The right way is not particularly more to type than the wrong way, so it's easy to get in a good habit.

痴情换悲伤 2024-09-04 11:13:08

将字符串替换为转义引号足以防止 SQL 注入攻击向量。

这仅适用于当 QUOTED_IDENTIFIER 为 ON 时的 SQL Server,并且当您不对转义字符串执行一些愚蠢的操作(例如截断它)时或者在转义后将 Unicode 字符串转换为 8 位字符串。特别是,您需要确保 QUOTED_IDENTIFIER 设置为 ON。通常这是默认设置,但它可能取决于您在 PHP 中使用的用于访问 MSSQL 的库。

参数化是最佳实践,但只要谨慎小心,转义引号以防止 SQL 注入本身并没有什么不安全的地方。

转义字符串的 rel 问题不是替换的效果,而是每次都忘记进行替换的可能性。

也就是说,您的代码会转义该值,但不会将该值括在引号中。您需要这样的东西:

function mssql_escape($str) {
  return "N'" + str_replace("'", "''", $str) + "'";
}

上面的 N 允许您传递更高的 Unicode 字符。如果这不是问题(即您的文本字段是 varchar 而不是 nvarchar),您可以删除 N

现在,如果您这样做,有一些警告:

  1. 您需要确保为每个字符串值调用mssql_escape。这就是问题所在。
  2. 日期和 GUID 值也需要以相同的方式转义。
  3. 您应该验证数值,或者至少使用相同的函数对它们进行转义(MSSQL 会将字符串转换为适当的数值类型)。

同样,就像其他人所说的那样,参数化查询更安全——不是因为转义引号不起作用(除了上面提到的之外,它确实有效),而是因为更容易直观地确保您没有忘记转义某些内容。

String replacement to escape quotes IS sufficient to prevent SQL injection attack vectors.

This only applies to SQL Server when QUOTED_IDENTIFIER is ON, and when you don't do something stoopid to your escaped string, such as truncating it or translating your Unicode string to an 8-bit string after escaping. In particular, you need to make sure QUOTED_IDENTIFIER is set to ON. Usually that's the default, but it may depend on the library you are using in PHP to access MSSQL.

Parameterization is a best practice, but there is nothing inherently insecure about escaping quotes to prevent SQL injection, with due care.

The rel issue with escaping strings is not the efficacy of the replacement, it is the potential for forgetting to do the replacement every time.

That said, your code escapes the value, but does not wrap the value in quotes. You need something like this instead:

function mssql_escape($str) {
  return "N'" + str_replace("'", "''", $str) + "'";
}

The N above allows you to pass higher Unicode characters. If that's not a concern (i.e., your text fields are varchar rather than nvarchar), you can remove the N.

Now, if you do this, there are some caveats:

  1. You need to make DAMNED SURE you call mssql_escape for every string value. And therein lies the rub.
  2. Dates and GUID values also need escaping in the same manner.
  3. You should validate numeric values, or at least escape them as well using the same function (MSSQL will cast the string to the appropriate numeric type).

Again, like others have said, parameterized queries are safer--not because escaping quotes doesn't work (it does except as noted above), but because it's easier to visually make sure you didn't forget to escape something.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文