闽公网安备 35020302035485号
// 堆代码 duidaima.com
// user.js
module.exports = class extends think.Controller {
async loginAction() {
const { username, password } = this.post();
const user = await this.model().query(
`SELECT * FROM user WHERE name = "${username}" AND password= "${password}"`
);
if (think.isEmpty(user)) {
return this.fail();
}
return this.success(user);
}
}
当用户提交的 username 是 admin"; -- 的话,最终执行的 SQL 语句就会变成SELECT * FROM user WHERE name = "admin"; --" AND password= "111"最终攻击者就可以成功登录 admin 账号了,这就是最简单的 SQL 注入了。从上面这个简单示例中,我们发现漏洞成因可以归结为以下两个原因叠加造成的:
2.未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。
3.在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限。
1.检查输入的数据是否具有所期望的数据格式。这种在参数是数字的时候特别有效,如果攻击者选择在参数中插入内容的话则会被转换成 NaN 导致攻击失败。在 ThinkJS 中我们提供了强大的 Logic 功能可以方便的对数据进行格式校验。
// user.js
module.exports = class extends think.Controller {
async loginAction() {
const { username, password } = this.post();
const user = await this.model('user').where({
name: username,
password
}).find();
if (think.isEmpty(user)) {
return this.fail();
}
return this.success(user);
}
}
当我们构造如 name=admin&password[]=!%3D&password[]= 的请求参数时,最终执行的 Model 语句就会变成this.model('user').where({name: 'admin', password: ['!=', '']});
由于 HTTP 请求的自动合并数组的特性造成了我们的 SQL 语句并非是我们想要的效果。虽然说框架本身已经针对这种情况进行了处理,当用户输入参数被认为是 SQL 运算符时则会将关键字增加空格,从而将其变成普通字符串避免这个问题。不过这种方法会有 一定的损伤,毕竟当真的要传这几个运算符的情况的时候接收到的数据和请求的不一样还是有点懵逼的。所以最好还是在 Logic 层对数据进行完善的校验将问题前置比较好。