我们需要谈谈 SQL 注入。它是什么,如何去做,最重要的是如何预防它。今天介绍一些 SQL 注入示例,解释SQL注入的原理,并解释如何识别漏洞以便保护我们的数据。
什么是 SQL 注入
SQL 注入是一种漏洞,它允许恶意用户以意想不到的方式访问数据库。
当我们允许将用户输入直接传递到数据库时,通常会创建此漏洞。当攻击者识别出这一点时,他们能够制作包含在数据库上运行的 SQL 命令的输入。他们基本上可以访问读取或操作你的整个数据库。稍后我们将回顾示例,
假设一个想法是这样的
如果你的网站上有输入,例如从数据库返回记录的搜索框。攻击者可以输入由数据库读取的字符串以返回匹配结果。
SQL 注入攻击可以暴露什么?
当攻击者识别出注入漏洞时,他们能够将该字符串中的 SQL 指令传递给数据库。然后数据库将运行用户提供的任何 SQL 命令不是很好。此漏洞的潜在影响是巨大的。它可以产生任何结果,从让用户读取数据库中每个表的每一行,到能够编写INSERT或UPDATE命令修改甚至可能删除数据库。
黑客从数据库中删除了数据,并通过 SQL 注入窃取和发布了数百万人的个人信息。
如你所看见的,SQL 注入是一个极其危险的安全漏洞,即使是一些最大的公司仍然会不定期暴露此漏洞。大家有兴趣自己去百度搜寻一下
SQL注入如何工作?
SQL攻击基于恶意用户将 SQL 指令传递到你的数据库。有很多方法可以做到这一点,你用于与数据库通信的任何代码行其实都存在被注入的潜在威胁。
让我们设置一个场景。假设你的网站销售一种产品,你使用类别来过滤用户可以看到的内容。
https://jikesiji.com/products?category=books
你的服务器获取 URL 并解析类别查询字符串以确定如何为你过滤结果。
它将产生一个 SQL 语句,如下所示:
SELECT * FROM products WHERE category = 'books'
但是,如果恶意用户足够精明,意识到你正在根据该 URL 查询数据库怎么办?
他们可能会将 URL 更改为这样的内容......
https://jikesiji.com/products?category=books'+OR+1=1--
然后你的服务器可能会将该字符串传递给数据库,从而产生这样的查询......
SELECT * FROM products WHERE category = 'books' OR 1=1--'
他们所做的是通过在输入中传递结束单引号来提前终止字符串。这允许他们向他们的命令添加额外的 SQL。在这种情况下,他们添加了一个OR 1=1表示他们现在将看到所有产品而不考虑类别的产品,因为1=1对于表中的所有行都是如此。然后他们附加最后一个--注释,这样当你的服务器添加结束单引号时,它不会抛出任何错误。
这是一个非常惯性的示例,但展示了针对其他人的数据库运行原始 SQL 的最简单方法。现在想象一下,如果精明的攻击者不是OR操作员,而是添加了一个UNION并附加了其他表。他们甚至可以通过这种方式返回用户电子邮件、密码和其他敏感数据。
此外,这只是 URL,但任何允许用户输入的界面都是潜在的攻击媒介。你站点上的输入字段、任何文本框、表单……任何与数据库交互的内容都会产生潜在的漏洞。
因此,实施 SQL 注入攻击比你想象的要容易。
二阶 SQL 注入
另一种形式的 SQL 注入可以归类为二阶 SQL 注入。在上面的示例中,我们能够提交 SQL 指令并立即从数据库返回结果。二阶攻击的工作方式略有不同,并且更难检测。
二阶攻击的目标不是将输入传递到数据库,而是保留恶意 SQL 命令以供将来使用。
有例子吗?
让我们来看一个例子。假设我想在网站上更改我的电子邮件。在电子邮件字段中,我可能会输入我知道会存储在数据库中并稍后检索的恶意数据。
hello@example.com';update users set password='password'--
你能猜出这会做什么吗?
如果该网站曾经将我的电子邮件直接传递给数据库,它将包含我的 SQL 指令......(然后每个人的密码都将设置为 password)。
这种攻击策略很危险,因为恶意输入可能会在执行预期攻击之前长时间处于休眠状态。
如何识别 SQL 注入漏洞
当你充分了解 SQL 注入的工作原理后,我建议你尝试识别你网站上的漏洞。
你可以通过测试你的网站并尝试注入无害的 SQL 来做到这一点。
你还应该检查你的代码以确保你没有将字符串直接传递到你的数据库。我建议你查看用于开发网站的语言和框架的库。大多数框架都有有用的工具,可以在将它们提升到生产环境之前检测这些类型的漏洞。
测试你的网站是否存在漏洞
确定 SQL 注入漏洞的可靠方法是测试你的站点是否存在这些漏洞。以上一节中的一些示例为例。观察你的服务器如何响应。你可能会惊讶地发现执行 SQL 注入攻击比你想象的要容易。不要因为你不能攻击你自己的系统就忽视这个漏洞。除了传递用户输入之外,还有许多执行 SQL 注入的方法。
如上所述,无法立即检测到二阶 SQL 注入。很难检测用户是否正在存储可以在以后执行的查询。仔细检查你的代码防止 SQL 注入的最佳方法是学习和理解 SQL 注入是如何发生的。当你了解它是如何工作的以及是什么使它成为可能时,你将更好地了解你可能在代码中的哪些地方暴露机会。看看你的代码。你与数据库交互的任何地方。并确保你没有将任何类型的用户输入或用户生成的内容(记住,二阶注入)直接传递到你的数据库。
如果这个字符串包含恶意 SQL 指令会发生什么?
安全总比后悔好,或者在这种情况下是安全的而不是暴露整个数据库。
如何防止 SQL 注入
防止 SQL 注入实际上非常容易。需要的是输入验证和清理。这些是清理用户输入以防止将有害字符串传递到数据库的技术。3 大网站攻击(SQL 注入、跨站点脚本和远程文件包含)都来自缺乏输入清理。幸运的是,在大多数主要语言和框架中清理输入并不难
如何净化用户输入
这个想法是,你获取用户提交的字符串,然后转义任何可能导致问题的字符。当你正确使用 Active Record ORM 时,Ruby on Rails 会自动为你处理这个问题。但是如果你没有正确使用 Active Record,或者如果你直接在代码中编写 SQL,你仍然可以打开你的数据库漏洞。
一个很好的例子,说明不小心暴露你的数据是多么容易......
# Don't do this!
User.where("id = #{params[:id]}")
如果将恶意输入传递到 中params[:id],则可以将 SQL 注入到此数据库调用中。
字符串插值通常是一种非常糟糕的模式,不适合在数据库调用中使用。在这种情况下,使用 Active Record 的更好方法是将字符串作为附加参数传递。然后 Active Record 会为你清理它。
# Better example
User.where("id = ?", params[:id])
该where()方法清理作为附加参数传递的字符串,但不会清理第一个参数的 SQL 命令。避免在 Active Record 中编写 SQL使用 Active Record 的真正正确方法是完全避免传递原始 SQL 字符串。如果你使用没有字符串插值的 Active Record 方法,它总是会自动清理你的输入。
# Safe example
User.where(id: params[:id])
# or even better
User.find(params[:id])
这时我们需要使用 “活动记录”不支持的运算符编写查询。例如LIKE运算符或比较日期时。请记住检查你可能引入的 SQL 注入漏洞。
如果你好奇的话,这个堆栈溢出对 Rails 中的输入清理有很好的深入解释。Python、PHP 和大多数其他流行语言也支持净化用户输入。
因此,正如你所见,保护数据库并不难,实际上非常简单。 真正的挑战是如何防止有人忘记清理和防止恶意用户输入的事件。好难……真的好难。即使是最大、最先进的技术公司仍在为此苦苦挣扎。