参数是否真的足以防止 Sql 注入?

我一直在向我的同事和这里的 SO 说教在 SQL 查询中使用参数的好处,特别是在。NET 应用程式。我甚至承诺他们对 SQL 注入攻击免疫。

但我开始怀疑这是不是真的。是否有任何已知的 SQL 注入攻击能够成功地攻击参数化查询?例如,您可以发送一个在服务器上导致缓冲区溢出的字符串吗?

当然,还有其他考虑因素要确保 Web 应用程序是安全的(如消毒用户输入等等) ,但现在我考虑的是 SQL 注入。我对攻击 MSSQL2005和2008特别感兴趣,因为它们是我的主要数据库,但是所有的数据库都很有趣。

编辑: 澄清我所说的参数和参数化查询的含义。使用参数意味着使用“变量”而不是在字符串中构建 sql 查询。
所以,与其这样做:

SELECT * FROM Table WHERE Name = 'a name'

我们这样做:

SELECT * FROM Table WHERE Name = @Name

然后在 query/command 对象上设置@Name 参数的值。

29135 次浏览

Placeholders are enough to prevent injections. You might still be open to buffer overflows, but that is a completely different flavor of attack from an SQL injection (the attack vector would not be SQL syntax but binary). Since the parameters passed will all be escaped properly, there isn't any way for an attacker to pass data that will be treated like "live" SQL.

You can't use functions inside placeholders, and you can't use placeholders as column or table names, because they are escaped and quoted as string literals.

However, if you use parameters as part of a string concatenation inside your dynamic query, you are still vulnerable to injection, because your strings will not be escaped but will be literal. Using other types for parameters (such as integer) is safe.

That said, if you're using use input to set the value of something like security_level, then someone could just make themselves administrators in your system and have a free-for-all. But that's just basic input validation, and has nothing to do with SQL injection.

any sql parameter of string type (varchar, nvarchar, etc) that is used to construct a dynamic query is still vulnerable

otherwise the parameter type conversion (e.g. to int, decimal, date, etc.) should eliminate any attempt to inject sql via the parameter

EDIT: an example, where parameter @p1 is intended to be a table name

create procedure dbo.uspBeAfraidBeVeryAfraid ( @p1 varchar(64) )
AS
SET NOCOUNT ON
declare @sql varchar(512)
set @sql = 'select * from ' + @p1
exec(@sql)
GO

If @p1 is selected from a drop-down list it is a potential sql-injection attack vector;

If @p1 is formulated programmatically w/out the ability of the user to intervene then it is not a potential sql-injection attack vector

A buffer overflow is not SQL injection.

Parametrized queries guarantee you are safe against SQL injection. They don't guarantee there aren't possible exploits in the form of bugs in your SQL server, but nothing will guarantee that.

It is possible for a stored proc to be vulnerable to special types of SQL injection via overflow/truncation, see: Injection Enabled by Data Truncation here:

http://msdn.microsoft.com/en-us/library/ms161953.aspx

Your data is not safe if you use dynamic sql in any way shape or form because the permissions must be at the table level. Yes you have limited the type and amount of injection attack from that particular query, but not limited the access a user can get if he or she finds a way into the system and you are completely vunerable to internal users accessing what they shouldn't in order to commit fraud or steal personal information to sell. Dynamic SQL of any type is a dangerous practice. If you use non-dynamic stored procs, you can set permissions at the procesdure level and no user can do anything except what is defined by the procs (except system admins of course).

No, there is still risk of SQL injection any time you interpolate unvalidated data into an SQL query.

Query parameters help to avoid this risk by separating literal values from the SQL syntax.

'SELECT * FROM mytable WHERE colname = ?'

That's fine, but there are other purposes of interpolating data into a dynamic SQL query that cannot use query parameters, because it's not an SQL value but instead a table name, column name, expression, or some other syntax.

'SELECT * FROM ' + @tablename + ' WHERE colname IN (' + @comma_list + ')'
' ORDER BY ' + @colname'

It doesn't matter whether you're using stored procedures or executing dynamic SQL queries directly from application code. The risk is still there.

The remedy in these cases is to employ FIEO as needed:

  • Filter Input: validate that the data look like legitimate integers, table names, column names, etc. before you interpolate them.

  • Escape Output: in this case "output" means putting data into a SQL query. We use functions to transform variables used as string literals in an SQL expression, so that quote marks and other special characters inside the string are escaped. We should also use functions to transform variables that would be used as table names, column names, etc. As for other syntax, like writing whole SQL expressions dynamically, that's a more complex problem.

There seems to be some confusion in this thread about the definition of a "parameterised query".

  • SQL such as a stored proc that accepts parameters.
  • SQL that is called using the DBMS Parameters collection.

Given the former definition, many of the links show working attacks.

But the "normal" definition is the latter one. Given that definition, I don't know of any SQL injection attack that will work. That doesn't mean that there isn't one, but I have yet to see it.

From the comments, I'm not expressing myself clearly enough, so here's an example that will hopefully be clearer:

This approach is open to SQL injection

exec dbo.MyStoredProc 'DodgyText'

This approach isn't open to SQL injection

using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
newParam.Value = "DodgyText";
.....
cmd.Parameters.Add(newParam);
.....
cmd.ExecuteNonQuery();
}

Just remember that with parameters you can easily store the string, or say username if you don't have any policies, "); drop table users; --"

This in itself won't cause any harm, but you better know where and how that date is used further on in your application (e.g. stored in a cookie, retrieved later on to do other stuff.

You can run dynamic sql as example

DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);


SELECT  @ParameterDefinition = '@date varchar(10)'


SET @SQL='Select CAST(@date AS DATETIME) Date'


EXEC sp_executeSQL @SQL,@ParameterDefinition,@date='04/15/2011'