什么是 SSRF(服务器端请求伪造)?
定义:
服务器端请求伪造(Server-Side Request Forgery, SSRF)是一种由攻击者控制服务器发送恶意请求的安全漏洞。攻击者可以利用 SSRF 在受害服务器内部发起网络请求,进而访问未授权的资源或服务。
漏洞产生原因
- 应用程序允许用户输入外部 URL 或 IP 地址,然后由服务器来处理或访问这些地址。
- 应用程序未对用户输入的 URL 进行严格验证和过滤。
- 服务器通常具有比外部用户更高的权限,攻击者可以通过伪造请求利用服务器与内部网络或其他受保护的系统通信。
漏洞产生的危害
常见的 SSRF 攻击目标
- 内网探测:攻击者利用服务器访问企业内网的资源,探测内网服务,如数据库、API等。
- 攻击其他服务:攻击者可以通过 SSRF 向目标服务发送恶意请求,执行命令或攻击其他系统。
- 滥用云服务:攻击者可利用 SSRF 滥用云服务的元数据 API,窃取敏感信息,如 AWS EC2 实例中的 IAM 凭证。
实际案例
-
Capital One 数据泄露(2019年)
背景: Capital One 是美国一家大型金融机构。2019 年发生了一起大规模的数据泄露事件,攻击者利用 SSRF 漏洞获取了该公司托管在 AWS S3 服务中的敏感数据。
漏洞详情: 攻击者通过 SSRF 漏洞访问了 AWS EC2 实例元数据服务,从中获取到了 IAM 角色的临时访问凭证。这些凭证允许攻击者访问 S3 存储桶,从而导致了 1 亿多
美国客户和 600 万加拿大客户的个人信息泄露。损失: Capital One 因此面临数千万美元的罚款,并需要处理大量的法律诉讼及声誉损失。
-
Jenkins SSRF 漏洞(2017年)
背景: Jenkins 是一个流行的开源自动化服务器,常用于 CI/CD(持续集成和持续部署)。在 2017 年,Jenkins 被发现存在一个 SSRF 漏洞,影响了多个版本。
漏洞详情: 攻击者可以利用 Jenkins 中的 SSRF 漏洞发送恶意请求,访问内部资源,甚至是其他内网服务。这个漏洞影响了多个企业使用 Jenkins 的环境。
影响: Jenkins 广泛应用于企业开发环境中,攻击者可以利用此漏洞获取内网中其他系统的访问权限,给大量企业的网络安全带来潜在的威胁。
SSRF 漏洞的破坏力巨大,通过 SSRF 攻击,攻击者可以访问内部网络或云元数据,进而窃取敏感数据,甚至控制关键系统。
如何防御 SSRF 漏洞?
输入校验与过滤:
通常而言,根据当前 HTTP 接口的具体功能,可以分为下面的两个场景:
-
应用服务所接收的 url 是固定的域名列表或者域名范围是可控的(已知的多个二级域名/多级域名),就应该创建白名单来校验域名。
- 严格验证输入:应限制用户输入的 URL,仅允许访问受信任的外部地址。可以使用白名单方式,限制 URL 必须符合特定的正则表达式或域名。
- 禁止私有地址访问:使用 IP 黑名单,阻止私有 IP 地址段的访问,如 127.0.0.1、10.x.x.x、192.168.x.x、172.16.x.x等。
-
如果接收的url的域名是不可控的(未知的),则可以考虑用一个沙箱环境来进行数据请求,实现与内网的分离。
代码示例
包含SSRF漏洞的代码(Java)
package com.example.ssrf_demo;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.stream.Stream;
@RestController
public class SSRF {
// 未对用户输入的url做任何限制,用户可以输入任意的url
// 例如,攻击者可以访问内网域名:http://oa.in.example.com
@GetMapping("/ssrf1")
public String ssrf1(@RequestParam(required = true) String url) throws IOException {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
HttpResponse