SQL 和 OS 命令注入攻击
一、什么是注入攻击
注入攻击是 Web 安全领域中一种最为常见的攻击方式。
XSS 本质上也是一种针对 HTML 的注入攻击。
注入攻击的本质,是把用户输入的数据当做代码执行。
这里有两个关键条件,第一个是用户能够控制输入;第二个是原本程序要执行的代码,拼接了用户输入的数据。
二、SQL 注入
1、什么是 SQL 注入
SQL 注入,是将恶意的 SQL 查询或添加语句插入到应用的输入参数中,然后在后台 SQL 服务器上解析执行的攻击,是一种常见的 Web 安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。
2、SQL 注入攻击的原理
一次 SQL 注入的过程:
- 获取用户请求参数;
- 拼接到代码当中;
- SQL 语句按照构造参数的语义执行成功。
SQL注入的必备条件:
- 可以控制输入的数据;
- 服务器要执行的代码拼接了控制的数据。
SQL 注入流程中与正常请求服务器类似,只是黑客控制了数据,构造了 SQL 查询,而正常的请求不会 SQL 查询这一步。
SQL 注入的本质:数据和代码未分离,即数据当做了代码来执行。
举个万能钥匙的例子:

<form action="/login" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="登陆" /></p>
</form>
后端的 SQL 语句可能是如下这样的:
let querySQL = `
SELECT *
FROM user
WHERE username='${username}'
AND psw='${password}'
`;
// 接下来就是执行 sql 语句...
这是常见的登录页面,但如果有一个恶意攻击者输入的用户名是 admin' --,密码随意输入,就可以直接登入系统,这就是 SQL 注入。
原来预想的 SQL 语句是:
SELECT * FROM user WHERE username='admin' AND psw='password'
但攻击者用奇怪用户名将 SQL 语句变成了如下形式:
SELECT * FROM user WHERE username='admin' --' AND psw='xxxx'
在 SQL 中 ' -- 是闭合和注释的意思,-- 是注释后面的内容的意思,所以查询语句就变成了:
SELECT * FROM user WHERE username='admin'
所谓的万能钥匙密码本质上就是 SQL 注入的一种利用方式。
3、SQL 注入攻击的危害
- 获取数据库信息
- 管理员后台用户名和密码
- 获取其他数据库敏感信息:用户名、密码、手机号码、身份证、银行卡信息...
- 整个数据库:脱裤
- 获取服务器权限
- 植入 Webshell,获取服务器后门
- 读取服务器敏感文件
4、如何防御 SQL 注入攻击
严格限制 Web 应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害;
后端代码检查输入的数据是否符合预期,严格限制变量的类型,例如使用正则表达式进行一些匹配处理;
对进入数据库的特殊字符(
'"\<>&*;等)进行转义处理或编码转换。基本上所有的后端语言都有对字符串进行转义处理的方法,比如 lodash 的 lodash._escapehtmlchar 库,或 mysql2 的 escape,点击查看转译实现代码所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,即不要直接拼接 SQL 语句。例如 Node.js 中的 mysqljs 库的 query 方法中的
?占位参数。
三、OS 命令注入
1、OS 命令注入的定义
OS 命令注入和 SQL 注入差不多,只不过 SQL 注入是针对数据库的,而 OS 命令注入是针对操作系统的。
OS 命令注入攻击指通过 Web 应用,执行非法的操作系统命令达到攻击的目的。只要在能调用 Shell 函数的地方就有存在被攻击的风险。倘若调用 Shell 时存在疏漏,就可以执行插入的非法命令。
命令注入攻击可以向 Shell 发送命令,让 Windows 或 Linux 操作系统的命令行启动程序。也就是说,通过命令注入攻击可执行操作系统上安装着的各种程序。
2、OS 命令注入的原理
黑客构造命令提交给 Web 应用程序, Web 应用程序提取黑客构造的命令,拼接到被执行的命令中,因黑客注入的命令打破了原有命令结构,导致 Web 应用执行了额外的命令,最后 Web 应用程序将执行的结果输出到响应页面中。
这里通过一个例子来说明其原理,假如需要实现一个需求:用户提交一些内容到服务器,然后在服务器执行一些系统命令去返回一个结果给用户。
// 以 Node.js 为例,假如在接口中需要从 github 下载用户指定的 repo
const exec = require('mz/child_process').exec;
let params = {/* 用户输入的参数 */ };
exec(`git clone ${params.repo} /some/path`);
如果 params.repo 传入的是 https://github.com/admin/admin.github.io.git 确实能从指定的 git repo 上下载到想要的代码。
但如果 params.repo 传入的是 https://github.com/xx/xx.git && rm -rf /* && 恰好你的服务是用 root 权限起的就糟糕了。
3、如何防御 OS 注入攻击
- 后端对前端提交内容进行规则限制(比如正则表达式)
- 在调用系统命令前对所有传入参数进行命令行参数转义过滤;
- 不要直接拼接命令语句,借助一些工具做拼接、转义预处理,例如 Node.js 的 shell-escape npm 包。