摘要:事件原因之前使用开发的一个网站。事件的处理方法在公司是有专门的安全组来做安全这块儿工作的。第一时间对这个接口进行了下线处理,然后评估了安全的解决方案,再次上线该接口。这些机器也肯定是经过做特殊的隔离处理的没有敏感的公司信息资源。
事件原因:
之前使用Nodejs开发的一个网站。在网站上有一个页面有个功能,允许用户上传图片或者粘贴一张图片链接。服务端读取用户上传的图片信息或者是请求用户填写的图片链接获取图片信息。
如果是用户使用上传功能,前端可以在input控件上做下限制上传文件的类型,后端再做下校验,以保障获取图片文件的合法(或者是安全)。
如果是用户填写的图片链接,其中隐藏的安全隐患就比较大了。如果用户填写不是正常的http、https或者是ftp类的链接,而是curl这种可以在服务端执行的命令,服务端拿到用户的链接不作处理直接请求的话,可能会对公司造成不可估量的额损失(比如:内网服务器信息泄露、更严重是内网服务器被攻击)。
事件的处理方法:在公司是有专门的安全组来做Web安全这块儿工作的。这个事件发生的时候,我首先是短信收到了服务器的报警,线上出现了大量的FATAL日志。这个时候立刻是登上服务器排查,紧接着就被公司的安全组某位同事在内部聊天工具上通知,网站收到了ssrf(服务器端请求伪造)攻击。第一时间对这个接口进行了下线处理,然后评估了安全的解决方案,再次上线该接口。
那么在服务端具体的防范ssrf攻击的方法时什么呢,请接着往下看。
服务端的解决方案ssrf具体原理就是在服务端伪造请求,请求服务器的资源。上面的案例场景我也提到了。我有个功能是会请求一张外网图片链接获取图片信息的。而用户填写的这个图片链接很可能就是违法的请求。当我服务端拿到这个链接后,并没有按照预期去请求外网资源,而是请求了内网服务的资源。这样我的服务器就被攻击。
那么什么原因可能造成ssrf漏洞呢:
web服务存在请求外网资源的功能
请求的url参数是外接用户可以控制的
服务端在请求外网资源前,没有做域名和IP校验(是否请求的是内网资源)
仅仅通过正则匹配来判断IP段(域名指向的也可能是内网IP,如果IP是十进制表示法没有问题,但IP还有十六进制和十进制、二进制表示法)
由上面提到的原因其实已经可以知道一些解决方法:
1.校验用户填写链接的协议头在拿到用户填写的链接时做下校验。看是否是合法的http、https、ftp协议头。当然这个校验为了减轻服务器压力应该在前端和后端都校验。如不过不是合法的请求协议头直接拒绝和返回请求资源失败的错误消息。(伪代码如下)
const allowReqprotocol = [ "http", "https", "ftp", ]; const reqProtocol = request.protocol; // 强制校验用户输入图片链接的协议头是否为自己网站允许的协议头 if (allowReqprotocol.indexOf(reqProtocol) < 0) { yog.log.warning(imgUrl); res.json(errorMsg.imgTypeError()); return; }2.检验用户请求的IP指向的是否为内网ip
上面已经介绍过仅仅通过正则没办法完全校验是否是合法的IP段。其实还有一种方法就是把我们的IP段转化为int来进行判断。刚好npm上有个包ip-to-int可以满足我们的需求。我们可以把两者结合起来一起来判断请求链接的IP是否为内网IP。
const dns = require("dns"); const url = require("url"); const ipToInt = require("ip-to-int"); const parseUrl = url.parse(rqUrlString); const hostname = parseUrl.hostname; // 使用Nodejs的dns模块根据域名解析出域名对应的ip地址 dns.lookup(hostname, (err, address, family) => { //加入我的内网IP段为 180.xxx.xxx.xxx 180.255.255.255 const ipRegx = /^180./; const ipArr = [ipToInt(180.0.0.0).toInt(), ipToInt(180.255.255.255).toInt()]; if(ipRegx.test(address) || ipRang(address)){ // 拒绝请求 } else { // 继续请求 } }); // 判断一个ip是否在一个数组之间 function ipRang(ip, array) { // ip在给定的ip段之间 return true; // ip不在给定的ip段之间 return false; }3.最后的检验
经过上面的检验已经基本上可以保证如果有请求外网的Url ,请求的在服务端执行以后指向的也是外网资源。那么当资源到达服务端以后,为了保障资源的安全性。我们对请求的内容做最后的校验。比如我仅仅是想请求图片资源,而且是限制了几种格式。比如:png、jpg、jpeg。
const allowImgContentType = [ "image/jpeg", "image/png", "image/gif" ]; if( allowImgContentType.indexOf(response.headers["content-type"]) < 0){ // 返回的内容为非法资源 }4.假如允许请求内网资源
上面的方法基本上都是如果请求的是内网资源都是直接拒绝掉了。但是假如有请求内网的需求怎么办呢。如果内网的资源对外部开发,肯定也是特定的机器。这些机器也肯定是经过op做特殊的隔离处理的、没有敏感的公司信息资源。那么当我们的请求到达公司内网后怎么保证该请求始终请求的是特定的IP呢?
其实我们只需要将请求的链接的host和在header投中和请求机器的ip做下绑定就好。
// 通过dns模块解析出host对应的ip 这里是address // 并且通过url模块解析出port path 和querystring 重新拼接请求的url const bindURLString = `${parseUrl.protocol}//${address}:${reqUrlPort}${pathName}?${queryString}`; // 然后在请求发出去的时候设置下header头,下面是我使用request模块发出请求时设置参数 const options = { "url": bindURLString, "encoding": "binary", "rejectUnauthorized": false, "headers": { "Host": bindDnsObj.hostname //这里绑定请求的host } };
这样做就是强制请求去请求特定的机器。而不会请求其它机器。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/90442.html
摘要:所以前端工程师不仅仅是写好页面和做好兼容性。对前端工程师的技术能力也会带来考验。这里想要说的是,如果使用了,使用了服务端渲染,对于前端工程师的个人素质要求会比较高,因为需要处理很多服务端的问题。 背景 我们团队有个项目由于开发时间较长,且是前后端杂糅的开发方式,维护成本很高,在线上暴露的问题很多。而且因为对接了公司一百多条产品线,每天都会接到大量的客诉和产品线反馈的问题。2017年11...
摘要:借助预加载图片详情文档地址项目中为了确保页面显示时,图片已经全部加载完毕,因此需要提前加载图片,加载图片的过程使用进度条显示。第二个参数表示是否搜索其子目录。并在同元素或父级添加了时,元素显示。 showImg(https://segmentfault.com/img/bV0ZzM?w=395&h=640); 1. 借助require.context预加载图片 详情文档地址 项目中为了...
摘要:起因由于国内的搜索引擎对单页面应用支持不友好,所以一般网站的网站做的是多页面应用选择做网站当然是世界最好的语言啦,开始也是想这样做的,但是在写这篇文章的时候,自己是一枚前端开发,考虑到可维护性,其他的前端未必能看懂代码,所以还是在方面选型, 起因 由于国内的搜索引擎对单页面应用支持不友好,所以一般网站的网站做的是多页面应用 选择 做网站当然是世界最好的语言PHP啦,开始也是想这样做的,...
阅读 1219·2021-11-22 13:54
阅读 1406·2021-11-22 09:34
阅读 2646·2021-11-22 09:34
阅读 3907·2021-10-13 09:39
阅读 3322·2019-08-26 11:52
阅读 3319·2019-08-26 11:50
阅读 1495·2019-08-26 10:56
阅读 1881·2019-08-26 10:44