内容简介:说到 Web 安全,我们前端可能接触较多的是 XSS 和 CSRF。工作原因,在所负责的内部服务中遭遇了SSRF 的困扰,在此记录一下学习过程及解决方案。SSRF(Server-Side Request Forgery),即服务端请求伪造,是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定 URL 地址获取网页文本内容,加载指定地址的
说到 Web 安全,我们前端可能接触较多的是 XSS 和 CSRF。工作原因,在所负责的内部服务中遭遇了SSRF 的困扰,在此记录一下学习过程及解决方案。SSRF(Server-Side Request Forgery),即服务端请求伪造,是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。
SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定 URL 地址获取网页文本内容,加载指定地址的图片,下载等等。攻击者可根据程序流程,使用应用所在服务器发出攻击者想发出的 http 请求,利用该漏洞来探测生产网中的服务,可以将攻击者直接代理进内网中,可以让攻击者绕过网络访问控制,可以下载未授权的文件,可以直接访问内网,甚至能够获取服务器凭证。
笔者负责的内部 web 应用中有一个下载文件的接口 /download
,其接受一个 url 参数,指向需要下载的文件地址,应用向该地址发起请求,下载文件至应用所在服务器,然后作后续处理。问题便来了,应用所在服务器在这里成了跳板机,攻击者利用这个接口相当于取得了内网权限,能够进行不少具有危害的操作。
SSRF 带来的危害有:
- 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息;
- 攻击运行在内网或本地的应用程序(比如溢出);
- 对内网 web 应用进行指纹识别,通过访问默认文件实现;
- 攻击内外网的 web 应用,主要是使用 get 参数就可以实现的攻击(比如 struts2,sqli 等);
- 利用 file 协议读取本地文件等。
通用的解决方案有:
- 过滤返回信息。验证远程服务器对请求的响应是比较容易的方法。如果 web 应用是去获取某一种类型的文件,那么在把返回结果展示给用户之前先验证返回的信息是否符合标准;
- 统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态;
- 限制请求的端口为 http 常用的端口,比如 80, 443, 8080, 8090;
- 白名单内网 ip。避免应用被用来获取获取内网数据,攻击内网;
- 禁用不需要的协议。仅仅允许 http 和 https 请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题。
由于笔者的应用 /download
接口请求的文件地址比较固定,因此采用了白名单 IP 的方式。当然,笔者也学习了一下更加全面的解决方案,下面给出安全部门同事的思路:
- 协议限制(默认允许协议为 HTTP、HTTPS)、30x跳转(默认不允许 30x 跳转)、统一错误信息(默认不统一,统一错误信息避免恶意攻击通过错误信息判断)
-
IP地址判断:
- 禁止访问 0.0.0.0/8,169.254.0.0/16,127.0.0.0/8 和 240.0.0.0/4 等保留网段
-
若 IP 为 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 私有网段,请求该 IP 地址并判断响应
contents-type
是否为application/json
- 解决 URL 获取器和 URL 解析器不一致的方法为:解析 URL 后去除 RFC3986 中 user、pass 并重新组合 URL
然后是按照以上思路实现的 Node.js 版本的处理 SSRF 漏洞的主要函数的代码:
const dns = require('dns') const parse = require('url-parse') const ip = require('ip') const isReservedIp = require('martian-cidr').default const protocolAndDomainRE = /^(?:https?:)?\/\/(\S+)$/ const localhostDomainRE = /^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/ const nonLocalhostDomainRE = /^[^\s\.]+\.\S{2,}$/ /** * 检查链接是否合法 * 仅支持 http/https 协议 * @param {string} string * @returns {boolean} */ function isValidLink (string) { if (typeof string !== 'string') { return false } var match = string.match(protocolAndDomainRE) if (!match) { return false } var everythingAfterProtocol = match[1] if (!everythingAfterProtocol) { return false } if (localhostDomainRE.test(everythingAfterProtocol) || nonLocalhostDomainRE.test(everythingAfterProtocol)) { return true } return false } /** * @param {string} uri * @return string * host 解析为 ip 地址 * 处理 SSRF 绕过:URL 解析器和 URL 获取器之间的不一致性 * */ async function filterIp(uri) { try { if (isValidLink(uri)) { const renwerurl = renewUrl(uri) const parseurl = parse(renwerurl) const host = await getHostByName(parseurl.host) const validataResult = isValidataIp(host) if(!validataResult) { return false } else { return renwerurl } } else { return false } } catch (e) { console.log(e) } } /** * 根据域名获取 IP 地址 * @param {string} domain */ function getHostByName (domain) { return new Promise((resolve, reject) => { dns.lookup(domain, (err, address, family) => { if(err) { reject(err) } resolve(address) }) }) } /** * @param {string} host * @return {array} 包含 host、状态码 * * 验证 host ip 是否合法 * 返回值 array(host, value) * 禁止访问 0.0.0.0/8,169.254.0.0/16,127.0.0.0/8,240.0.0.0/4 保留网段 * 若访问 10.0.0.0/8,172.16.0.0/12,192,168.0.0/16 私有网段,标记为 PrivIp 并返回 */ function isValidataIp (host) { if ((ip.isV4Format(host) || ip.isV6Format(host)) && !isReservedIp(host)) { if (ip.isPrivate(host)) { return [host, 'PrivIp'] } else { return [host, 'WebIp'] } } else { return false } } /** * @param {string} uri * @return {string} validateuri * 解析并重新组合 url,其中禁止'user' 'pass'组合 */ function renewUrl(uri) { const uriObj = parse(uri) let validateuri = `${uriObj.protocol}//${uriObj.host}` if (uriObj.port) { validateuri += `:${uriObj.port}` } if (uriObj.pathname) { validateuri += `${uriObj.pathname}` } if (uriObj.query) { validateuri += `?${uriObj.query}` } if (uriObj.hash) { validateuri += `#${uriObj.hash}` } return validateuri }
对于最主要的可能出现漏洞的接口处理函数,由于各逻辑不同,这里就不给出具体实现。但是只要按照上面提出的规避 SSRF 漏洞的原则,结合上述几个函数,就能大致完成。
最后,一句话总结:永远不要相信用户的输入!
本文首发于我的博客( 点此查看 ),欢迎关注
以上所述就是小编给大家介绍的《Web 安全漏洞 SSRF 简介及解决方案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [浏览器安全漏洞一] dll劫持漏洞
- 【FreeBuf字幕组】Web安全漏洞系列:Electron框架漏洞
- 甲骨文例行更新修复了301个安全漏洞 包含45个严重漏洞
- 甲骨文例行更新修复了301个安全漏洞 包含45个严重漏洞
- [浏览器安全漏洞二] hao123桔子浏览器 – 页面欺骗漏洞
- 现如今连安全漏洞命名都用表情符了:思科路由器安全启动漏洞????????????(生气猫)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C++标准模板库编程实战
Ivor Horton / 郭小虎、程聪 / 2017-1
《C++标准模板库编程实战》介绍最新的C++14标准的API、库和扩展,以及如何将它们运用到C++14程序中。在书中,作者Ivor Horton 则阐述了什么是STL,以及如何将它们应用到程序中。我们将学习如何使用容器、迭代器,以及如何定义、创建和应用算法。此外,还将学习函数对象和适配器,以及它们的用法。 阅读完本书之后,你将能够了解如何扩展STL,如何定义自定义类型的C++组件,你还将能够......一起来看看 《C++标准模板库编程实战》 这本书的介绍吧!