何谓SQL注入,这个漫画告诉你!
点击上方“民工哥技术之路”选择“星标”
每天10点为你分享不一样的干货
链接:
https://jizhi.im/blog/post/sql_injection_intro
先来看一副很有意思的漫画:
今天我们来聊一聊SQL注入相关的内容。
何谓SQL注入?
SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。大家也许都听过某某学长通过攻击学校数据库修改自己成绩的事情,这些学长们一般用的就是SQL注入方法。
SQL注入其实就是恶意用户通过在表单中填写包含SQL关键字的数据来使数据库执行非常规代码的过程。简单来说,就是数据「越俎代庖」做了代码才能干的事情。这个问题的来源是,SQL数据库的操作是通过SQL语句来执行的,而无论是执行代码还是数据项都必须写在SQL语句之中,这就导致如果我们在数据项中加入了某些SQL语句关键字(比如说SELECT、DROP等等),这些关键字就很可能在数据库写入或读取数据时得到执行。
多言无益,我们拿真实的案例来说话。下面我们先使用SQLite建立一个学生档案表。
SQL数据库操作示例
import sqlite3
# 连接数据库
conn = sqlite3.connect('test.db')
# 建立新的数据表
conn.executescript('''DROP TABLE IF EXISTS students;
CREATE TABLE students
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL);''')
# 插入学生信息
students = ['Paul','Tom','Tracy','Lily']
for name in students:
query = "INSERT INTO students (name) VALUES ('%s')" % (name)
conn.executescript(query);
# 检视已有的学生信息
cursor = conn.execute("SELECT id, name from students")
print('IDName')
for row in cursor:
print('{0}{1}'.format(row[0], row[1]))
conn.close()
点击运行按钮将会打印目前表中的内容。上述程序中我们建立了一个test.db数据库以及一个students数据表,并向表中写入了四条学生信息。
那么SQL注入又是怎么一回事呢?我们尝试再插入一条恶意数据,数据内容就是漫画中的"Robert');DROP TABLE students;--",看看会发生什么情况。
SQL数据库注入示例
conn = sqlite3.connect('test.db')
# 插入包含注入代码的信息
name = "Robert');DROP TABLE students;--"
query = "INSERT INTO students (name) VALUES ('%s')" % (name)
conn.executescript(query)
# 检视已有的学生信息
cursor = conn.execute("SELECT id, name from students")
print('IDName')
for row in cursor:
print('{0}{1}'.format(row[0], row[1]))
conn.close()
你将会发现,运行后,程序没有输出任何数据内容,而是返回一条错误信息:表单students无法找到!
这是为什么呢?问题就在于我们所插入的数据项中包含SQL关键字DROP TABLE,这两个关键字的意义是从数据库中清除一个表单。而关键字之前的Robert');使得SQL执行器认为上一命令已经结束,从而使得危险指令DROP TABLE得到执行。也就是说,这段包含DROP TABLE关键字的数据项使得原有的简单的插入姓名信息的SQL语句。
"INSERT INTO students (name) VALUES ('Robert')"
变为了同时包含另外一条清除表单命令的语句
"INSERT INTO students (name) VALUES ('Robert');DROP TABLE students;--"
如何防止SQL注入问题
SQL数据库反注入示例
conn = sqlite3.connect('test.db')
# 以安全方式插入包含注入代码的信息
name = "Robert');DROP TABLE students;--"
query = "INSERT INTO students (name) VALUES (?)"
conn.execute(query, [name])
# 检视已有的学生信息
cursor = conn.execute("SELECT id, name from students")
print('IDName')
for row in cursor:
print('{0}{1}'.format(row[0], row[1]))
conn.close()
关注 民工哥技术之路 微信公众号对话框回复关键字:1024 可以获取一份最新整理的技术干货:包括系统运维、数据库、redis、MogoDB、电子书、Java基础课程、Java实战项目、架构师综合教程、架构师实战项目、大数据、Docker容器、ELK Stack、机器学习、BAT面试精讲视频等。
ROW 还是 STATEMENT?线上 MySQL Binlog 怎么选?
一文速查 Git 常用命令,搞定版本控制照做就ok|新手福利
点击【阅读原文】发现更多精彩内容~~
在看的你,请点这里↓↓↓