查看原文
其他

不会SQL注入,连漫画都看不懂了

点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长

每天早上8点20分, 第一时间与你相约

每日英文

When you have something you really love but it causes you pain, God is just testing you to see if you are strong enough to hold it.

当你真正喜欢一样东西,但它又给你带来伤害的时候,其实这是老天在考验你是否足够坚持。


每日掏心话

距离因拥抱而拉近,天气因拥抱而温暖,呼吸而拥抱而急促,心跳因拥抱而猛烈,空气因拥抱而凝固,时间因拥抱而停止。


责编:乐乐

链接:jizhi.im/blog/post/sql_injection_intro

程序员小乐(ID:study_tech)第 631 次推文   图片来自网络


往日回顾:Java 程序员常用资源工具集合(建议收藏)



   00 前言    


先来看一副很有意思的漫画:

相信大家对于学校们糟糕的网络环境和运维手段都早有体会,在此就不多做吐槽了。今天我们来聊一聊SQL注入相关的内容。



   01 何谓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 (nameVALUES ('%s')" % (name)

conn.executescript(query)

# 检视已有的学生信息
cursor = conn.execute("
SELECT idname 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 (nameVALUES ('Robert')"

变为了同时包含另外一条清除表单命令的语句

"INSERT INTO students (nameVALUES ('Robert');DROP TABLE students;--"

而SQL数据库执行上述操作后,students表单被清除,因而表单无法找到,所有数据项丢失。


   02 如何防止SQL注入问题    


那么,如何防止SQL注入问题呢?

大家也许都想到了,注入问题都是因为执行了数据项中的SQL关键字,那么,只要检查数据项中是否存在SQL关键字不就可以了么?的确是这样,很多数据库管理系统都是采取了这种看似『方便快捷』的过滤手法,但是这并不是一种根本上的解决办法,如果有个美国人真的就叫做『Drop Table』呢?你总不能逼人家改名字吧。

合理的防护办法有很多。首先,尽量避免使用常见的数据库名和数据库结构。在上面的案例中,如果表单名字并不是students,则注入代码将会在执行过程中报错,也就不会发生数据丢失的情况——SQL注入并不像大家想象得那么简单,它需要攻击者本身对于数据库的结构有足够的了解才能成功,因而在构建数据库时尽量使用较为复杂的结构和命名方式将会极大地减少被成功攻击的概率。

使用正则表达式等字符串过滤手段限制数据项的格式、字符数目等也是一种很好的防护措施。理论上,只要避免数据项中存在引号、分号等特殊字符就能很大程度上避免SQL注入的发生。

另外,就是使用各类程序文档所推荐的数据库操作方式来执行数据项的查询与写入操作,比如在上述的案例中,如果我们稍加修改,首先使用execute()方法来保证每次执行仅能执行一条语句,然后将数据项以参数的方式与SQL执行语句分离开来,就可以完全避免SQL注入的问题,如下所示:

SQL数据库反注入示例



conn = sqlite3.connect('test.db')

# 以安全方式插入包含注入代码的信息
name = "Robert');DROP TABLE students;--"
query = "INSERT INTO students (nameVALUES (?)"

conn.execute(query, [name])

# 检视已有的学生信息
cursor = conn.execute("
SELECT idname from students")
print('IDName')
for row in cursor:
    print('{0}{1}'.format(row[0], row[1]))

conn.close()

而对于PHP而言,则可以通过mysql_real_escape_string等方法对SQL关键字进行转义,必要时审查数据项目是否安全来防治SQL注入。

当然,做好数据库的备份,同时对敏感内容进行加密永远是最重要的。某些安全性问题可能永远不会有完美的解决方案,只有我们做好最基本的防护措施,才能在发生问题的时候亡羊补牢,保证最小程度的损失。

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。


欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。

猜你还想看


阿里、腾讯、百度、华为、京东最新面试题汇集

关于如何实现一个 TCC 分布式事务框架的一点思考

中美互联网科技公司对比图!你有的,我们也有!

教你用Python将图片转化为字符画!附源代码

太牛逼了,居然有人将各大编程语言绘成了一部编年史!

如何用几个简单的命令改善你的Linux安全?

关注「程序员小乐」,收看更多精彩内容
嘿,你在看吗?

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存