SQL 注入
简介
SQL 注入 或 SQL 注入是一种入侵系统的技术,它在互联网上很有名,但可以在任何编程语言中使用。然而,在互联网上,我们有一个爆炸性的组合
- 应用程序可以被整个互联网访问,拥有成千上万的用户想要破解您的系统;
- 弱类型脚本语言与弱类型数据类型的组合使用,有助于打开一些安全漏洞。
- HTTP 协议有一些特点,如果使用不当,会使 Web 应用程序更容易受到攻击,例如使用 GET 参数。
经典的例子是应用程序用户的登录。您在屏幕上要求用户输入用户名和密码,然后用这个特性发送一个 SQL。
sql SELECT TRUE WHERE nome = 'telles' AND senha = 'abizi'
在登录界面,用户可以在密码栏中输入类似的东西
USUÁRIO: admin SENHA: ' OR 1=1 --
替换变量后,您的 SQL 将变成这样
SELECT TRUE WHERE nome = 'admin' AND senha = OR 1=1 --'
防止 SQL 注入
最小权限原则
可以看到,这里有一系列问题需要分析。任何系统首先要分析的问题是,如果用户成功攻击,他们将拥有多大的破坏力。如果连接到应用程序的用户是数据库中对象的拥有者,就像很多应用程序出于方便而喜欢做的那样,您会发现您的用户将拥有对您的对象的完全权限。使用 SQL 注入,他们可以执行 DROP、TRUNCATE、ALTER 以及 DELETE、INSERT、UPDATE 和 SELECT 等操作。
因此,必须始终严格遵守的第一条规则是:“应用程序用来连接到数据库的用户绝不应该是数据库中创建的对象的拥有者”
一些关于 SQL 注入的文章完全忽略了这个建议。有些人意外失业。因此,在每个应用程序用户都没有在数据库中创建自己的用户的情况下,应该采取特殊预防措施,这在拥有数千个用户且不受 DBA 控制的 Web 应用程序中很常见。您至少应该为每个应用程序创建 4 个用户,他们具有以下角色:
- 一个拥有数据库中创建对象的用户的用户。这个用户应该完全并且只受 DBA 控制,无论是在测试环境还是生产环境。请记住,这个用户对数据库中的这些对象拥有完全的权限。
- 一个用户或用户组,专门用于开发者,他们拥有执行其任务所需的特定权限。可以确定,在生产环境中,他们可以对所有表和序列进行 SELECT 操作,而在生产环境中,他们可以进行 SELECT、INSERT、UPDATE 和 DELETE 操作。
- 一个用户或用户组,专门用于应用程序管理员,他们将拥有应用程序中特定的权限,例如删除记录或更新表中的值。这个用户应该从与普通用户使用的可执行文件不同的可执行文件连接。用于管理目的的应用程序应该限制对本地访问的权限,例如。这个用户将拥有 DELETE、UPDATE 和 INSERT 表的权限,而普通用户没有权限。因此,这个用户绝不应该用于日常操作。
- 一个用于应用程序普通用户的用户。这个用户应该严格遵守最小权限原则。他们只应该拥有访问严格必要对象的权限。谨慎授予 UPDATE 和 DELETE 权限。如果在没有合适的 WHERE 子句的情况下执行此类操作,可能会造成灾难性的后果。这个用户将是 SQL 注入攻击的目标。如果您对这个用户的权限严格控制,就可以最大限度地减少成功攻击可能造成的损害。
使用加密、视图和函数
完成了这些,您一定已经拥有了一个更加安全的环境。我们工作的下一部分将专门处理系统中最敏感的信息。仔细识别这些对象。有些机密数据可以加密。密码绝不应该以未加密的形式存储。为此,请使用 MD5 等函数。请记住,您无法知道存储在字段中的密码是什么,只能检查密码是否与存储的密码相同。但是,如果只有特定组需要访问这些信息,您应该限制对它们的访问。当您想将访问权限限制为表的单个列或列组时,最佳方法是创建一个只包含用户需要的列的视图。允许用户访问此视图,但不要允许他们直接读取表。
有些情况下,用户需要更改或读取记录,但您不想限制用户更改表中所有记录的可能性。为此,您可以创建封装整个操作的函数。您将要更改的参数传递给函数,函数检查这些数据的有效性,在所有必要的表中进行所有更改,并返回操作是否成功,或者返回一个期望的值,就像一个 SELECT 一样。这样做的结果是,您只需要授予用户执行函数的权限,而不需要授予他们访问任何参与事务的对象的权限。这意味着用户只能执行由函数指定的特定事务,如果函数编码良好,将完全避免任何未经授权的访问的可能性。
预处理语句
第三步是使用预处理语句或“prepared statements”。这些语句允许您将要替换的值作为参数传递到任何 SQL 命令。人们避免使用此语句,因为它需要您分两个步骤执行:准备语句和执行语句。当频繁执行时,预处理语句不仅更安全,而且还具有性能优势。
美元符号引号
您可以使用的第四步是 PostgreSQL 特有的,称为“美元符号引号”。您可以使用 $$ 而不是将字符字符串放在 SQL 表达式中的单引号内,例如
SELECT TRUE FROM users WHERE name = $$$$ AND senha = $$abizi$$;
您还可以执行以下操作
SELECT TRUE FROM users WHERE name = $name$telles$name$ AND senha = $senha$abizi$senha$;
请注意,您可以将字符串放在 $$、$nome$、$senha$ 或任何其他标记之间,只要开始和结束使用相同的标记即可。在 SQL 代码中使用这种类型的表示法可能最初看起来很奇怪,但在编写函数时,它可以使您的生活更轻松,因为您不会遇到奇怪的情况,例如一组单引号或反斜杠。使用美元符号引号,您不必担心单引号。
检查数据类型
在与 SQL 注入作斗争的防御措施中,下一个屏障是检查使用的数据类型。一种常见的攻击类型是例如在 http 协议的 GET 参数中注入代码。如果您期望一个数字,请在将您的变量替换到 SQL 代码中之前确保它是一个数字。一种有效的方法是使用 'printf' 函数。几乎所有编程语言都有类似于 C 语言的 printf 命令。使用 printf 的 SQL 字符串将类似于这样
SQL = printf('SELECT TRUE FROM users WHERE nome = %s AND senha = %s', nome, senha)
这里的问题是,printf 函数将强制在 SQL 函数中使用正确的数据类型。这不是完美的解决方案,但可以避免一些问题,还可以避免隐式数据类型转换,这是数据库中许多头痛的原因。
转义字符串
防止 SQL 注入最著名的屏障是使用转义字符串的函数。如今,大多数编程语言都有函数来转义字符串中的字符,例如单引号。请注意,单引号可以是字符串的一部分,例如 "database's security"。为了使这种类型的字符串被接受,必须在 SQL 表达式中以这种方式编写它
INSERT INTO titulo VALUES ('database''s security');
请注意,有两个单引号,而不是一个双引号。这是在数据库字符串中添加单引号的唯一方法。您应该期望您的用户出于任何原因在任何字符串中输入单引号。除非您在构建 SQL 命令之前显式地从字符串中删除单引号,否则您应该始终对其进行转义。如果您的编程语言拥有特定于您的数据库的字符串转义函数,请使用它,而不是通用函数。
结论
一般来说,安全问题是由信息不全的专业人士或认为安全是多余因素,认为实施所有必要的屏障非常费力的开发者造成的。对安全的疏忽达到了很多 Web 服务器直接在用户的屏幕上显示其错误消息的程度,即使是在生产环境中也是如此。在没有实施所有上述屏障并且还将应用程序错误毫不费力地显示在屏幕上的环境中使用 SQL 注入技术绝对是毁灭性的。用户可以发现应用程序中所有表和列的名称,更改任何记录,甚至删除所有表中的所有信息。在大型应用程序中,我们经常会发现存在巨大的安全漏洞。著名的网站和大型公司经常遭受信息泄露、欺诈和数据丢失的困扰。损失总是大于在应用程序中正确实施安全措施的成本。
当您再次思考安全问题时,请记住,这是一项共同的任务。SQL 注入攻击非常容易执行,以至于人们创造了“Script kiddie”这个词来指代那些技术知识有限但使用简单有效的食谱来入侵系统的人。如果您使用的是没有采取这种预防措施的应用程序(无论是由您自己开发还是由第三方开发),即使网络、服务器和数据库采取了严格的安全措施,也是徒劳的。
版权所有 (c) 2008 由 Fábio Telles Rodriguez ([email protected] - http://savepoint.blog.br)