该文中使用的数据库为本地测试数据库,数据内容无任何实际意义!
学习SQL注入技术,不是为了成为攻击者,而是为了理解:在这个数据即权力的时代,保护数据库的安全,就是在保护数字世界的基石。每一次的防止注入攻击,你可能保护的是某个人的隐私、某个家庭的财务安全、某个企业的商业未来。这是技术人员的职业责任,也是数字公民的基本伦理。
为了理解SQL注入的重要性,首先需要明白现代Web应用如何运作
用户浏览器 → Web服务器 → 应用程序 → 数据库
↑ ↑ ↑ ↑
界面展示 逻辑处理 业务规则 数据核心数据库处于整个架构的最底层,却是整个web应用的核心。应用程序只是数据库数据的前台展示,比如常见的个人资料,账户余额等皆来自数据库数据。
你的应用程序不是在与用户交互,而是用户提交的数据与你的数据库进行交互的过程。每一次交互都是一次数据库sql语句执行的过程,都是一次信任与危机共存的过程。
一个典型的商业应用数据库中通常包含:
用户数据层,商业数据层,系统控制层:比如用户的账号密码,交易数据,管理员的权限配置等等。
我们通常通过前端访问容易接触的都是登录过程,比如新安装一个app,或者打开一个网页需要输入用户名密码,或者注册,这些数据就需要在服务器上存储,一般存储的位置就是数据库。所以只需要完成数据库的注入攻击,即可获取所有用户的数据信息,将数据变为实际的经济价值。
常见的关系型数据库有:MySQL,Oracle,Microsoft SQL Server,PostgreSQL,SQLite等等国产数据库(TiDB,达梦,openGauss)等等
比如如下mysql数据库使用数据库工具打开之后的UI界面:

非关系型数据库:MongoDB,Redis,Cassandra等
SQL注入主要针对关系型数据库,因为其攻击原理基于SQL语言的语法,而SQL是关系型数据库的标准查询语言。关系型数据库如MySQL、Oracle、SQL Server等均使用SQL进行数据操作,因此易成为注入目标。非关系型数据库(如NoSQL)虽然也有注入风险,但其数据模型和查询方式与SQL不同,注入方法和关系型数据库存在差异。
SQL是一种声明式编程语言,这与过程式语言(如Python、Java)有本质区别,它是专门操作数据库的一种语言格式。与其他语言一样,它的执行也是具有优先级的,比如and优先级高于or。
声明式:告诉数据库"我想要什么",比如:select name from employee :查询name(名字)列的内容从表employee表中如下:得到查询结果

其不指定具体执行步骤,由数据库优化器决定如何执行。
在操作数据库的过程中,我们用的最多的基本上都是增删改查即(IDUS)
增(Insert):往数据库里添加新数据
语法规则:INSERT INTO <表名> [(<列列表>)] VALUES (<值列表>) ;
比如:

删(Delete):删除指定的数据
语法规则:DELETE FROM <表名> [WHERE <条件>];
这个较为简单,自行了解
改(Update):修改已有数据
语法规则:UPDATE <表名> SET <列名> = <表达式> [, ...] [WHERE <条件>];
比如修改上面的价格:

查(Select):查询数据库中的现有的内容
其中查询应该是用的最多的,SQL查询语句的结构遵循SELECT 列表名 FROM 表名 WHERE 条件 的基本框架
比如最简单的查询语句构成:select name from employee where id = 2,不管多复杂的查询也都遵循这样的规则
以上为直接通过数据库工具对数据库进行的操作,在用户访问web应用的时候,其对数据库中的数据访问其实也是通过sql语句进行操作的。但这漏洞是如何产生的呢?
我们通过web服务器的代码来看,比如登录过程存在漏洞的代码可能是下面这样的:
服务端post请求得到用户的username和password

然后通过拼接sql字符串(将用户post请求的数据拼接到字符串中),然后将该sql字符串当作数据库的SQL语言给mysql执行。

在这个数据库中的数据如下:

比如这个示例中的我们启动这个项目:

然后输入正常的用户名,密码得到的结果是这样的:

相当于直接在mysql中执行了SELECT * FROM users WHERE user = 'test' AND pass = 'test' 这个语句如下图:结果一致。

当然这是假设用户按照开发的预期输入的流程,但是如果用户不按照这个预期输入内容呢?
String sql = "SELECT * FROM users WHERE user = '" + username +
"' AND pass = '" + password + "'";从源码中可以看到,用户的输入会被拼接到sql中相当于是这样服务端的mysql数据库将会执行这条语句:SELECT * FROM users WHERE user = '用户输入' AND pass = '用户输入';那么如果用户手滑了输入了一个单引号'呢?

这样就会导致SQL执行的语法错误,无法正确识别这行语句而报错,通常这也是报错注入的一种判断方法。
那么要让语句正常执行怎么做,当然是需要根据SQL语言的语法规则,让其语法正确了,让其满足SQL语言的语法规则。可以看到是多了一个'单引号。那么加一个单引号就行了,比如手滑输入2个单引号,果然会执行,不会报错,但是无法查询到结果了因为不存在user=test'的内容(在SQL中2个单引号会被解释成一个单引号内容)

SQL中2个单引号会被解释成一个单引号内容

因为在SQL 中,单引号用于表示字符串字面量:字符串以单引号开始:'同时字符串以单引号结束:',如果想在字符串中包含单引号字符,需要使用两个单引号进行转义:'',所以在SQL语句中,单引号总是成对出现的
上面查询的实际是user= test' 的内容,数据库中当然没有这个数据,所以返回空未匹配到用户数据。所以SQL注入成功的关键其实就是如何闭合SQL查询语句并让其正确执行后面的语句,而后面的语句是我们可控的。
仅仅是这样肯定无法达到我们想要的目的,我们的目的是注入SQL语句,执行一些非预期的动作,并能返回有意义的或者有价值的内容。
比如这样:

输入单引号,保证语法正确,但密码未知,需要同时保证执行结果为真。所以如下:SQL语句可分成3段:第一段:select * from users where user = 'test' 。第二段:'' = '' 第三段: pass = 'asdfadf' ,当执行第一段时,会查询到test用户的内容,由于and执行的优先级比or高,所以会先执行'' = '' and pass = 'asdfadf' 得到pass = 'asdfadf' ,最后组合到一起实际执行结果是SELECT * FROM users WHERE user = 'test' OR pass = 'asdfadf',最终返回所有test用户名的信息。

这样就达到了在不知道密码的情况下,登录成功。也就是所谓的万能密码。并非必须是 1=1 为万能密码,其核心原理是保证语法正确,且执行结果为真--即可查询到数据,就是所谓的万能密码---保证SQL执行结果为真。
SQL注入学习
那么在没有靶场或者环境的情况下如何快速进行注入的练习或者学习呢。答案就是搭建一个数据库,直接通过SQL语句操作数据库,同时这样也可以在渗透测试的时候进行一些问题验证。
上面已经知道了SQL注入实际就是用户的输入内容带入了SQL语句中进行了执行,那么直接在数据库中输入用户的数据就可实现模拟注入的练习。
比如上面的实际执行的SQL语句为:SELECT * FROM users WHERE user = 'test' or ''='' AND pass = 'asdfadf'将其在数据库中直接执行:效果一样

当我们在安全检测过程中发现存在得注入漏洞,都可以通过本地的数据库进行模拟注入。当然最主要的前提是,要对SQL语言有一定了解,服务端的查询语句大概内容可能是什么样的。通常情况下注入的方法都不止一种,还是上面的例子,比如我们通过闭合引号,保证语法不出错,同样的我们可以通过注释的方式,更直接的将语句提前终止,让其正确执行,比如使用-- 的方式。

其实就是SQL语言的运用,SQL运用得越是熟练,可能注入得技巧就越多。
SQL注入的分类,有很多种方式,有的根据注入的回显的方式分为报错注入和盲注,也有根据输入类型分为字符型和数字型的,根据回显方式可分为有回显和无回显两种
这个