# 第四节:SQL注入
# SQL注入
所谓SQL注入,就是通过把SQL命令插入到表单中或页面请求的查询字符串中,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的。
## 场景:
比如现在数据库中有一个`front_user`表,表结构如下:
```
<pre class="calibre12">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span><span class="hljs-params">(models.Model)</span>:</span>
telephone = models.CharField(max_length=<span class="hljs-params">11</span>)
username = models.CharField(max_length=<span class="hljs-params">100</span>)
password = models.CharField(max_length=<span class="hljs-params">100</span>)
```
```
然后我们使用原生`sql`语句实现以下需求:
1. 实现一个根据用户`id`获取用户详情的视图。示例代码如下:
```
<pre class="calibre12">```
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">(request)</span>:</span>
user_id = request.GET.get(<span class="hljs-string">'user_id'</span>)
cursor = connection.cursor()
cursor.execute(<span class="hljs-string">"select id,username from front_user where id=%s"</span> % user_id)
rows = cursor.fetchall()
<span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> rows:
print(row)
<span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'success'</span>)
```
```
这样表面上看起来没有问题。但是如果用户传的`user_id`是等于`1 or 1=1`,那么以上拼接后的`sql`语句为:
```
<pre class="calibre12">```
<span class="hljs-keyword">select</span> <span class="hljs-keyword">id</span>,username <span class="hljs-keyword">from</span> front_user <span class="hljs-keyword">where</span> <span class="hljs-keyword">id</span>=<span class="hljs-params">1</span> <span class="hljs-keyword">or</span> <span class="hljs-params">1</span>=<span class="hljs-params">1</span>
```
```
以上`sql`语句的条件是`id=1 or 1=1`,只要`id=1`或者是`1=1`两个有一个成立,那么整个条件就成立。毫无疑问`1=1`是肯定成立的。因此执行完以上`sql`语句后,会将`front_user`表中所有的数据都提取出来。
2. 实现一个根据用户的`username`提取用户的视图。示例代码如下:
```
<pre class="calibre12">```
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">(request)</span>:</span>
username = request.GET.get(<span class="hljs-string">'username'</span>)
cursor = connection.cursor()
cursor.execute(<span class="hljs-string">"select id,username from front_user where username='%s'"</span> % username)
rows = cursor.fetchall()
<span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> rows:
print(row)
<span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'success'</span>)
```
```
这样表面上看起来也没有问题。但是如果用户传的`username`是`zhiliao' or '1=1`,那么以上拼接后的`sql`语句为:
```
<pre class="calibre12">```
<span class="hljs-keyword">select</span> <span class="hljs-keyword">id</span>,username <span class="hljs-keyword">from</span> front_user <span class="hljs-keyword">where</span> username=<span class="hljs-string">'zhiliao'</span> <span class="hljs-keyword">or</span> <span class="hljs-string">'1=1'</span>
```
```
以上`sql`语句的条件是`username='zhiliao'`或者是一个字符串,毫无疑问,字符串的判断是肯定成立的。因此会将`front_user`表中所有的数据都提取出来。
## sql注入防御:
以上便是`sql`注入的原理。他通过传递一些恶意的参数来破坏原有的`sql`语句以便达到自己的目的。当然`sql`注入远远没有这么简单,我们现在讲到的只是冰山一角。那么如何防御`sql`注入呢?归类起来主要有以下几点:
1. 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
2. 永远不要使用动态拼装`sql`,可以使用参数化的`sql`或者直接使用存储过程进行数据查询存取。比如:```
<pre class="calibre12">```
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">(request)</span>:</span>
user_id = <span class="hljs-string">"1 or 1=1"</span>
cursor = connection.cursor()
cursor.execute(<span class="hljs-string">"select id,username from front_user where id=%s"</span>,(user_id,))
rows = cursor.fetchall()
<span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> rows:
print(row)
<span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'success'</span>)
```
```
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
5. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装。
## 在Django中如何防御`sql`注入:
1. 使用`ORM`来做数据的增删改查。因为`ORM`使用的是参数化的形式执行`sql`语句的。
2. 如果万一要执行原生`sql`语句,那么建议不要拼接sql,而是使用参数化的形式。
- Introduction
- 第一章:学前准备
- 第一节:虚拟环境
- 第二节:准备工作
- 第三节:Django介绍
- 第四节:URL组成部分
- 第二章:URL与视图
- 第一节:第一个Django项目
- 第二节:视图与URL分发器
- 第三章:模板
- 第一节:模板介绍
- 第二节:模板变量
- 第三节:常用标签
- 第四节:常用过滤器
- 第五节:自定义过滤器
- 第七节:模版结构优化
- 第八节:加载静态文件
- 第四章:数据库
- 第一节:MySQL相关软件
- 第二节:数据库操作
- 第三节:ORM模型
- 第四节:模型常用字段
- 第五节:外键和表关系
- 第六节:增删改查操作
- 第七节:查询操作
- 第八节:QuerySet API
- 第九节:ORM模型迁移
- 第十节:ORM作业
- 第十一节:ORM作业参考答案
- 第十二节:Pycharm连接数据库
- 第五章:视图高级
- 第一节:限制请求method
- 第二节:页面重定向
- 第三节:HttpRequest对象
- 第四节:HttpResponse对象
- 第五节:生成CSV文件
- 第六节:类视图
- 第七节:错误处理
- 第六章:表单
- 第一节:表单概述
- 第二节:用表单验证数据
- 第三节:ModelForm
- 第四节:文件上传
- 第七章:cookie和session
- 第八章:上下文处理器和中间件
- 第一节:上下文处理器
- 第二节:中间件
- 第九章:安全
- 第一节:CSRF攻击
- 第二节:XSS攻击
- 第三节:点击劫持攻击
- 第四节:SQL注入
- 第十章:信号
- 第一节:什么是信号
- 第十一章:验证和授权
- 第一节:概述
- 第二节:用户对象
- 第三节:权限和分组
- 第十二章:Admin系统
- 第十三章:Django的缓存
- 第十四章:memcached
- 第十五章:Redis