表达式注入下载地址

极客

我踩过的坑和摸爬滚打出来的经验,全给你扒一扒!

说实话,第一次听到“表达式注入下载地址”这个词的时候,我整个人是懵的,啥玩意儿?表达式还能跟下载地址扯上关系?你是不是也跟我当初一样,脑子里一堆问号?别急,今天我就把自己这段时间踩坑、翻车、再爬起来的过程,原原本本跟你唠一唠,保证你看完之后,对表达式注入下载地址这事儿能有个明明白白的认识。

表达式注入下载地址

先说说我是怎么撞上这档子事的

那会儿我在做一个内部工具,需要根据用户传进来的参数动态生成一个下载链接,你知道的嘛,懒人写法,直接拼接字符串,图省事,结果有一天测试小哥跑过来跟我说:“你这链接咋还能执行命令呢?”我当时第一反应是——不可能吧?我写的又不是啥高危接口,结果人家甩给我一个构造好的参数,下载地址直接给我跳出个系统命令执行的结果……那一刻,我整个人是麻的。

后来才搞明白,这就是典型的表达式注入下载地址问题,简单说,就是程序在处理下载地址的时候,把用户可控的输入当成表达式给“算”了一遍,而不是老老实实当成一个普通字符串,你说吓人不吓人?本来只是想让人下个文件,结果人家能顺着这个口子干别的事儿。

表达式注入下载地址到底是怎么“中招”的?

我后来复盘了一下,问题基本都出在这几个地方:

第一种,模板引擎滥用。 有些框架的模板语法支持表达式求值,、 这种,开发的时候图方便,把下载地址直接塞进模板里渲染,用户输入没做任何转义,好家伙,人家传个 ${7*7} 进来,页面直接显示 49,你说这要是换成更狠的表达式呢?表达式注入下载地址的口子就这么开了。

第二种,URL 参数解析太“聪明”。 有些库在解析下载地址的 query 参数时,会自动做类型转换甚至表达式计算,你以为传的是 file=abc,结果人家传 file=1+1,后端给你算出个 2 来,这种“贴心”功能,在表达式注入下载地址场景下简直就是定时炸弹。

第三种,日志或者回调地址拼接。 这个最隐蔽,下载完成后要回调通知吧?回调地址里带参数吧?拼接的时候要是用了支持表达式的拼接方式,攻击者就能通过构造下载地址里的参数,把恶意表达式带进去,等你触发回调,boom,炸了。

我是怎么一步步把这个洞堵上的

说真的,修复的过程比发现还折腾,我试过好几个方案,这里都跟你抖搂出来:

白名单校验。 最笨但最稳,下载地址的域名、路径、参数名,全部走白名单,表达式?对不起,不认识,直接拒绝,这个方案上线之后,误杀了不少正常请求,但安全性确实拉满了,如果你对表达式注入下载地址特别敏感,这招值得考虑。

强制类型转换。 所有从下载地址里拿到的参数,一律当成字符串处理,坚决不让它们进任何表达式求值的上下文,听起来简单对吧?但坑在于,有些框架底层会自动帮你“智能”转换,你得把这个自动转换关掉,或者在外面再包一层,我为了找这个开关,翻文档翻到凌晨两点,眼睛都快瞎了。

用安全的下载地址生成方式。 别自己拼字符串了!用框架提供的安全 API,或者干脆用 URL 编码把参数全部转义掉,表达式符号?转义之后就是普通字符,看你还怎么注入,这个方案我最推荐,改造成本低,效果立竿见影。

对了,还得提一嘴:表达式注入下载地址这事儿,不光是后端的事,前端在拼接下载链接的时候,同样要小心,我见过有前端直接用 eval 处理下载参数的,我的天,那简直是给攻击者递刀子,千万别这么干!

给你几个实操小建议,拿走不谢

  1. 下载地址里的每一个参数,都当成敌人对待。 别信任何用户输入,表达式符号、特殊字符,该转义转义,该拒绝拒绝。
  2. 定期做安全扫描。 我现在的习惯是每次发版前跑一遍针对表达式注入下载地址的专项测试,用一些经典的 payload 去怼一怼,看能不能打穿。
  3. 日志里别打印原始表达式。 有些同学调试的时候喜欢把求值结果打日志,结果日志系统也支持表达式……好嘛,二次注入,防不胜防。
  4. 多看看框架的安全公告。 很多表达式注入下载地址的漏洞,都是框架层面爆出来的,你用的模板引擎、URL 解析库,出了安全更新赶紧升,别拖。

最后唠两句

安全这事儿吧,真的是“不怕一万,就怕万一”。表达式注入下载地址听起来好像离你很远,但只要你写过拼接下载链接的代码,就有可能中招,我写这篇文章,就是希望你别走我走过的弯路,看完之后,赶紧去翻翻你项目里那些生成下载地址的代码,看看有没有我上面说的问题,有则改之,无则加勉嘛!

行了,今天就唠到这儿,如果你也在表达式注入下载地址上踩过坑,或者有啥更好的防护思路,欢迎来评论区跟我掰扯掰扯,咱们下篇见!

文章版权声明:除非注明,否则均为极客网安-咸鱼原创文章,转载或复制请以超链接形式并注明出处。

目录[+]