掘金 后端 ( ) • 2024-04-02 13:55

一、SQL注入

SQL注入攻击的目标是底层使用SQL数据库且使用不安全的方式构造数据库查询语句的网站。

1.1 什么是SQL

结构化查询语言(Structured Query Language,SQL)是一种从关系型数据库中提取数据和数据结构的语言。关系型数据库将数据存储在表中,表中的每一行都是一个数据项(例如用户或商品)。SQL语法允许Web服务器等应用程序使用INSERT语句向数据库中添加行,使用SELECT语句读取行,使用UPDATE语句更新行,使用DELETE语句删除行。

1.2 SQL注入攻击剖析

当Web服务器不安全地构造传递给数据库驱动程序的SQL语句时,就会发生SQL注入攻击。这使攻击者可以通过HTTP请求传递参数,这些参数会导致驱动程序执行预料之外的动作。 让我们看一个构造不安全的SQL语句,当用户试图登录到网站时,它从数据库中读取用户数据,如以下Java代码所示。

Connection connection = DriverManager.getConnection(DB_URL,DB_USER,DB_PASSWORD);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users WHERE email='" + email='" + email +
"'AND encrypted_password='" + password + "'";
statement.executeQuery(sql);

上例中SQL语句的构造是不安全的!这段代码使用从HTTP请求获取的email和password参数,并将他们直接插入SQL语句中。因为没有检查参数中是否有改变SQL语句含义的SQL控制字符(例如'),所以攻击者可以制作绕过网站身份验证系统的输入。 攻击者将用户电子邮件参数传递为[email protected]'--,会提前终止SQL语句并导致密码检查逻辑无法执行;示列代码如下:

statement.executeQuery("SELECT * FROM users WHERE email='[email protected]'--' AND encrypted_password='123'");

数据库驱动程序仅执行SQL语句,会忽略其后的所有内容。这种类型的SQL注入攻击,单引号(')会提前关闭email参数,而SQL注释语法(--)会诱使数据库驱动程序忽略执行密码检查语句的结尾。即该SQL语句允许攻击者以任何用户身份登录,而无须知道其密码!攻击者所需要做的就是在登录表单中将'和--字符添加到用户的电子邮箱地址中。

1.3 SQL注入攻击缓解措施

1.3.1 使用参数化语句

参数化语句即绑定参数构造SQL字符串。绑定参数是占位符,数据库驱动程序将这些占位符安全地替换为相应的输入。使用绑定参数时,会将具有特殊含义的“控制字符”(如上文的'和--)转义,为了消除潜在的注入攻击。如以下的绑定参数安全构建的SQL语句代码:

Connection connection = DriverManager.getConnection(DB_URL,DB_USER,DB_PASSWORD);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users WHERE email = ? and encrypted_password = ?";
statemennt.executeQuery(sql,email,password);

这段代码使用“ ?”作为绑定参数构造SQL查询。然后,代码将每个参数的输入值绑定到该语句,要求数据库驱动程序在安全处理任何控制字符的同时将参数值插入SQL语句。参数化语句确保数据库驱动程序将所有控制字符(如'、--、和;)视为SQL语句的输入,而不是SQL语句的一部分。

1.3.2 使用对象关系映射

许多Web服务器库和框架抽象化了代码中SQL语句的显示构造,允许使用对象关系映射访问数据对象。对象关系映射(ORM)库将数据库表中的行映射到内存中的代码对象,开发人员通常不必编写SQL语句即可读取和更新数据库。这种体系结构在大多数情况下可以抵御SQL注入攻击,但是如果使用自定义的SQL语句,仍然很容易受到攻击。因此,了解ORM在后台的工作方式非常重要。

1.3.3 使用纵深防御策略

使用绑定参数的SQL语句也是纵深防御策略的一种,同时也需采取最小特权原则,在每个级别强制实施安全控制,允许通过其他策略缓解某个级别上的故障。