一、关于SQL注入
sql注入是目前web应用中一种常见的攻击方式,通过恶意构造参数生成不可预期的sql语句,来完成不可告人的秘密。危害极大!它的影响主要有以下两点:
第一:拖库,拖库的意思是直接把整个数据表甚至库中的数据都拖出来了。当今的互联网环境中,数据毫无疑问在任何公司都是最宝贵的财富,一旦数据泄露,轻者造成经济损失,重者可能造成法律责任。
第二:删库,拖库的危害可能只是和他人共享了劳动成果,而删库就不同了,数据被共享了不说,还把数据都删了。这就是典型的——走别人的路,让别人无路可走!
近期闹得沸沸扬扬的“微盟删库”事件,因为运维人员把数据库删了,导致业务接近一周都没有恢复,股价直接下跌10+亿。可见“删库”的危害实在太大!
sql注入示例
数据表user_info
保存了用户相关的信息,库中包含了三条数据:
ysql> select * from user_info;
+----+--------+-----+
| id | name | age |
+----+--------+-----+
| 1 | maqian | 25 |
| 2 | nginx | 10 |
| 3 | redis | 15 |
+----+--------+-----+
3 rows in set (0.00 sec)
后台服务中接口get_info
可以通过用户的id来获取用户信息,部分代码如下:
func handler(w http.ResponseWriter, r *http.Request) {
// ...
id := r.FormValue("id")
sqlClause := fmt.Sprintf("select name, age from user_info where id = %s", id)
// ...
rows, err := db.Query(sqlClause)
// ...
data, _ := json.marshal(rs)
w.write(data)
}
代码的逻辑是:先通过参数中的id构造出sql语句,然后到数据库中查询数据,最后再返回json到前端。
例如请求返回id等于1的用户信息:
可以看到是能正常获取到用户信息的,符合功能预期。但是如果把id的值改成1 or 1
,此时构造出来的sql语句就是:
select name, age from user_info where id = 1 or 1
执行就会把库中所有用户的信息都打印出来,这就完成了一次拖库:
更有甚者,如果把id改成1; drop table user_info;
,构造出来的sql语句就是:
select name, age from user_info where id = 1; drop table user_info;
查询完数据之后直接调用了drop表了,成功删库!
二、避免sql注入
2.1 安全检查
安全检查要求程序员在写代码时不要相信任何外部输入,所有来自外部输入的数据都进行关键字检查,过滤掉可能存在注入的字符。对不符合要求的数据直接不处理。
这也是常用的方式,但是不很稳妥,因为攻击的方式太多,各种奇怪的组合都有可能,很容易忽略掉一些场景。
2.2 参数化查询
参数化查询是数据库层面提供的避免sql注入的方式,是目前最有效的避免sql注入的方法。它通过提前预编译sql语句,所有的参数通过占位符(@/?
等,mysql是?
,mssql是@
)来表示,避免执行输入的参数。
例如,在golang中,执行sql语句之前可以先通过prepare
来编译语句,然后再执行语句:
stmt, err := db.Prepare(sqlClause)
if err != nil {
return nil, err
}
defer stmt.Close()
rs, err := stmt.Exec(val...)
// ...
此处评论已关闭