Web 安全漏洞之 SQL 注入

什么是 SQL 注入

鲁迅

“有人的地方就有江湖,有数据库存在的地方就可能存在 SQL 注入漏洞。”javascript

在全部漏洞类型中,SQL 注入但是说是危害最大最受你们关注的漏洞。简单说来,SQL 注入是经过在用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序时意料以外结果的攻击行为。仍是以 ThinkJS 为例,假设咱们写了以下一个接口(实际状况确定不会这么写的):php

// 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);
  }
}
复制代码

当用户提交的 usernameadmin"; -- 的话,最终执行的 SQL 语句就会变成html

SELECT * FROM user WHERE name = "admin"; --" AND password= "111"
复制代码

最终攻击者就能够成功登陆 admin 帐号了,这就是最简单的 SQL 注入了。从上面这个简单示例中,咱们发现漏洞成因能够归结为如下两个缘由叠加形成的:java

  1. 程序编写者在处理应用程序和数据库交互时,使用字符串拼接的方式构造SQL语句。
  2. 未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。

SQL注入根据攻击者获取数据的方式分为回显注入报错注入以及盲注。刚才演示的直接从返回结果中获取数据则为回显注入,固然也能够经过 MySQL 执行的报错结果中嗅探到数据库的结构和内容,这就是报错注入。盲注则是根据数据库执行的延时等操做来判断是否接近正确值,简单的说来有点像是拿着听诊器试探保险箱的密码的感受。mysql

不一样的分类原则会有不一样的分类,也有按照注入位置及方式不一样进行分类分为POST注入GET注入cookie注入盲注延时注入搜索注入base64注入等。不过你们都支持分类形式不一样,原理仍是一致的,这里就不一一细说了。git

SQL 注入的危害

若是网站存在 SQL 注入漏洞,至关于将数据库直接暴露在攻击者面前,可想而知危害会有多大了。攻击者利用 SQL 注入漏洞能实现如下攻击:github

  1. 跳过帐户权限验证达到越权
  2. 获取数据库关键信息从而进行脱库
  3. 在特别状况下还能够修改数据库内容或者插入内容到数据库,若是数据库权限分配存在问题,或者数据库自己存在缺陷,那么攻击者能够经过SQL注入漏洞直接获取webshell或者服务器系统权限。

防护方法

数据校验

从文章开头能够看到,其实漏洞的主要缘由仍是没有对用户输入的数据进行过滤,因此对来自用户的数据(GET, POST, cookie 等)最好作到如下两种过滤校验golang

  1. 检查输入的数据是否具备所指望的数据格式。这种在参数是数字的时候特别有效,若是攻击者选择在参数中插入内容的话则会被转换成 NaN 致使攻击失败。在 ThinkJS 中咱们提供了强大的 Logic 功能能够方便的对数据进行格式校验。
  2. 使用数据库特定的敏感字符转义函数把用户提交上来的非数字数据进行转义。在 ThinkJS 中封装了 escapeString() 方法能够对敏感字符进行转义,其原理则和 PHP 的 mysql_escape_string() 方法是一致的。

检查输入数据格式在 ThinkJS 中还能防止另一种非通用 SQL 安全问题。文章开头的示例代码咱们在实际的应用中通常会这么写:web

// 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 语句就会变成sql

this.model('user').where({name: 'admin', password: ['!=', '']});
复制代码

因为 HTTP 请求的自动合并数组的特性形成了咱们的 SQL 语句并不是是咱们想要的效果。虽说框架自己已经针对这种状况进行了处理,当用户输入参数被认为是 SQL 运算符时则会将关键字增长空格,从而将其变成普通字符串避免这个问题。不过这种方法会有 必定的损伤,毕竟当真的要传这几个运算符的状况的时候接收到的数据和请求的不同仍是有点懵逼的。因此最好仍是在 Logic 层对数据进行完善的校验将问题前置比较好。

除了数据校验,也能够选择使用数据库的存储过程和预约义指针等特性来抽象数库访问,使用户不能直接访问数据表和视图。但这个办法又有别的影响。

via: SQL注入

权限限制

严格限制Web应用的数据库的操做权限,给此用户提供仅仅可以知足其工做的最低权限,从而最大限度的减小注入攻击对数据库的危害。**请记住永远不要使用超级用户或全部者账号去链接数据库!**当数据库被攻击时将损伤限制在当前表的范围是比较明智的选择。经过权限限制能够防止攻击者获取数据库其它信息,甚至利用数据库执行 Shell 命令等操做。

日志处理

当数据库操做失败的时候,尽可能不要将原始错误日志返回,好比类型错误、字段不匹配等,把代码里的 SQL 语句暴露出来,以防止攻击者利用这些错误信息进行 SQL 注入。除此以外,在容许的状况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但按期审计数据库执行日志能够跟踪是否存在应用程序正常逻辑以外的 SQL 语句执行。日志自己没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。

后记

综上所说以后,你们可能以为 SQL 数据校验会比较麻烦,其实在 ThinkJS 中已经将关键字处理类的方法已经集成,使用程序提供的 ORM 方法进行 SQL 构造会比本身写 SQL 语句拼接来的更方便,同时也能提升项目代码复用,减小潜在的风险。若是对 ThinkJS 默认的 think-model 不喜欢的话,也可使用其它第三方的 ORM 框架,例如 think-sequelize

参考资料:

相关文章
相关标签/搜索