当前位置: 首页 > 科技观察

什么是SQL注入,这部漫画告诉你!

时间:2023-03-17 13:38:21 科技观察

让我们来看一个很有趣的漫画:今天我们来说说SQL注入。什么是SQL注入?SQL注入是一种非常常见的数据库攻击方式,SQL注入漏洞也是网络世界中最常见的漏洞之一。你可能听说过某位学长通过攻击学校数据库来修改成绩。这些前辈一般都是用SQL注入的方式。SQL注入实际上是恶意用户通过在表单中??填写包含SQL关键字的数据,导致数据库执行非常规代码的过程。简单的说就是数据只能做代码才能做的事情。这个问题的根源在于,对SQL数据库的操作是通过SQL语句进行的,执行代码和数据项都必须写在SQL语句中,这就导致如果我们在SQL语句中加入一些SQL语句数据项关键字(如SELECT、DROP等),这些关键字很可能在数据库写入或读取数据时被执行。说多了也没用,还是说真实的案例吧。接下来,我们首先使用SQLite创建学生档案表。SQL数据库操作示例importsqlite3#连接数据库conn=sqlite3.connect('test.db')#新建数据表conn.executescript('''DROPTABLEIFEXISTSstudents;CREATETABLEstudents(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL);''')#插入学生信息students=['Paul','Tom','Tracy','Lily']fornameinstudents:query="INSERTIINTOStudents(name)VALUES('%s')"%(name)conn.executescript(query);#Viewalready部分学生信息cursor=conn.execute("SELECTid,namefromstudents")print('IDName')forrowincursor:print('{0}{1}'.format(row[0],row[1]))conn.close()点击运行按钮会打印当前表的内容。在上面的程序中,我们创建了一个test.db数据库和一个students数据表,并在表中写入了4条学生信息。那么SQL注入呢?我们尝试插入另一段恶意数据,数据内容为卡通中的“Robert');DROPTABLEstudents;--”,看看会发生什么。SQL数据库注入实例conn=sqlite3.connect('test.db')#Insertinformationcontaininginjectioncodename="Robert');DROPTABLEStudents;--"query="INSERTIINTOstudents(name)VALUES('%s')"%(name)conn.executescript(query)#查看现有学生信息cursor=conn.execute("SELECTid,namefromstudents")print('IDName')forrowincursor:print('{0}{1}'.format(row[0],row[1]))conn.close()运行后你会发现程序没有输出任何数据内容,而是返回错误信息:找不到学生表格!为什么是这样?问题是因为我们插入的数据项中包含SQL关键字DROPTABLE,这两个关键字的意思是从数据库中清除一个表。罗伯特');before关键字使SQL执行器认为前面的命令已经结束,从而执行危险的命令DROPTABLE。也就是说,这段包含DROPTABLE关键字的数据项,使得原来简单的插入姓名信息的SQL语句。"INSERTINTOstudents(name)VALUES('Robert')"成为一个语句,其中还包含另一个命令来清除表单"INSERTIINTOStudents(name)VALUES('Robert');DROPTABLEstudents;--"并在SQL数据库执行上述操作后,学生表格已被清除,因此无法找到该表格并且所有数据项都将丢失。如何防范SQL注入问题那么,如何防范SQL注入问题呢?你可能认为注入问题是由于执行了数据项中的SQL关键字引起的。然后,只要检查数据项中是否有SQL关键字即可。是吗?确实如此。很多数据库管理系统都采用了这种看似“方便快捷”的过滤方式,但这并不是根本的解决办法。如果真的有一个美国人叫“Drop”,那“Table”呢?你总不能强迫人家改名字吧?有很多合理的防御方法。首先,尽量避免使用通用的数据库名称和数据库结构。在上面的案例,如果表单名不是students,注入代码在执行过程中会报错,不会发生数据丢失——SQL注入并不像大家想象的那么简单,它需要攻击者只有一个足够了解的人数据库的结构可以成功。因此,在构建数据库时尝试使用更复杂的结构和命名方式将大大降低被攻击成功的概率。使用正则等字符串过滤方法也是一种很好的保护措施表达式来限制数据项的格式和字符数,理论上只要在数据项中避免使用引号、分号等特殊字符,就不会发生SQL注入n在很大程度上被避免。另外就是使用各种程序文档推荐的数据库操作方法,执行数据项的查询和写入操作。比如上面的案例,如果我们稍微修改一下,先使用execute()方法保证只能执行A语句,然后可以将数据项以参数的形式从SQL执行语句中分离出来,这样就可以完全避免SQL注入的问题,如下图:SQL数据库反注入示例conn=sqlite3.connect('test.db')#Insertinformationcontaininginjectedcodeinasafemannername="Robert');DROPTABLEStudents;--"query="INSERTIINTOstudents(name)VALUES(?)"conn.execute(query,[name])#查看现有学生信息cursor=conn.execute("SELECTid,namefromstudents")print('IDName')forrowincursor:print('{0}{1}'.format(row[0],row[1]))conn.close()对于PHP,可以通过mysql_real_escape_string等方法对sql关键字进行转义,如果需要,检查数据项是否安全,防止SQL注入。当然做好数据库备份和加密敏感内容永远是最重要的。某些安全问题可能永远没有完美的解决方案。只有做好最基本的防护措施,才能在出现问题时及时补救,确保损失降到最低。