浅谈SQL注入及其防御方法

昨晚跟学生们在群里讨论到什么是SQL注入的时候, 硬挤出来了一个比喻. 码字不易, 特整理记录如下. 

 

首先, 电脑里面的语言分两种, 编译型, 解析型(脚本型). 比如PHP就是解析型, C就是编译型.

由于SQL语句可以在这两类语言下执行, 所以为了充分明白是什么导致了SQL注入漏洞, 我们先来讨论一个问题: 什么是SQL注入漏洞的通用表现形式, 它的本质是什么?

 

SQL注入通常就是在一个查询语句的最后, 比如"Select * from users where id = '$id' " 中的id部分, 通过一些特殊的语法, 修改原本这个SQL查询语句的原意, 比如通过输入框让id等于: ' or 1=1

从而把所有的用户信息列出.

 

这个攻击方法就好比, 我们在进行故事接龙, 我说:"你来到一个古堡门前, 颤颤巍巍地打开了一个木门, 突然, 里面冲出来了", 本来恐怖氛围渲染得好好的, 接龙的非要搞破坏, 硬是要接"我的妈妈, 原来她在里面当NPC, 瞬间就扭着我耳朵带我回家了". 整个氛围瞬间就毁了. 

如果再推而广之, 讨论下什么叫漏洞, 我会说是程序的运行超出了程序员最开始设想的道路.当然, 超出预期的运行结果有很多, 这里讲的SQL注入主要是说一个命令原本已经定下了大半, 但因为恶意用户的恶意输入导致命令原意被变化.

如果这个命令原本仅仅只是缺一个合法的宾语. 动词,  用户的输入有限, 那么不会发生注入漏洞. 如果语言非常灵活, 可以通过一些特殊符号扭转整个命令的原意, 注入漏洞就出现了.

有同学提问, 机器是怎么知道命令的原意呢? 我的答案是, 机器在拿到具体指令前根本不知道. 所以, 如果程序员原本设定好一个命令, 但在运行之前有给人篡改的机会, 那就会造成漏洞.

 

C语言这种编译型语言, 程序员写好程序之后, 马上编译成机器码, 这样用户想要改变命令, 就只有改变机器码.

对于SQL语句而言, 我们看到的很多情况下, 整个指令都是以字符串存在的, 如果中间有参数等待扩展, 比如上面的例子, 那么很可能会出现漏洞.

 

防御思路:

既然知道了这类攻击的大致方法就是通过"关联词"或者说"特殊符号"来修改句子的原因, 那么最简单的防御思路就是把这些特殊符号统统屏蔽, 一旦遇到直接报错.

或者再简单点, 不让用户自由输入, 而给他们一个选择列表. (当然这个列表实现得不好也可能会被攻击, 这是另外的问题.)

 

但还有一个思路, 既然这些关联词/特殊符号只属于原始命令的语法, 那我在执行这个命令之前, 把它翻译成另一种新语言, 这种新语言无法处理原语言的语法, 那么这些特殊符号就失效了. 比如, #在SQL里面经常用来做注释用途, 但在机器码里面不会说遇到#的ascii码就注释掉后面的内容. 因此, 如果把上面这个SQL命令预编译成机器码, 然后在需要id的部分弄成一个指针. 这样无论进来什么特殊符号, 都会只当作是一个普通符号来处理.

 

总结: 所谓SQL注入漏洞, 在我看来就是看输入的内容是否会改变机器拿到的"命令"原意. 要防御, 要么屏蔽造成句子含义变化的特殊符号, 要么就直接把原来的命令翻译成别的语言, 从而让这些特殊符号失效.

posted @ 2023-07-30 11:33  lynnzixing  阅读(95)  评论(0编辑  收藏  举报