防止 SQL 注入的好方法是啥? [复制]

Posted

技术标签:

【中文标题】防止 SQL 注入的好方法是啥? [复制]【英文标题】:What are good ways to prevent SQL injection? [duplicate]防止 SQL 注入的好方法是什么? [复制] 【发布时间】:2021-04-04 07:13:03 【问题描述】:

我必须为我的 OJT 公司编写一个应用程序管理系统。前端用 C# 做,后端用 SQL 做。

现在我以前从未做过这个范围的项目;在学校里,我们只有关于 SQL 的基本课程。不知何故,我们的老师完全没有讨论 SQL 注入,这是我现在才通过在网上阅读到的。

所以无论如何我的问题是:如何防止 C# 中的 SQL 注入?我隐约认为可以通过适当地屏蔽应用程序的文本字段来做到这一点,使其只接受指定格式的输入。例如:电子邮件文本框的格式应为“example@examplecompany.tld”。这种方法就足够了吗?或者.NET 是否有预定义的方法来处理这样的事情?我可以将过滤器应用于文本框,使其仅接受电子邮件地址格式或名称文本框,因此它不接受特殊字符吗?

【问题讨论】:

other question was posted later,但更详尽。 【参考方案1】:

通过使用SqlCommand 和它的child collection of parameters,所有检查 sql 注入的痛苦都被消除了,将由这些类处理。

这里是一个例子,取自上面的一篇文章:

private static void UpdateDemographics(Int32 customerID,
    string demoXml, string connectionString)

    // Update the demographics for a store, which is stored  
    // in an xml column.  
    string commandText = "UPDATE Sales.Store SET Demographics = @demographics "
        + "WHERE CustomerID = @ID;";

    using (SqlConnection connection = new SqlConnection(connectionString))
    
        SqlCommand command = new SqlCommand(commandText, connection);
        command.Parameters.Add("@ID", SqlDbType.Int);
        command.Parameters["@ID"].Value = customerID;

        // Use AddWithValue to assign Demographics. 
        // SQL Server will implicitly convert strings into XML.
        command.Parameters.AddWithValue("@demographics", demoXml);

        try
        
            connection.Open();
            Int32 rowsAffected = command.ExecuteNonQuery();
            Console.WriteLine("RowsAffected: 0", rowsAffected);
        
        catch (Exception ex)
        
            Console.WriteLine(ex.Message);
        
    

【讨论】:

如果我想主动识别sql注入尝试怎么办。是否有任何好的库可以与这种方法结合使用,以便我识别和记录攻击? SQL 注入的第一个也是最简单的方法是结束当前字符串和语句的方法,方法是用单引号或双引号开始您的值,后跟大括号和分号。因此,通过检查给定的输入是否以这些开头将是一个很好的提示,可能是通过像^\s*['"]\s*\)\s*; 这样的正则表达式。这不是唯一的方法,而是最常见的方法。 请注意,SqlCommand 也是 IDisposable 所以应该在 using 块中。您可能想阅读can we stop using AddWithValue。 注意,我实现了这段代码,它在遇到上面带有“~”的“n”时崩溃了。在那之前它就像一个奇迹,现在我不确定其他语言......我希望这会安全。【参考方案2】:

SQL 注入可能是一个棘手的问题,但有一些方法可以解决它。只需使用 Linq2Entities、Linq2SQL、NHibrenate 之类的 ORM,您的风险就会降低。但是,即使使用它们,您也可能遇到 SQL 注入问题。

SQL 注入的主要内容是用户控制输入(与 XSS 一样)。在最简单的示例中,如果您有一个登录表单(我希望您永远不会有一个这样做的),它需要用户名和密码。

SELECT * FROM Users WHERE Username = '" + username + "' AND password = '" + password + "'"

如果用户为用户名 Admin' -- 输入以下内容,则在对数据库执行时 SQL 语句将如下所示。

SELECT * FROM Users WHERE Username = 'Admin' --' AND password = ''

在这种简单的情况下,使用参数化查询(这是 ORM 所做的)将消除您的风险。您还有一个鲜为人知的 SQL 注入攻击向量的问题,那就是存储过程。在这种情况下,即使您使用参数化查询或 ORM,您仍然会遇到 SQL 注入问题。存储过程可以包含执行命令,而这些命令本身可能容易受到 SQL 注入攻击。

CREATE PROCEDURE SP_GetLogin @username varchar(100), @password varchar(100) AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ' SELECT * FROM users' +
              ' FROM Product Where username = ''' + @username + ''' AND password = '''+@password+''''

EXECUTE sp_executesql @sql

因此,即使您使用参数化查询或 ORM,此示例也会出现与上一个示例相同的 SQL 注入问题。虽然这个例子看起来很傻,但你会惊讶于这样的东西被写的频率。

我的建议是使用 ORM 立即减少遇到 SQL 注入问题的机会,然后学习发现可能出现问题的代码和存储过程并努力解决它们。我不建议直接使用 ADO.NET(SqlClient、SqlCommand 等...),除非您必须这样做,不是因为将它与参数一起使用不安全,而是因为它更容易变得懒惰并开始编写 SQL使用字符串查询并忽略参数。 ORMS 在强迫您使用参数方面做得很好,因为这正是他们所做的。

下一步访问 OWASP 网站上的 SQL 注入 https://www.owasp.org/index.php/SQL_Injection 并使用 SQL 注入备忘单确保您可以发现并解决代码中可能出现的任何问题。 https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet 最后我想说的是,在您和您公司的其他开发人员之间进行良好的代码审查,您可以在其中审查彼此的代码,例如 SQL 注入和 XSS。很多时候程序员会错过这些东西,因为他们试图赶出一些功能并且没有花太多时间来审查他们的代码。

【讨论】:

我不知道 EF/类似 ORM 的 任何 SQL 注入风险,因为它们在内部使用参数化查询 - 你能提供链接吗?谢谢 我上面给出的存储过程的例子仍然容易受到 SQL 注入攻击,即使你使用像实体框架这样的 ORM 来调用过程,因为漏洞就在过程本身中。因此,我试图传达的是,您不能简单地使用 ORM 并认为您已经涵盖了 100% 的 SQL 注入攻击案例。以下是更多信息的链接:troyhunt.com/2012/12/stored-procedures-and-orms-wont-save.html 如果您使用的是 ORM,为什么要使用 sproc?但是,是的,我同意你的观点,如果你想手动做一些事情,那么你可能会引入漏洞 虽然我同意最近由于 ORM 的兴起,存储过程的使用已经下降,但仍有许多业务情况需要使用它们。如果没有充分的理由,那么也许您有一个数据架构师需要出于某些流程或性能原因使用它们。即使在今天,我也不知道有多少数据库中没有存储过程,因此降低它们在 SDLC 中的重要性可能会导致应用程序中的 SQL 注入缺陷。我们开发人员有时生活在一个我们“想要”的世界中,而不是业务现实的世界中。【参考方案3】:

我的答案很简单:

使用实体框架在 C# 和您的 SQL 数据库之间进行通信。这将使参数化的 SQL 字符串不易受到 SQL 注入的影响。

另外,它也很容易使用。

【讨论】:

当您不知道返回的数据会是什么样子时,这并不总是一种选择。 我不喜欢英孚。我更喜欢像 Dapper 和 Insight.Database 这样的 Light ORMS。使用 ORM 时,您可以查看/编写要执行的确切查询。 作为负面因素,它会增加很多性能开销【参考方案4】:

不应通过尝试验证您的输入来阻止 SQL 注入;相反,该输入应该在传递到数据库之前正确转义。

如何转义输入完全取决于您用于与数据库交互的技术。在大多数情况下,除非您编写裸 SQL(应尽可能避免),否则框架会自动处理它,因此您可以免费获得防弹保护。

在您确切决定您的接口技术将是什么之后,您应该进一步探索这个问题。

【讨论】:

对不起,“写裸 sql”是什么意思? @LeonidasFett:通常您通过编写User.Name = "Joe"; User.Save(); 之类的代码而不是Database.ExecuteQuery("UPDATE users SET name = 'Joe' WHERE id = 1"); 之类的代码来使用数据库。二是写裸sql。 啊好的。现在我明白了:) 所以基本上我应该通过框架而不是 sql 与数据库交互? 您在这里提倡的是 ORM - 就像实体框架一样。我自己使用它并喜欢它,但它确实有一些缺点——比如批量操作非常慢。另一种方法是使用参数化命令 - 您为变量编写带有占位符的 SQL,然后将变量传递给一个不同的方法,该方法负责为您将它们合并到查询中(事实上,它们是单独传递给 Db处理其余部分的服务器)。 See here for more info 到目前为止,现有数据库中大约有 3000 条记录,但今年将大幅增加。我将列出两个选项(EF 和参数化命令)以及每个选项的优缺点,然后让我的项目主管决定:D

以上是关于防止 SQL 注入的好方法是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 CodeIgniter 中防止 SQL 注入的最佳方法是啥 [重复]

使用 MySQLi 时如何防止 SQL 注入? [复制]

浅析php过滤html字符串,防止SQL注入的方法

这个准备好的语句可以防止 SQL 注入吗? [复制]

防止XSS注入的方法

防止sql注入的方法都有哪些