为什么我们总是喜欢在 SQL 语句中使用参数?

我对使用数据库非常陌生。现在我可以编写 SELECTUPDATEDELETEINSERT命令。但我看过很多论坛,我们更喜欢写:

SELECT empSalary from employee where salary = @salary

而不是:

SELECT empSalary from employee where salary = txtSalary.Text

为什么我们总是喜欢使用参数,我将如何使用它们?

我想知道第一种方法的用途和好处。我甚至听说过 SQL 注入,但我并不完全理解它。我甚至不知道 SQL 注入是否与我的问题有关。

153273 次浏览

当数据库与程序接口(如桌面程序或网站)一起使用时,使用参数有助于防止 SQL 注入攻击

在您的示例中,用户可以通过在 txtSalary中创建语句直接在数据库上运行 SQL 代码。

例如,如果他们要编写 0 OR 1=1,执行的 SQL 将是

 SELECT empSalary from employee where salary = 0 or 1=1

所有的薪水将被归还。

此外,用户可能对数据库执行更糟糕的命令,包括删除数据库。如果用户编写 0; Drop Table employee:

SELECT empSalary from employee where salary = 0; Drop Table employee

然后将删除表 employee


In your case, it looks like you're using .NET. Using parameters is as easy as:

string sql = "SELECT empSalary from employee where salary = @salary";


using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
var salaryParam = new SqlParameter("salary", SqlDbType.Money);
salaryParam.Value = txtMoney.Text;


command.Parameters.Add(salaryParam);
var results = command.ExecuteReader();
}
Dim sql As String = "SELECT empSalary from employee where salary = @salary"
Using connection As New SqlConnection("connectionString")
Using command As New SqlCommand(sql, connection)
Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
salaryParam.Value = txtMoney.Text


command.Parameters.Add(salaryParam)


Dim results = command.ExecuteReader()
End Using
End Using

编辑2016-4-25:

根据 GeorgeStocker 的注释,我更改了示例代码以避免使用 AddWithValue。另外,通常建议在 using语句中包装 IDisposable

您是对的,这与 SQL 注入有关,SQL 注入是一个漏洞,允许恶意用户对您的数据库执行任意语句。这个旧时最受欢迎的 XKCD 漫画展示了这个概念:

Her daughter is named Help I'm trapped in a driver's license factory.


在你的例子中,如果你只是使用:

var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query

您可以接受 SQL 注入,例如,假设有人输入 txtSalary:

1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.

当您执行这个查询时,它将执行一个 SELECT和一个 UPDATEDROP,或者他们想要的任何东西。最后的 --只是简单地注释掉查询的其余部分,如果您在 txtSalary.Text之后连接任何内容,这将在攻击中非常有用。


The correct way is to use parameterized queries, eg (C#):

SqlCommand query =  new SqlCommand("SELECT empSalary FROM employee
WHERE salary = @sal;");
query.Parameters.AddWithValue("@sal", txtSalary.Text);

With that, you can safely execute the query.

为了参考如何避免其他几种语言的 SQL 注入,请检查 bobby-tables.com,一个由 所以用户维护的网站。

In Sql when any word contain @ sign it means it is variable and we use this variable to set value in it and use it on number area on the same sql script because it is only restricted on the single script while you can declare lot of variables of same type and name on many script. We use this variable in stored procedure lot because stored procedure are pre-compiled queries and we can pass values in these variable from script, desktop and websites for further information read Declare Local Variable, 存储过程 and sql injections.

也可以阅读 Protect from sql injection,它将指导您如何保护您的数据库。

希望它也能帮助你理解我的任何问题。

其他答案包括为什么参数是重要的,但有一个缺点!进去。Net 中,有几种方法可以创建参数(Add、 AddWithValue) ,但是它们都需要您不必要地担心参数名称,而且它们都降低了代码中 SQL 的可读性。当您试图冥想 SQL 时,您需要在上面或下面搜索,以查看参数中使用了什么值。

我谦虚地声明我的小 SqlBuilder 类是 编写参数化查询的最优雅方式

C #

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();

您的代码将更短,可读性更强。您甚至不需要额外的行,并且,当您回读时,您不需要到处寻找参数的值。你需要的课程在这里。

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;


public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "@SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "@SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}

In addition to other answers need to add that parameters not only helps prevent sql injection but 可以提高查询的性能. Sql server caching parameterized query plans and reuse them on repeated queries execution. If you not parameterized your query then Sql 服务器将为每个查询编译新计划(with some exclusion) execution if text of query would differ.

有关查询计划缓存的详细信息

我的第一次之后两年,我重新开始..。

为什么我们更喜欢参数?SQL 注入显然是一个很大的原因,但可能是 we're secretly longing to get back to SQL as a language。字符串文字中的 SQL 已经是一种奇怪的文化实践,但至少您可以将请求复制并粘贴到管理工作室。使用宿主语言条件和控制结构动态构造 SQL,当 SQL 具有条件和控制结构时,就是0级野蛮。您必须在调试或跟踪中运行应用程序,以查看它生成的 SQL。

不要仅仅停留在参数上。一直使用 QueryFirst”rel = “ norefrer”> QueryFirst (免责声明: 这是我写的)。您的 SQL 存在于 在一个.sql 文件中中。您可以在神话般的 TSQL 编辑器窗口中编辑它,为表和列提供语法验证和智能感知。您可以在特殊注释部分分配测试数据,然后单击“播放”在窗口中运行查询。创建参数就像在 SQL 中放入“@myParam”一样简单。然后,每次保存时,QueryFirst 都会为查询生成 C # 包装器。您的参数作为 Execute ()方法的参数弹出,强类型。结果在强类型 POCO 的 IEnumable 或 List 中返回,这些 POCO 是从查询返回的实际架构生成的类型。如果您的查询不运行,您的应用程序将无法编译。如果数据库架构发生更改,查询运行但某些列消失,则编译错误指向尝试访问缺失数据的 代码中的行。而且还有许多其他的优势。为什么要用其他方式访问数据?

旧的职位,但希望确保新人了解 存储过程

我的10点价值在于,如果您能够将 SQL 语句编写为 stored procedure,那么在我看来,这是最佳方法。我 一直都是使用存储进程,从不循环通过我的主代码中的记录。例如: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class

使用存储过程时,可以将用户限制为仅具有 EXECUTE权限,也就是 reducing security risks

您的存储过程本质上是参数化的,您可以指定输入和输出参数。

存储过程(如果它通过 SELECT语句返回数据)可以按照与代码中常规 SELECT语句完全相同的方式访问和读取。

在 SQLServer 上编译时,它也运行得更快。

我有没有提到你可以做多个步骤,比如 update一个表,检查另一个数据库服务器上的值,然后一旦最终完成,返回数据给客户端,所有的都在同一个服务器上,不与客户端交互。所以这比在代码中编写这个逻辑要快得多。