常见Web安全漏洞(01):注入的原理与防御措施
公司里项目刚刚上线不久,有同事提出要做好安全防御工作,随即写下了这篇关于注入漏洞的简要指南。顺便发在此处以飨读者。
注入是什么
跳出网络安全领域用俗话说,注入就是“打针”。打针是把药液注射到生物体内的办法,然后让药液与生物体内的各种细胞发生反应,最终治愈生物体的某种疾病。还有一种操作也是利用打针(最初为获得健康而发明的途径),但就带来的是彻底负面的效果,那就是注射毒品。
网络安全术语中,注入是指利用软件中为正常目的预留的数据传递途径,把恶意代码或恶意数据送入程序体中去,对软件产生负面影响的操作。也就是骇客利用注入方法给正常程序注射“毒品”,给程序造成负面影响。轻则服务器宕机,重则隐私与敏感数据泄露,乃至整个公司关张大吉。
注入的种类
按被注入的软件体划分,常见的有:
SQL 数据库注入(MySQL\MS SQL Server\MariaDB\PostgreSQL等)
NoSQL 数据库注入(MongoDB\Cassandra\Redis\ElasticSearch等)
OS 注入(Linux\Windows)
按传递数据的中间框架,常见的有:
ORM 注入
LDAP 注入
Struts 注入(Java 某编程框架)
按数据携带者分,常见的有:
报文注入
HTTP 头部注入
SMTP 头部注入
参数注入
URL 参数注入
表单参数注入
Cookie 注入
XML 实体注入
SOAP 注入
按传递的数据作用划分,常见的有:
表达式注入(EL:Expression Language)
OGNL 注入
SPEL 注入
MVEL 注入
JSTL 注入
OS命令注入(shell注入)
按构造数据的手法/形态划分,常见的有:
永真逻辑注入
特殊字符注入(换行符、注释符、转义符等)
大小写变种注入
空字节注入
字符转换编码注入
还有其他杂七杂八的叫法,比如显示服务器软件内的版本和系统信息的,叫回显注入;连续两次注入才能得到一次有效反馈的,叫二阶注入等等……
上面的分类大家看看便可,不要死记硬背,背住了也没有什么卵用。
而我还写下它们的目的是,目前全网相关资料中(我检索到的),还没有一篇是如此有条理的给注入分类。纯技术意义而言,可以帮我们按图索骥式开拓注入思路;非技术意义在于借此是想给大家一个系统化思考问题和梳理信息的学习小样本。当然我的分类也不尽完善,仅仅抛砖引玉,欢迎大家建立自己的注入知识结构。
注入的本质模型
从本质上讲,不论什么软件,它们的功能都是处理输入的数据或执行输入的命令。这些数据、命令,都是符合该软件的设计,满足编程语言(可以是通用编程语言,也可以是如SQL、HTML或你自定义的领域特定语言(DSL))的设计规范。要执行这些数据/命令,必然是先解解释/解析它们,由相应的解释器/解析器完成。
正是如此,非常多的注入漏洞实际上都属于表达式注入漏洞,因为表达式是现代编程语言中非常基本的结构。程序员在设计代码之初,期待某处的表达式求值结果为X,而骇客要做的就是构造特定的输入让该处表达式的求值结果是Y。
骇客正是利用处理程序(解释器/解析器)的解析和执行规则,让解释后被执行的并不是产品程序员设计的逻辑。
注入的简单示例
SQL 注入示例
某程序员在项目中写下了如下构造 SQL 查询语句的字符串拼接代码,再正常不过。该代码从 HTTP 的 URL 请求参数中获取 uid
变量的值,并将改值拼接到 SQL 语句字符串中,然后将 SQL 语句交给 MySQL 引擎执行。
query = "SELECT * FROM accounts WHERE uid = '" + request.args.get("uid") + "'"resuts = mysql.execute(query)
骇客利用这点,构造出如下的 URL 然后发出请求(注意不正常的参数):http://example.com/accounts?uid=' or '1'='1
。
当程序中接收到 uid
的参数是 ' or '1'='1
之后,SQL 语句的拼接结果为:
SELECT * FROM accounts WHERE uid = '' or '1'='1'
SQL 引擎执行的等价语句是:
SELECT * FROM accounts WHERE FALSE or TRUE
WHERE
子句后面的表达式求值结果永远为真,条件成立,于是 accounts
表中的数据都会被返回,在骇客面前一览无遗。
OS 注入示例
OS注入是指操作系统层面的 CLI 交互语言的注入,例如 Linux 下的 Shell 程序。不少项目中可能会用 Popen 模块、CMD 模块、Shell 模块直接执行操作系统命令,而该命令来自于用户输入。
假设某 Python 的自动化运维项目中包含如下代码:
cmd = request.args.get("cmd")
os.system(cmd)
若用户恶意构造输入参数,令 cmd = “rm -rf /“ ,如果该 Python 进程的权限恰好又具备 root 权限,很明显造成的后果是轻则服务器完蛋,重则公司要关张大吉。
如何发掘注入漏洞
工欲善其事必先利其器,Web安全领域经过长足发展,已经有不少好用的用来发掘注入漏洞的工具。比如下面所列:
OWASP ZAP
Burp Suite
SQLmap
Wapiti
……
上述个别软件主要集中在SQL注入发掘,而某些则包含SQL注入、端口与目录扫描、XSS扫描等多种功能。关于注入漏洞的发掘核心原理都是构造足够多的恶意输入样例,然后针对潜在的注入点(可接收用户输入的接口)进行扫描,也就是把构造的非法参数/数据向这些接口提交,看接口返回的数据是否可被利用。
在上述常用安全工具的注入扫描模块里,已经内置了很多安全人员编写的恶意用例,可以开箱即用地做一些基本扫描。但若要针对性的挖掘注入漏洞,需要更深入的了解被攻击服务的特点,例如用了哪些数据库,用的哪种编程语言,用的什么框架,运行在什么操作系统上等等。
进而,需要了解这些数据库引擎、Web开发框架、操作系统的特点。如查询语言或程序表达式的解析规则,发现其中可利用的点(如是否可以用语句的结束符来截断程序的正常逻辑),然后针对性地构造“恶意输入”样本,若发现恶意输入组合可以很多,那么写一段程序生成所有可能的恶意组合是省时省力的,再将这些恶意输入样本导入扫描工具拿去做批量扫描。
注入漏洞防御措施
从前文的注入本质模型中我们可以看出,应用研发团队可做的防御措施无非三点:
不允许恶意输入进入程序;
若第1步之后有漏网之鱼,不允许它按入侵者逻辑执行;
若第2步之后仍有漏网的,则不允许引发严重后果。
但凡可以达成上述三个目的的防御措施,都算是有效措施,可以开发并集成到软件中,使软件体“内置安全”,而不是“事后补救”。
不允许恶意输入进入程序的措施
养成不信任外部输入的习惯,培养安全意识;
前端提供鼠标点选控件,而不是文本输入框,避免用户无意输入非法参数;
前端对所有 input 类型或同性质的控件进行输入检查,不合法参数不允许提交至后端;
后端 API 层之前,再增加安全校验层,过滤非法参数;
后端封装数据库 SDK 或 ORM 框架,业务逻辑中不直接拼写查询语句或操作系统命令,在接收参数的模块/函数的最外层增加参数安全检查机制;
利用各类开发框架已经提供的加入了安全考虑因素的参数化构造方法,而不是自己用字符串拼凑参数;
备制黑白名单机制,定义某类字符、某种规则/特征的数据作为输入一定是非法的,定义某类数据/字符/拼写/格式才被允许的列入白名单,根据黑白名单机制提前过滤;
让恶意输入不能按入侵者逻辑执行的措施
权限层面让越权的恶意语句/命令无执行机会。遵循最小权限控制原则,让要执行该语句的程序/数据库用户,不要具备不应该有的权限,例如某个文件夹或数据表,只用于查询用途的,就不要给写入权限,更不要给 root 级别的权限;现代数据库引擎和操作系统,还支持更细粒度的权限控制,例如某个命令该用户/进程是否可执行,必要时在这个级别也需要控制;
程序中尽量杜绝使用危险的编程接口,例如 exec()函数、eval()函数,需慎重校验与调用 delete,remove 等操作;
语句/命令/查询参数在传递给解释器之前,应该按解释器支持的特定转义语法对输入数据先进行转义;
不允许引发严重负面后果的措施
数据库/数据表做合理的拆分,隐私、敏感、半敏感、可公开等数据放在不同的表、不同的库中,分别赋予不同权限的操作角色,
优化业务模型和逻辑,尽量减少数据库操作入口,
遵守好单一职责原则,某个数据库操作逻辑应该只完成该业务直接相关的数据操作,不要再一个借口内完成一连串的数据库链表查询、业务串联查询等等操作,
以上三条是避免城门失火殃及池鱼的做法;
利用好数据库的 limit 等功能,避免一次查询就泄露大量数据;
API 层面做访问频率限制,超过阈值的判定为非法用户,拒绝为它服务;
如果系统出现异常或报错,对外返回的额数据应当简略化、代号化,不对外暴露细节。
终极操作
所有开发人员养成良好的安全意识,将网络安全的危机意识根植于日常开发任务中;
代码上线前做好 Code Review,依据本文相关指南进行代码审查,必要时可将代码风格审查、代码安全审查、代码效率审查、代码业务逻辑审查分开执行,分别着重解决各个方面;
代码上线前做好仿真的攻击测试,多站在骇客角度尽力“搞垮”系统以发现问题。
结语
本文开篇以注射毒品做类比,说明了网络安全领域中所谓的注入攻击是怎么回事;紧接着梳理了常见的注入类型,以便大家可以二次扩展,按图索骥地寻找注入可能性;然后我们给出了注入的最本质模型,拿SQL注入和OS注入做了简要说明;然后借助注入模型推演了注入漏洞挖掘方法和工具以及注入漏洞防御措施。
希望大家通过本文不仅理解了注入漏洞,且在实践中去培养自我安全意识,去提高项目的安全性,我也希望通过 “注入类型梳理”、“注入本质模型”、“注入防御措施” 这几节内容给大家分享我的一种分析和解决问题的逻辑思维方法。
另外,本提到的注入漏洞挖掘工具我就不再贴说明书了,网上可搜到非常多的相关资料;还有大多同学不太了解的 MongoDB 注入、Elasticsearch 注入等,其实与 MySQL 注入如出一辙,不通点仅仅在于我们需要去学习 MongoDB 和 ES 的查询语言,了解它们的解析和执行规则,去发现其中可能的利用点。本号也就不在此处教大家如何用各类数据库或技术框架了,大家应该去翻阅对应的官方文档。
最后,如果你是公司里的开发人员、架构师、项目经理、测试人员、安全人员,请聊聊你们曾经是怎么做的?接下来你会做什么?项目的安全性会发生什么改观呢?欢迎大家与我分享自己的经验,让我也多多学习,谢谢!
*END*
这里是 驹说码事,分享程序猿的码路历程
感谢您的关注