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

发布于 2024-12-05 16:42:25 字数 413 浏览 2 评论 0原文

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

SELECT empSalary from employee where salary = @salary

...而不是:

SELECT empSalary from employee where salary = txtSalary.Text

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

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

I am very new to working with databases. Now I can write SELECT, UPDATE, DELETE, and INSERT commands. But I have seen many forums where we prefer to write:

SELECT empSalary from employee where salary = @salary

...instead of:

SELECT empSalary from employee where salary = txtSalary.Text

Why do we always prefer to use parameters and how would I use them?

I wanted to know the use and benefits of the first method. I have even heard of SQL injection but I don't fully understand it. I don't even know if SQL injection is related to my question.

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

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

发布评论

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

评论(7

好菇凉咱不稀罕他 2024-12-12 16:42:26

当数据库与桌面程序或网站等程序界面结合使用时,使用参数有助于防止SQL注入攻击

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

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

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

返回所有 empSalaries。

此外,用户可能会对您的数据库执行更糟糕的命令,包括删除它如果他们写了 0;删除表员工

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

然后将删除表employee


就您而言,您似乎正在使用 .NET。使用参数就像:

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 日:

根据 George Stocker 的评论,我将示例代码更改为不使用 AddWithValue。另外,通常建议您将 IDisposable 包装在 using 语句中。

Using parameters helps prevent SQL Injection attacks when the database is used in conjunction with a program interface such as a desktop program or web site.

In your example, a user can directly run SQL code on your database by crafting statements in txtSalary.

For example, if they were to write 0 OR 1=1, the executed SQL would be

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

whereby all empSalaries would be returned.

Further, a user could perform far worse commands against your database, including deleting it If they wrote 0; Drop Table employee:

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

The table employee would then be deleted.


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

Edit 2016-4-25:

As per George Stocker's comment, I changed the sample code to not use AddWithValue. Also, it is generally recommended that you wrap IDisposables in using statements.

心头的小情儿 2024-12-12 16:42:26

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

她女儿的名字叫救命,我被困在驾驶执照工厂里了。


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

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.

当您执行此查询时,它将执行 SELECTUPDATEDROP,或者他们想要的任何内容。最后的 -- 只是注释掉查询的其余部分,如果您在 txtSalary.Text 之后连接任何内容,这在攻击中会很有用。


正确的方法是使用参数化查询,例如(C#):

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

这样,您就可以安全地执行查询。

有关如何避免其他几种语言中的 SQL 注入的参考,请查看 bobby-tables.com,该网站由SO 用户

You are right, this is related to SQL injection, which is a vulnerability that allows a malicioius user to execute arbitrary statements against your database. This old time favorite XKCD comic illustrates the concept:

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


In your example, if you just use:

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

You are open to SQL injection. For example, say someone enters txtSalary:

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

When you execute this query, it will perform a SELECT and an UPDATE or DROP, or whatever they wanted. The -- at the end simply comments out the rest of your query, which would be useful in the attack if you were concatenating anything after 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.

For reference on how to avoid SQL injection in several other languages, check bobby-tables.com, a website maintained by a SO user.

负佳期 2024-12-12 16:42:26

除了其他答案之外,还需要添加参数不仅有助于防止 sql 注入,而且可以提高查询性能。 Sql 服务器缓存参数化查询计划并在重复查询执行时重用它们。如果您没有对查询进行参数化,那么如果查询文本不同,sql server 将为每个查询编译新计划(有一些排除)执行。

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

In addition to other answers need to add that parameters not only helps prevent sql injection but can improve performance of queries. Sql server caching parameterized query plans and reuse them on repeated queries execution. If you not parameterized your query then sql server would compile new plan on each query(with some exclusion) execution if text of query would differ.

More information about query plan caching

世界如花海般美丽 2024-12-12 16:42:26

我第一次尝试两年后,我又重新开始......

为什么我们更喜欢参数? SQL 注入显然是一个重要原因,但是否我们暗自渴望回到 SQL 作为一种语言。字符串文字中的 SQL 已经是一种奇怪的文化实践,但至少您可以将您的请求复制并粘贴到 Management Studio 中。使用宿主语言条件和控制结构动态构造的 SQL,当 SQL 有条件和控制结构时,只是 0 级野蛮行为。您必须在调试或跟踪中运行您的应用程序,才能查看它生成的 SQL。

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

Two years after my first go, I'm recidivating...

Why do we prefer parameters? SQL injection is obviously a big reason, but could it be that we're secretly longing to get back to SQL as a language. SQL in string literals is already a weird cultural practice, but at least you can copy and paste your request into management studio. SQL dynamically constructed with host language conditionals and control structures, when SQL has conditionals and control structures, is just level 0 barbarism. You have to run your app in debug, or with a trace, to see what SQL it generates.

Don't stop with just parameters. Go all the way and use QueryFirst (disclaimer: which I wrote). Your SQL lives in a .sql file. You edit it in the fabulous TSQL editor window, with syntax validation and Intellisense for your tables and columns. You can assign test data in the special comments section and click "play" to run your query right there in the window. Creating a parameter is as easy as putting "@myParam" in your SQL. Then, each time you save, QueryFirst generates the C# wrapper for your query. Your parameters pop up, strongly typed, as arguments to the Execute() methods. Your results are returned in an IEnumerable or List of strongly typed POCOs, the types generated from the actual schema returned by your query. If your query doesn't run, your app won't compile. If your db schema changes and your query runs but some columns disappear, the compile error points to the line in your code that tries to access the missing data. And there are numerous other advantages. Why would you want to access data any other way?

深海夜未眠 2024-12-12 16:42:26

在Sql中,当任何单词包含@符号时,它意味着它是变量,我们使用这个变量来设置其中的值,并在同一个sql脚本的数字区域上使用它,因为它只限制在单个脚本上,而你可以声明很多变量许多脚本上具有相同的类型和名称。我们在存储过程中使用此变量,因为存储过程是预编译的查询,我们可以从脚本、桌面和网站传递这些变量中的值,以获取更多信息,请阅读 声明局部变量, Sql 存储过程sql 注入

另请阅读防止 SQL 注入它将指导您如何保护数据库。

希望它能帮助您理解任何问题也可以评论我。

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, Sql Stored Procedure and sql injections.

Also read Protect from sql injection it will guide how you can protect your database.

Hope it help you to understand also any question comment me.

黎歌 2024-12-12 16:42:26

旧帖子,但希望确保新人了解存储过程

我的 10 美分价值是,如果您能够将 SQL 语句编写为存储过程,那么在我看来,这是最佳方法。我始终使用存储过程,并且从不在主代码中循环记录。例如:SQL 表> SQL存储过程> IIS/Dot.NET >类。

当您使用存储过程时,您可以限制用户仅具有EXECUTE权限,从而降低安全风险

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

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

由于它是在 SQL Server 上编译的,因此运行速度也更快。

我是否还提到过您可以执行多个步骤,例如更新表,检查另一个数据库服务器上的值,然后最终完成后,将数据返回给客户端,所有这些都在同一服务器上,并且没有交互与客户。因此,这比在代码中编写此逻辑要快得多。

Old post but wanted to ensure newcomers are aware of Stored procedures.

My 10¢ worth here is that if you are able to write your SQL statement as a stored procedure, that in my view is the optimum approach. I ALWAYS use stored procs and never loop through records in my main code. For Example: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

When you use stored procedures, you can restrict the user to EXECUTE permission only, thus reducing security risks.

Your stored procedure is inherently paramerised, and you can specify input and output parameters.

The stored procedure (if it returns data via SELECT statement) can be accessed and read in the exact same way as you would a regular SELECT statement in your code.

It also runs faster as it is compiled on the SQL Server.

Did I also mention you can do multiple steps, e.g. update a table, check values on another DB server, and then once finally finished, return data to the client, all on the same server, and no interaction with the client. So this is MUCH faster than coding this logic in your code.

肤浅与狂妄 2024-12-12 16:42:26

其他答案解释了为什么参数很重要,但有一个缺点!在.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();
}
}

Other answers cover why parameters are important, but there is a downside! In .net, there are several methods for creating parameters (Add, AddWithValue), but they all require you to worry, needlessly, about the parameter name, and they all reduce the readability of the SQL in the code. Right when you're trying to meditate on the SQL, you need to hunt around above or below to see what value has been used in the parameter.

I humbly claim my little SqlBuilder class is the most elegant way to write parameterized queries. Your code will look like this...

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

Your code will be shorter and much more readable. You don't even need extra lines, and, when you're reading back, you don't need to hunt around for the value of parameters. The class you need is here...

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