##9.4 避免SQL注入 
##什么是SQL注入
SQL注入攻击（SQL Injection），简称注入攻击，它是Web开发中最常见的一种安全漏洞。SQL注入漏洞可以用来从数据库获取敏感信息，或者利用数据库的特性执行添加用户，导出文件等一系列恶意操作，甚至有可能获取数据库乃至系统最高权限。

而造成SQL注入的原因是由于程序没有过滤用户的输入，攻击者通过向服务器提交恶意的SQL查询语句，应用程序接收后错误的将攻击者的输入作为原始SQL查询语句的一部分执行，导致改变了程序原始的SQL查询逻辑，额外的执行了攻击者构造的SQL查询语句。
##SQL注入实例
很多 web 开发者没有注意到 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可能是这样的：

	username:=r.Form.Get("username")
	password:=r.Form.Get("password")
	sql:="SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'" 

如果用户的输入用户名如下所示，密码任意

	myuser' or 'foo' = 'foo' --

那么我们的SQL变成了如下所示：

	SELECT * FROM user WHERE username='myuser' or 'foo'=='foo' --'' AND password='xxx'
	
在SQL里面`--`是注释标记，所以查询语句会在此中断。这就允许了一个攻击者在不知道任何合法用户名和密码的情况下登录。

对于MSSQL还有更加危险的一种SQL注入，就是控制系统，下面这个可怕的例子将会演示如何在某些数据库上执行系统命令。

	sql:="SELECT * FROM products WHERE name LIKE '%"+prod+"%'"
	Db.Exec(sql)

如果攻击提交`a%' exec master..xp_cmdshell 'net user test testpass /ADD' --`作为变量 prod的值，那么sql将会变成	

	sql:="SELECT * FROM products WHERE name LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--%'"
	
MSSQL服务器会执行这条SQL语句，包括它后面那个用于向系统添加用户的命令。如果这个程序是以sa运行而 MSSQLSERVER服务又有足够的权限的话，攻击者就可以获得一个系统帐号来访问主机了。
	
>虽然以上的例子是针对某一特定的数据库系统的，但是这并不代表不能对其它数据库系统实施类似的攻击。使用不同的方法，各种数据库都有可能遭殃。

	
##如何预防SQL注入
也许你会说攻击者要知道数据库结构的信息才能实施SQL注入攻击。确实如此，但没人能保证攻击者一定拿不到这些信息，一但他们拿到了，数据库就存在泄露的危险。如果你在用开放源代码的软件包来访问数据库，比如论坛程序，攻击者就很容易得到相关的代码。如果这些代码设计不良的话，风险就更大了。目前Discuz、phpwind、phpcms这些流行的框架都有被SQL注入攻击的先例。

这些攻击总是建立在安全意识不强的代码上的。所以，永远不要信任外界输入的数据，特别是来自于客户端的，包括选择框、表单隐藏域和 cookie。就如上面的第一个例子那样，就算是正常的查询也有可能造成灾难。

SQL注入攻击的危害这么大，那么该如何来防治呢?下面这些建议或许对你编写Web应用防治SQL注入有一定的帮助。

- 1、严格限制Web应用的数据库的操作权限，给此用户提供仅仅能够满足其工作的权限，从而最大限度的减少注入攻击对数据库的危害。
- 2、检查输入的数据是否具有所期望的数据格式，严格限制变量的类型，例如使用regexp包进行一些匹配处理，或者使用strconv包里面的字符串转化成相应格式的数据进行判断。
- 3、对进入数据库的特殊字符（'"\尖括号&*;等）进行转义处理，或编码转换。Go里面`text/template`里面的`HTMLEscapeString`函数进行转义处理。
- 3、所有的查询语句都使用数据库提供的参数化查询接口，参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中。例如`database/sql`里面的查询的话首先执行`Prepare`然后执行`Query`，更新或者插入的话`Exec(query string, args ...interface{})`
- 4、在应用发布之前建议使用一些专业的SQL注入检测工具进行检测，及时修补这些SQL注入漏洞。例如sqlmap、SQLninja等SQL注入检测工具，网上应该有很多这方面的开源工具。
- 5、避免网站打印出来SQL错误信息，比如类型错误、字段不匹配等，防止攻击者利用这些错误信息进行一些判断。

##总结
SQL注入是目前Web应用开发中存在的比较严重的漏洞，通过上面的示例我们也知道了他的危害性是非常严重的，所以对于我们平常编写的Web应用，应该对于每一个小细节都要非常重视，细节决定命运，生活如此，编写Web应用也是这样。

## links
   * [目录](<preface.md>)
   * 上一节: [避免XSS攻击](<9.3.md>)
   * 下一节: [存储密码](<9.5.md>)

## LastModified 
   * $Id$