如何修复Spring Boot中CodeQL检测到的RestTemplate SSRF漏洞
修复Spring Boot中CodeQL检测出的SSRF问题
问题分析
你的代码中,UriComponentsBuilder基于getUserUrl()构造请求URL并传入用户可控的userId参数。CodeQL触发SSRF告警,核心原因是当前的UTILITY.isValidUrl()仅验证URL格式合法性,无法阻止攻击者通过篡改getUserUrl()或userId参数,诱导服务器请求恶意内部/外部地址。
具体修复方案
1. 实现域名/IP白名单验证
替换原有仅校验格式的逻辑,只允许请求预先定义的可信服务地址:
// 定义可信域名白名单(根据实际业务调整) private static final Set<String> ALLOWED_DOMAINS = Set.of("trusted-user-service.com", "internal-user-api.company.com"); public boolean isTrustedUrl(String url) { try { URI uri = new URI(url); String host = uri.getHost(); // 检查域名是否在白名单内,同时禁止访问私有IP段 return ALLOWED_DOMAINS.contains(host) && !isPrivateIp(host); } catch (URISyntaxException e) { return false; } } // 辅助方法:判断是否为私有IP地址 private boolean isPrivateIp(String host) { try { InetAddress address = InetAddress.getByName(host); return address.isSiteLocalAddress() || address.isLoopbackAddress() || address.isLinkLocalAddress(); } catch (UnknownHostException e) { return false; } }
在业务代码中替换原验证逻辑:
boolean isValid = isTrustedUrl(builder.toUriString());
2. 严格转义用户输入参数
对userId进行URL编码,防止攻击者通过注入特殊字符(如@、/、%2F)篡改URL结构:
// 对userId进行URL编码,避免注入风险 String encodedUserId = UriUtils.encode(userId, StandardCharsets.UTF_8.name()); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getUserUrl()) .queryParam("userId", encodedUserId);
3. 配置RestTemplate的安全限制
禁止RestTemplate自动跟随重定向,防止被诱导请求恶意地址;同时限制仅允许HTTP/HTTPS协议:
@Bean public RestTemplate safeRestTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); // 禁止自动重定向 factory.setFollowRedirects(false); RestTemplate restTemplate = new RestTemplate(factory); // 添加拦截器,二次校验请求地址的合法性 restTemplate.getInterceptors().add((request, body, execution) -> { URI uri = request.getURI(); if (!"http".equals(uri.getScheme()) && !"https".equals(uri.getScheme())) { throw new IllegalArgumentException("仅允许HTTP/HTTPS协议请求"); } if (!isTrustedUrl(uri.toString())) { throw new IllegalArgumentException("请求地址不在可信范围内"); } return execution.execute(request, body); }); return restTemplate; }
之后在业务代码中注入这个安全配置的RestTemplate,替代原有实例。
4. 确保getUserUrl()不可被用户控制
如果getUserUrl()的取值来自用户输入或外部配置,必须额外验证其合法性,禁止传入任意地址。建议将可信服务地址配置在环境变量或配置文件中,而非由用户传入。
最终修改后的代码示例
@RequestMapping(value = "/data", method = RequestMethod.GET) public ResponseEntity<List<User>> getUserDetails(@Valid String userId) { // 转义用户输入参数 String encodedUserId = UriUtils.encode(userId, StandardCharsets.UTF_8.name()); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getUserUrl()) .queryParam("userId", encodedUserId); // 验证请求地址是否可信 boolean isValid = isTrustedUrl(builder.toUriString()); ResponseEntity<String> result = null; if (isValid) { result = safeRestTemplate.exchange(builder.toUriString(), HttpMethod.GET, null, String.class); } // 后续业务转换逻辑... return ResponseEntity.ok(convertToUserList(result.getBody())); }
内容的提问来源于stack exchange,提问作者Coder
相关产品推荐
相关产品推荐

