joh*_*ose 113 c# sql sql-injection sqlcommand parameterized-query
在SQL注入方面,我完全理解参数化string参数的必要性; 这是本书中最古老的技巧之一.但什么时候可以证明不参数化SqlCommand?是否认为任何数据类型不安全参数化?
例如:我不认为自己在SQL专家附近,但我不能想到任何可能容易受到SQL注入接受a bool或an int并且只是将其连接到查询中的情况.
我的假设是正确的,还是可能在我的程序中留下一个巨大的安全漏洞?
为了澄清,这个问题被标记为c#,这是一种强类型语言; 当我说"参数"时,想一想 public int Query(int id).
Rob*_*Rob 101
我觉得从技术上来说这是安全的,但这是一个很糟糕的习惯.你真的想写这样的查询吗?
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive +
" AND FirstName = @firstName");
sqlCommand.Parameters.AddWithValue("firstName", "Rob");
Run Code Online (Sandbox Code Playgroud)
在类型从整数变为字符串的情况下,它也会使您容易受到攻击(Think员工编号,尽管名称可能包含字母).
因此,我们已将EmployeeNumber的类型更改int为string,但忘记更新我们的SQL查询.哎呀.
Joe*_*orn 65
当你使用控制(如Web服务器)的计算机上的强类型的平台,可以防止代码注入的查询只bool,DateTime或int(及其他数字)值.令人担忧的是由于强制sql server重新编译每个查询而导致的性能问题,并阻止它获得有关以什么频率运行查询的良好统计信息(这会损害缓存管理).
但是"在您控制的计算机上"部分很重要,因为否则用户可以更改系统用于从这些值生成字符串以包含任意文本的行为.
我也想长远思考.当今天的老式强类型代码库通过自动转换移植到新的热门动态语言时会发生什么,你突然失去了类型检查,但是还没有针对动态代码进行所有正确的单元测试?
实际上,没有充分的理由不对这些值使用查询参数.这是解决这个问题的正确方法.当它们确实是常量时,继续将值硬编码到sql字符串中,但是,为什么不使用参数呢?这不像是很难.
最终,我不会把它称为一个bug本身,但我会称之为一种气味:这个东西本身就是一个错误,但是有一个强烈的迹象表明错误在附近,或者最终会出现.好的代码避免留下气味,任何好的静态分析工具都会标记这一点.
我会补充说,不幸的是,这不是你可以直接赢得的那种争论.这听起来像是一种"正确"不再足够的情况,踩着你的同事脚趾来解决这个问题并不能促进良好的团队动力; 它最终可能会伤害到更多的伤害.在这种情况下,更好的方法可能是促进使用静态分析工具.这将为旨在和返回并修复现有代码的努力提供合法性和可信度.
Mac*_*iek 53
在某些情况下,可以使用非字符串值以外的非参数化(连接)变量执行SQL注入攻击 - 请参阅Jon的这篇文章:http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables - 文化/.
事实是,当ToString调用时,一些自定义文化提供程序可以将非字符串参数转换为其字符串表示形式,这会将一些SQL注入到查询中.
Kas*_*ols 51
这是不是即使对于非字符串类型安全.始终使用参数.期.
请考虑以下代码示例:
var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");
Run Code Online (Sandbox Code Playgroud)
乍一看代码看起来很安全,但如果您在Windows区域设置中进行了一些更改并以短日期格式添加注入,则一切都会更改:
现在生成的命令文本如下所示:
SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'
Run Code Online (Sandbox Code Playgroud)
对于int类型可以做同样的事情,因为用户可以定义自定义负号,可以很容易地将其更改为SQL注入.
有人可能会争辩说应该使用不变文化而不是当前的文化,但我已经看过很多次这样的字符串连接,并且在使用对象连接字符串时很容易错过+.
Rez*_*aei 23
"SELECT*FROM Table1 WHERE Id ="+ intVariable.ToString()
安全
没关系.
攻击者无法在你输入的int变量中注入任何东西.
表现
不太好.
最好使用参数,因此查询将被编译一次并缓存以供下次使用.下次即使使用不同的参数值,查询也会被缓存,不需要在数据库服务器中编译.
编码风格
糟糕的做法.
"SELECT*FROM Product WHERE Id ="+ TextBox1.Text
虽然这不是你的问题,但对未来的读者可能有用:
安全
灾难!
即使Id字段是整数,您的查询也可能受SQL注入.假设您的应用程序中有查询"SELECT * FROM Table1 WHERE Id=" + TextBox1.Text,攻击者可以插入文本框1; DELETE Table1,查询将是:
"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"
Run Code Online (Sandbox Code Playgroud)
如果您不想在此处使用参数化查询,则应使用类型化值:
string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))
Run Code Online (Sandbox Code Playgroud)
你的问题
我的问题出现了,因为一位同事写了一堆连接整数值的查询,我想知道是否浪费我的时间来解决所有这些问题.
我认为改变这些代码不是浪费时间.确实推荐变化!
如果您的同事使用int变量,它没有安全风险,但我认为更改这些代码不是浪费时间,建议更改这些代码.它使代码更易读,更易于维护,并使执行速度更快.
You*_*nse 18
实际上有两个问题在一个.标题中的问题与OP在之后的评论中表达的担忧几乎没有关系.
虽然我认识到OP对于他们的特殊情况很重要,对于来自谷歌的读者来说,重要的是要回答更一般的问题,这可以被称为"如果我确定的话,连接就像准备好的陈述一样安全我连接的每个文字都是安全的吗?" 所以,我想专注于后者.答案是
解释并不像大多数读者那样直接,但我会尽我所能.
我一直在思考这个问题,导致文章(虽然基于PHP环境)我试图总结一切.在我看来,保护SQL注入的问题往往是针对一些相关但较窄的主题,如字符串转义,类型转换等.虽然有些措施可以被认为是安全的,但是没有系统,也没有简单的规则可循.这使得它非常光滑,过分关注开发人员的注意力和经验.
SQL注入的问题不能简化为某个特定语法问题.它比一般的开发人员想象的要广泛.这也是一个方法论问题.它不仅是"我们必须应用哪种特定格式",而且" 它必须如何完成".
(从这个角度来看,Jon Skeet在另一个答案中引用的一篇文章表现得相当糟糕而不是好,因为它再次挑剔一些边缘案例,专注于特定的语法问题而且未能解决整个问题.)
当你试图解决保护问题时,不是整体而是作为一组不同的语法问题,你面临着许多问题.
与那些混乱不同,准备好的陈述确实是圣杯:
(进一步思考,我发现当前的占位符集不足以满足现实生活需求,并且必须扩展,包括复杂的数据结构,如数组,甚至SQL关键字或标识符,有时必须添加到动态查询,但开发人员没有武装这种情况,并被迫回退到字符串连接,但这是另一个问题的问题).
有趣的是,这个问题的争议是由Stack Overflow非常有争议的本质引起的.该网站的想法是利用来自用户的特定问题,这些用户直接要求实现具有适合来自搜索的用户的通用答案数据库的目标.这个想法本身并不坏,但它在这样的情况下失败了:当用户提出一个非常狭隘的问题时,特别是在与同事的争议中争论(或者决定是否值得重构代码).虽然大多数有经验的参与者都在努力写出答案,但请牢记使命 Stack Overflow整体而言,使得他们的答案尽可能多的读者,而不仅仅是OP.
mco*_*tle 15
我们不仅要考虑安全性或类型安全的考虑因素.
使用参数化查询的原因是为了提高数据库级别的性能.从数据库的角度来看,参数化查询是SQL缓冲区中的一个查询(使用Oracle的术语,尽管我想所有数据库在内部都有类似的概念).因此,数据库可以在内存中保存一定量的查询,准备好并准备执行.这些查询不需要解析,而且更快.经常运行的查询通常位于缓冲区中,每次使用时都不需要解析.
除非
有人不使用参数化查询.在这种情况下,缓冲区通过几乎相同的查询流不断刷新,每个查询需要由数据库引擎解析和运行,并且性能全面受损,因为即使频繁运行的查询最终也会被重新解析多次天.我已经调整了数据库以维持生计,这已成为最低端水果的最大来源之一.
现在
要回答您的问题,如果您的查询具有少量不同的数值,则可能不会导致问题,实际上可能无限制地提高性能.但是,如果有可能有数百个值并且查询被大量调用,那么您将影响系统的性能,所以不要这样做.
是的,你可以增加SQL缓冲区,但它总是以牺牲其他更重要的内存使用为代价,比如缓存索引或数据.道德,非常虔诚地使用参数化查询,这样您就可以优化数据库并使用更多的服务器内存来处理重要的事情......
要向Maciek添加一些信息,请回答:
通过反射调用程序集的主函数,可以很容易地改变.NET第三方应用程序的文化信息:
using System;
using System.Globalization;
using System.Reflection;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Assembly asm = Assembly.LoadFile(@"C:\BobbysApp.exe");
MethodInfo mi = asm.GetType("Test").GetMethod("Main");
mi.Invoke(null, null);
Console.ReadLine();
}
static Program()
{
InstallBobbyTablesCulture();
}
static void InstallBobbyTablesCulture()
{
CultureInfo bobby = (CultureInfo)CultureInfo.InvariantCulture.Clone();
bobby.DateTimeFormat.ShortDatePattern = @"yyyy-MM-dd'' OR ' '=''";
bobby.DateTimeFormat.LongTimePattern = "";
bobby.NumberFormat.NegativeSign = "1 OR 1=1 OR 1=";
Thread.CurrentThread.CurrentCulture = bobby;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这仅在BobbysApp的Main函数是公共的时才有效.如果Main不公开,可能还有其他公共功能可能会调用.
小智 7
在我看来,如果你可以保证你使用的参数永远不会包含一个字符串,那么它是安全的,但我不会在任何情况下都这样做.此外,由于您正在执行连接,您将看到性能略有下降.我会问你的问题是你为什么不想使用参数?
| 归档时间: |
|
| 查看次数: |
9160 次 |
| 最近记录: |