为什么SQL注入预防机制会演变为使用参数化查询的方向?


59

在我看来,可以通过以下方式防止SQL注入攻击:

  1. 仔细筛选,过滤和编码输入(在插入SQL之前)
  2. 使用准备好的语句 /参数化查询

我想每个都有优点和缺点,但是为什么#2脱颖而出,或多或少地被认为是防止注入攻击的实际方法?是更安全,更不容易出错,还是还有其他因素?

据我了解,如果正确使用#1并注意所有警告,它可能与#2一样有效。

消毒,过滤和编码

清除过滤编码的含义上,我感到有些困惑。我要说的是,出于我的目的,以上所有选项都可以考虑作为选项1。在这种情况下,我了解到清理和过滤有可能修改或丢弃输入数据,而编码可以按原样保留数据,但可以对其进行编码适当避免注射攻击。我认为转义数据可以视为对数据进行编码的一种方式。

参数化查询与编码库

有一些答案,其中parameterized queries和的概念encoding libraries可以互换使用。如果我错了,请指正我,但我印象深刻的是它们是不同的。

我的理解是encoding libraries,无论多么出色,他们总是有潜力修改SQL“程序”,因为在发送给RDBMS之前,他们正在对SQL本身进行更改。

Parameterized queries 另一方面,将SQL程序发送到RDBMS,然后优化查询,定义查询执行计划,选择要使用的索引等,然后插入数据,作为RDBMS内部的最后一步本身。

编码库

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

参数化查询

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

历史意义

一些答案提到,从历史上看,出于性能原因创建参数化查询(PQ),并且在针对编码问题的注入攻击变得流行之前。在某些时候,很明显PQ对注入攻击也很有效。为了符合我的问题的精神,为什么在防止SQL注入攻击时PQ仍然是首选方法,为什么它比大多数其他方法蓬勃发展?


1
评论不作进一步讨论;此对话已转移至聊天
maple_shaft

23
预备语句不是 SQL注入攻击演变的结果。他们从一开始就在那里。您的问题是基于错误的前提。
user207421'9

4
如果您认为自己比坏家伙更聪明,那就去第一名
狗仔队

1
“为什么PQ仍然是首选方法”,因为它是最简单,最可靠的方法。加上PQ的上述性能优势。确实没有缺点。
Paul Draper

1
因为这是解决如何执行查询问题的正确解决方案,即使不是针对安全上下文中的SQL注入问题也是如此。需要转义并使用带内数据和命令的表单始终是设计错误,因为它们易于出错,违反直觉并且在错误使用时会严重损坏。另请参阅:shell脚本。
R.,

Answers:


147

问题是,#1要求您有效地解析和解释要使用的SQL变量的整体,以便您知道它是否在做应做的事情。并在更新数据库时使代码保持最新。您可以在任何地方接受查询输入。而且不要搞砸。

所以是的,这种事情可以阻止SQL注入攻击,但是实现起来的成本高得多。


60
@dennis-嗯,您的SQL变体中的引号是什么?“?'?”?U + 2018?\ u2018?是否有一些技巧来分隔表达式?您的子查询可以更新吗?有很多事情要考虑
。– Telastyn

7
@Dennis,每个数据库引擎都有自己的处理方式,例如转义字符串中的字符。这有很多漏洞需要解决,尤其是当应用程序需要使用多个数据库引擎或与该引擎的未来版本兼容时,这可能会更改一些可利用的次要查询语法。

12
准备好的语句的另一个好处是,当您必须重新运行具有不同值的相同查询时,可以获得性能提升。同样,准备好的语句可以知道某个值是否确实是null,字符串或数字,并据此采取行动。这对安全性非常好。即使您只运行一次查询,数据库引擎也已经为您优化了查询。如果已缓存,那就更好了!
Ismael Miguel

8
@Dennis Henry Null先生将感谢您以正确的方式进行此操作。
Mathieu Guindon

14
@Dennis的名字无关紧要。问题在于姓氏。请参阅Stack OverflowProgrammers.SEFox SportsWiredBBC以及您可以在Google快速搜索中找到的其他内容;-)
Mathieu Guindon

80

因为选项1不是解决方案。筛选和过滤意味着拒绝或删除无效输入。但是任何输入都可能有效。例如,撇号是名称“ O'Malley”中的有效字符。它只需要在SQL中使用之前正确编码即可,这是准备好的语句所要做的。


添加注释后,似乎您基本上是在问为什么使用标准库函数,而不是从头开始编写自己的功能相似的代码?您应该始终喜欢使用标准库解决方案来编写自己的代码。它工作量少,可维护性强。任何功能都是这种情况,但是特别是对于安全敏感的东西,完全没有必要自己重新发明轮子。


2
就是这样(这是另外两个答案中缺少的部分,所以+1)。给出问题的表述方式,不是要对用户输入进行清理,而是要提出一个问题:“(在插入之前)过滤输入”。如果现在的问题是关于清理输入,那么您为什么要自己做而不是让库去做(同时,顺便说一句,也失去了缓存执行计划的机会)?
Arseni Mourzenko

8
@Dennis:清理或过滤意味着删除信息。编码意味着在丢失信息的情况下转换数据的表示形式。
JacquesB

9
@Dennis:过滤意味着接受或拒绝用户输入。例如,“杰夫”将被过滤为“用户年龄”字段的输入,因为该值显然无效。如果不是通过过滤输入,而是通过例如替换单引号字符开始对其进行转换,那么您所做的工作与使用参数化查询的数据库库完全相同。在这种情况下,您的问题很简单:“当我可以在每个项目中重新发明轮子时,为什么我会使用领域内专家编写的东西?”
Arseni Mourzenko 2016年

3
@Dennis:O\'Malley正在使用斜杠将引号转义以正确插入(至少在某些数据库中)。在MS SQL或Access中,可以使用附加引号将其转义O''Malley。如果您必须自己做,则不是很便携。
AbraCadaver

5
我不能告诉你我的名字被系统彻底拒绝了多少次。有时我什至仅使用我的名字就已经看到由SQL注入引起的错误。哎呀,曾经有人要求我更改用户名,因为我实际上在后端破坏了某些东西。
亚历山大·奥玛拉

60

如果您尝试执行字符串处理,那么您实际上并不是在生成SQL查询。您正在生成可以产生SQL查询的字符串。间接级别为错误和错误打开了很大的空间。鉴于在大多数情况下我们很高兴以编程方式与某些对象进行交互,这确实有点令人惊讶。例如,如果我们有一些列表结构并想要添加项目,则通常不这样做:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

如果有人建议这样做,那么您会正确地回答它相当荒谬,并且应该这样做:

List<Integer> list = /* ... */;
list.add(5, position=2);

它在概念上与数据结构交互。它不会对如何打印或解析该结构产生任何依赖性。这些是完全正交的决定。

您的第一种方法类似于第一个示例(只是稍微差一点):您假设可以以编程方式构造将正确解析为所需查询的字符串。这取决于解析器和一堆字符串处理逻辑。

使用准备好的查询的第二种方法更类似于第二种示例。当您使用准备好的查询时,实际上是在解析一个合法但包含一些占位符的伪查询,然后使用API​​正确替换其中的某些值。您不再涉及解析过程,也不必担心任何字符串处理。

通常,在概念上与事物交互更容易,并且更不容易出错。查询不是字符串,查询是解析字符串或以编程方式构造一个字符串(或任何其他使您创建的方法)时得到的结果。

在执行简单文本替换的C样式宏与执行任意代码生成的Lisp样式宏之间有一个很好的类比。使用C样式的宏,您可以替换源代码中的文本,这意味着您可以引入语法错误或误导性行为。使用Lisp宏,您将以编译器对其进行处理的形式生成代码(也就是说,您将返回编译器处理的实际数据结构,而不是阅读器在编译器可以访问之前必须处理的文本)。 。但是,使用Lisp宏,您将无法生成某些解析错误。例如,您无法生成(让((ab)a

即使使用Lisp宏,您仍然仍然可以生成错误的代码,因为您不一定要了解应该存在的结构。例如,在Lisp中, (let((ab))a)意味着“建立变量a与变量b的值的新词法绑定,然后返回a的值”,并且(let(ab)a)意味着“建立变量a和b的新词法绑定,并将它们都初始化为nil,然后返回a的值。” 这些在语法上都是正确的,但是它们含义不同。为避免此问题,您可以使用更多具有语义意识的函数并执行以下操作:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

有了这样的东西,就不可能返回语法上无效的东西,而要返回意外地不是您想要的东西则要困难得多


很好的解释!
Mike Partridge

2
您因“很好的类比”而迷失了我,但我根据前面的解释赞成。:)
通配符

1
很好的例子!-您可以添加:根据数据类型,有时甚至无法创建可行的字符串。-如果我的参数之一是包含故事草稿(〜10.000个字符)的自由文本字段怎么办?或如果一个参数是JPG图片怎么办?-唯一的办法则是参数化查询
法尔科

实际上不-关于准备好的语句为何演变为SQL注入的防御,这是一个非常糟糕的描述。特别是给定的代码示例是用Java编写的,它不在参数化查询可能在C / C ++被认为是最新技术的时间范围内发展的时候。SQL数据库在1970-1980年的早期开始使用。流行于高级语言之前。哎呀,我想说其中许多人使使用数据库变得更容易(任何人都可以使用PowerBuilder?)
TomTom

实际上,@ TomTom我同意您的大部分内容。在这里,我仅隐式地涉及安全方面。在SO上,我回答了很多SPARQL(RDF查询语言,与SQL有一些相似之处)的问题,很多人遇到了问题,因为它们将字符串连接在一起而不是使用参数化查询。即使没有注入攻击,参数化查询也有助于避免错误/崩溃,即使不是注入攻击,错误/崩溃也可能是安全问题。所以我要说的越来越少:即使SQL注入不是问题,参数化查询也很好,而且它们很好...
Joshua Taylor

21

这通常有助于将选项#2视为最佳实践,因为数据库可以缓存查询的非参数版本。参数查询比SQL注入问题早几年(我相信),碰巧您可以用一块石头杀死两只鸟。


10
自从SQL首次发明以来,SQL注入一直是一个问题。后来没有成为问题。
Servy 2013年

9
@Servy理论上是。实际上,只有当我们的输入机制上线时,这才成为真正的问题,这为任何人提供了巨大的攻击面。
Jan Doggen

8
小鲍比表会不同意您需要互联网或庞大的用户群来利用SQL注入。当然,网络要早于 SQL,因此,一旦SQL出现,您就不必等待网络了。是的,安全漏洞,当应用程序有一个小的用户基础脆弱,但他们仍然安全漏洞,而人们利用他们在数据库本身具有宝贵的数据(以及许多非常早期的数据库有非常有价值的数据,因为只有人与有价值的数据库可以负担得起的技术)
。–

5
@Servy据我所知,动态SQL是一个相对较晚的功能。最初使用SQL时,大多是使用值的参数(输入和输出)进行预编译/预处理的,因此查询中的参数可能早于软件中的SQL注入(可能不在ad / hoc / CLI查询中)。
Mark Rotteveel

6
他们可能早于 SQL注入的了解。
user253751 '16

20

简单地说:他们没有。您的声明:

为什么SQL注入预防机制演变为使用参数化查询的方向?

从根本上来说是有缺陷的。参数化查询的存在时间比至少众所周知的SQL Injection长。它们通常是为避免LOB(业务线)应用程序具有的常规“搜索形式”功能中的字符串集中而开发的。许多(很多)年后,有人发现所说的字符串操作存在安全问题。

我记得25年前做过SQL(当时互联网尚未广泛使用-它才刚刚开始),我还记得做过SQL与IBM DB5 IIRC版本5的比较-并且已经对查询进行了参数化。


谢谢。为什么需要避免字符串串联?在我看来,这将是一个有用的功能。有人有问题吗?
丹尼斯

3
实际上是两个。首先,它并不总是那么琐碎-为什么在不需要时处理内存分配等问题。但是第二,在远古时代,缓存sql数据库方面的性能并不十分出色-编译SQL非常昂贵。作为使用一条sql预准备语句(这是参数的来源)的副作用,执行计划可以重用。SQL Server引入了自动参数化(即使没有参数也可以重用查询计划-它们被扣除和暗示)我认为是2000还是2007-在IIRC之间。
TomTom公司

2
使用参数化查询不会消除进行字符串连接的能力。您可以进行字符串连接以生成参数化查询。仅仅因为一项功能有用并不意味着它始终是解决特定问题的好选择。
JimmyJames

是的,但是正如我说的那样-到他们发明之时,动态SQL带来了相当不错的性能;我说过2000年到2007年之间的某个时间点-相当长)。在那个旧时间,如果您多次运行sql,您真的想要PREPARED语句;)
TomTom

计划高速缓存动态SQL,其实是添加到SQL Server 7.0,在1998年 - sqlmag.com/database-performance-tuning/...
麦克Dimmick

13

除了所有其他好的答案:

#2之所以更好是因为它将代码中的数据与您的代码分开。在#1中,您的数据是代码的一部分,这就是所有不良情况的源头。使用#1,您可以获取查询,并且需要执行其他步骤以确保查询将数据理解为数据,而在#2中,您可以获取代码,代码和数据为数据。


3
分离代码和数据还意味着数据库供应商将编写并测试针对恶意代码注入的防御措施。因此,如果将作为参数传递的某些内容与无害的查询一起最终破坏或破坏了您的数据库,则数据库公司的声誉将持续上升,您的组织甚至可能会起诉他们并获胜。这也意味着,如果该代码包含可利用的漏洞,则很有可能是其他人的站点而不是您自己的站点,所有的站点都散乱了。(只是不要忽略安全错误修复!)
nigel222 '16

11

除了提供SQL注入防御之外,参数化查询通常还具有另一个优势,即仅编译一次,然后使用不同的参数多次执行。

从SQL数据库的角度来看select * from employees where last_name = 'Smith'select * from employees where last_name = 'Fisher'它们是截然不同的,因此需要单独的解析,编译和优化。它们还将在专用于存储已编译语句的存储区中占用单独的插槽。在具有大量具有不同参数的类似查询的重负载系统中,计算和内存开销可能很大。

随后,使用参数化查询通常会提供主要的性能优势。


我认为这是理论(基于用于参数化查询的已准备语句)。在实践中,我怀疑这确实是经常发生的情况,因为大多数实现只会在一个调用中进行prepare-bind-execute,因此对每个参数化查询都使用不同的Prepared语句,除非您采取明确的步骤实际准备语句(和库)级别prepare通常与实际的SQL级别完全不同prepare)。
jcaron

以下查询也与SQL解析器不同:SELECT * FROM employees WHERE last_name IN (?, ?)SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?)
Damian Yerrick '16

是的,他们有。这就是为什么MS于1998年向SQL Server 7添加了查询计划缓存的原因。如:您的信息已有很长时间了。
TomTom

1
@TomTom-查询计划缓存与您似乎在暗示的自动参数化不同。如上,在发布之前先阅读。
mustaccio

@mustaccio实际上至少MS确实同时引入了两者。
TomTom

5

等等,为什么呢?

选项1意味着您必须为每种类型的输入编写清理例程,而选项2则更不会出错,并且编写/测试/维护的代码也更少。

几乎可以肯定,“照顾所有警告”可能比您想象的要复杂,并且您的语言(例如Java PreparedStatement)比您想像的要复杂得多。

预准备的语句或参数化的查询已在数据库服务器中预编译,因此,在设置参数时,由于查询不再是SQL字符串,因此不会执行SQL串联。另一个好处是,RDBMS会缓存查询,并且即使参数值变化,后续调用也被视为相同的SQL,而对于串联SQL,每次以不同的值运行查询时,查询都是不同的,并且RDBMS必须解析它,再次创建执行计划,等等。


1
JDBC不清除任何内容。协议具有参数的特定部分,而DB根本不解释该参数。这就是为什么可以从参数设置表名称的原因。
talex

1
为什么?如果未解析或解释参数,则没有理由转义某些内容。
talex

11
我认为您对参数化查询的工作方式有错误的认识。不仅仅是参数稍后被替换的情况,它们永远不会被替换。DBMS将任何查询转变为一个“计划”,它将执行一组步骤以获取结果。在参数化查询中,该计划就像一个函数:它具有许多在执行时需要提供的变量。在提供变量时,SQL字符串已被完全忘记,并且仅使用提供的值执行了计划。
IMSoP 16/09/13

2
@IMSoP那是我的误解。尽管我认为这是一个常见的问题,您可以在stackoverflow.com/questions/3271249/…中对该问题的两个投票最多的答案中看到。我读到它,你是对的。我编辑了答案。
图兰斯·科尔多瓦

3
@TomTom这对性能很有帮助,但对安全性却无济于事。在编译并缓存了受损的动态SQL时,程序已被更改。从非动态参数化SQL创建计划,然后传递数据元素与从DBMS抽象出作为完整SQL字符串呈现给它的两个查询之间的相似性,从根本上还是有所不同。
IMSoP

1

让我们想象一下理想的“消毒,过滤和编码”方法会是什么样子。

在特定应用程序的上下文中进行清理和过滤可能很有意义,但最终它们都归结为说“您不能将这些数据放入数据库”。对于您的应用程序来说,这可能是个好主意,但是您不建议将其作为一般解决方案,因为有些应用程序需要能够在数据库中存储任意字符。

这样就剩下编码了。您可以通过添加转义字符来对字符串进行编码的函数开始,以便可以自己替换它们。由于不同的数据库需要转义不同的字符(在某些数据库中,两者均是,\'并且''对于而言是有效的转义序列',而在其他数据库中则无效),因此此功能需要由数据库供应商提供。

但并非所有变量都是字符串。有时您需要用整数或日期代替。它们与字符串的表示方式不同,因此您需要不同的编码方法(同样,这些方法必须特定于数据库供应商),并且需要以不同的方式将它们替换为查询。

因此,如果数据库也为您处理替换操作,则事情可能会更容易-它已经知道查询期望的类型,如何安全地编码数据以及如何安全地将它们替换到查询中,因此您不必担心它在您的代码中。

至此,我们刚刚重新发明了参数化查询。

一旦对查询进行了参数设置,它便开辟了新的机会,例如性能优化和简化的监视。

编码很难做到正确,而正确完成编码与参数设置是无法区分的。

如果您真的很喜欢字符串插值作为构建查询的一种方式,那么有两种语言(可以想到Scala和ES2015)具有可插入的字符串插值,因此 一些 可让您编写类似于字符串插值的参数化查询,但是从SQL注入是安全的-因此在ES2015语法中:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
“编码很难正确”-哈哈哈。它不是。一两天都记录在案。多年前,我为ORM编写了一个编码器(因为sql server对参数有限制,因此在一个语句中插入5000-10000行是有问题的(返回15年前)。我不记得这是一个大问题。
TomTom

1
也许SQL Server有足够的规则性,这不是问题,但是我在其他数据库中遇到了问题-字符编码不匹配,配置选项晦涩,特定于区域设置的日期和数字的特殊情况。所有这些都可以解决,但至少需要对DB的怪癖有一个粗略的了解(我正在看着您,MySQL和Oracle)。
James_pic

3
一旦考虑到时间因素,@ TomTom编码实际上很难正确设置。当数据库供应商决定在下一个版本中创建新的注释样式时,或者在升级过程中空词成为新的关键字时,您会怎么做?从理论上讲,您实际上可以为RDBMS的一个发行版获得正确的编码,而在下一个修订版中则是错误的。您甚至不使用将供应商转换为使用非标准语法即可有条件注释的
Eric

@Eric,这真是令人恐惧。(我使用Postgres;如果有任何此类怪异的疣,我还没有遇到过。)
通配符

0

在选项1中,您正在使用size = infinity的输入集,试图将其映射到非常大的输出尺寸。在选项2中,您已将输入限制为您选择的任何内容。换一种说法:

  1. 仔细筛选和过滤[ 无穷大 ] [ 所有安全SQL查询 ]
  2. 使用[ 限于您的范围的预先考虑的方案 ]

根据其他答案,通过将范围限制从无穷大到可管理的范围,似乎还具有一些性能优势。


0

SQL(尤其是现代方言)的一种有用的思维模型是,每个SQL语句或查询都是一个程序。在本机二进制可执行程序中,最危险的安全漏洞是溢出,攻击者可以用不同的指令覆盖或修改程序代码。

SQL注入漏洞与C这样的语言中的缓冲区溢出是同构的。历史表明,缓冲区溢出很难防止-即使受到公开审查的极其关键的代码也经常包含此类漏洞。

解决溢出漏洞的现代方法的一个重要方面是使用硬件和操作系统机制将内存的特定部分标记为不可执行,并将内存的其他部分标记为只读。(例如,请参阅Wikipedia上有关可执行空间保护的文章。)这样,即使攻击者可以修改数据,攻击者也无法将其注入的数据视为代码。

因此,如果SQL注入漏洞等效于缓冲区溢出,那么SQL等效于NX位或只读内存页又是什么?答案是:prepared语句,其中包括参数化查询以及用于非查询请求的类似机制。准备好的语句编译时带有某些标记为只读的部分,因此攻击者无法更改程序的那些部分,而其他部分则标记为不可执行的数据(准备好的语句的参数),攻击者可以将数据注入到其中,但是永远不会被视为程序代码,从而消除了大多数滥用可能性。

当然,清理用户输入是好的,但是要真正确保安全,您需要保持偏执(或者等效地,像攻击者一样思考)。程序文本外部的控制界面是执行此操作的方法,并且准备好的语句为SQL提供了该控制界面。因此,毫不奇怪的是,准备好的语句以及由此进行的参数化查询是绝大多数安全专业人员推荐的方法。


一切都很好,但它并没有完全按照标题解决问题。
TomTom

1
@TomTom:你什么意思?问题恰恰在于为什么参数化查询是防止SQL注入的首选机制;为什么?我的回答解释了为什么参数化查询比清理用户输入更安全,更可靠。
Daniel Pryden

抱歉,我的问题是“为什么SQL注入预防机制演变为使用参数化查询的方向?”。他们没有。这不是关于现在,而是关于历史。
TomTom

0

我已经在这里写过:https : //stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

但是,只是为了简单起见:

参数化查询的工作方式是,将sqlQuery作为查询发送,并且数据库确切知道该查询将执行的操作,然后才将用户名和密码仅作为值插入。这意味着它们无法影响查询,因为数据库已经知道查询将执行的操作。因此,在这种情况下,它将查找用户名“ Nobody OR 1 = 1'-”和一个空密码,该密码应该为false。

不过,这并不是一个完整的解决方案,因为仍然可以将javascript放入数据库中,因此不会影响其他问题(例如XSS攻击),因此仍然需要进行输入验证。然后,如果将其读出到页面上,则将根据任何输出验证将其显示为普通javascript。因此,实际上最好的方法仍然是使用输入验证,但是使用参数化查询或存储过程来停止任何SQL攻击


0

我从未使用过SQL。但是显然您会听到人们所遇到的问题,并且SQL开发人员在这种“ SQL注入”方面遇到了问题。很长一段时间我都不知道。然后我意识到,人们通过连接字符串来创建SQL语句,真实的文本SQL源语句,其中一些是用户输入的。我对这种认识的第一个念头是震惊。完全震惊。我想:任何人都怎么会如此愚蠢地以这种编程语言创建语句?对于C或C ++或Java或Swift开发人员而言,这简直是疯狂。

就是说,编写一个以C字符串作为参数并生成一个看起来与C源代码中的字符串文字完全相同的字符串不同的C函数并不是很困难。例如,该函数会将abc转换为“ abc”,将“ abc”转换为“ \” abc \“”,将“ \” abc \“”转换为“ \” \\“ abc \\” \“”。(好吧,如果这对您来说不对,那就是html。当我键入它时是正确的,但是当它显示时不是。)一旦编写了C函数,就很难在其中生成C源代码了用户提供的输入字段中的文本将转换为C字符串文字。确保安全并不难。为什么SQL开发人员不会使用该方法来避免SQL注入,这超出了我的范围。

“消毒”是完全有缺陷的方法。致命的缺陷是它使某些用户输入非法。您最终得到一个数据库,在该数据库中,通用文本字段不能包含类似;的文本。删除表或在SQL注入中使用的任何会造成损坏的表。我发现这是完全不能接受的。如果数据库存储文本,则它应该能够存储任何文本。而且实际的缺陷是消毒剂似乎无法正确处理:-(

当然,参数化查询是使用编译语言的任何程序员所期望的。它使工作变得更加轻松:您输入了一些字符串,甚至不必费心将其转换为SQL字符串,而只是将其作为参数传递,该字符串中的任何字符都不会造成任何损坏。

因此,从使用编译语言的开发人员的角度来看,清理是我永远不会发生的事情。消毒的需要是疯狂的。参数化查询是该问题的明显解决方案。

(我发现Josip的答案很有趣。他基本上说,通过参数化查询,您可以阻止对SQL的任何攻击,但是随后您可以在数据库中包含用于创建JavaScript注入的文本:-(嗯,我们又遇到了同样的问题,我不知道Javascript是否可以解决此问题。


-2

主要问题是,黑客找到了解决卫生问题的方法,而参数化查询是一个现有过程,可以完美地发挥性能和内存的额外好处。

有人将问题简化为“只是单引号和双引号”,但是黑客发现了避免检测的明智方法,例如使用不同的编码或利用数据库功能。

无论如何,您只需要忘记一个字符串就可以创建灾难性的数据泄露。黑客能够将脚本自动化以下载带有一系列查询的完整数据库。如果该软件像开源套件或著名的商务套件那样广为人知,那么您可以简单地访问用户和密码表。

另一方面,仅使用串联查询只是学习使用和习惯它的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.