{"record":{"id":"0ab85a090594a663","repo":"jeecgboot/JeecgBoot","slug":"url-hostaddress","errorCode":null,"errorMessage":"非法URL：禁止访问本机或链路本地地址 {hostAddress}","messagePattern":"非法URL：禁止访问本机或链路本地地址 (.+?)","errorType":"validation","errorClass":"JeecgBootException","httpStatus":null,"severity":"critical","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java","lineNumber":347,"sourceCode":"        } catch (URISyntaxException e) {\n            throw new JeecgBootException(\"非法URL：格式错误\");\n        }\n        String scheme = uri.getScheme();\n        if (scheme == null || !(scheme.equalsIgnoreCase(\"http\") || scheme.equalsIgnoreCase(\"https\"))) {\n            throw new JeecgBootException(\"非法URL：仅允许 http / https 协议\");\n        }\n        String host = uri.getHost();\n        if (StringUtils.isBlank(host)) {\n            throw new JeecgBootException(\"非法URL：主机名为空\");\n        }\n        // 去掉 IPv6 的中括号\n        if (host.startsWith(\"[\") && host.endsWith(\"]\")) {\n            host = host.substring(1, host.length() - 1);\n        }\n        try {\n            for (InetAddress addr : InetAddress.getAllByName(host)) {\n                if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {\n                    throw new JeecgBootException(\"非法URL：禁止访问本机或链路本地地址 \" + addr.getHostAddress());\n                }\n            }\n        } catch (UnknownHostException e) {\n            throw new JeecgBootException(\"非法URL：主机名无法解析\");\n        }\n    }\n    //update-end---author:zhangdaihao ---date:2026-04-15  for：【issues/9553】修复二次SSRF漏洞，对HTTP下载URL进行安全校验-----------\n\n    /**\n     * 批量校验文件路径安全性（逗号分隔的多个文件路径）\n     * @param files 逗号分隔的文件路径\n     */\n    public static void checkPathTraversalBatch(String files) {\n        if (StringUtils.isBlank(files)) {\n            return;\n        }\n        for (String file : files.split(\",\")) {\n            if (StringUtils.isNotBlank(file)) {","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java#L329-L365","documentation":"Thrown by checkSsrfHttpUrl when InetAddress.getAllByName(host) resolves to a loopback address (127.x.x.x, ::1) or a link-local address (169.254.x.x including cloud metadata endpoint 169.254.169.254, or fe80:: IPv6). This is the core SSRF defense — it prevents the server from fetching attacker-controlled URLs that point to internal services or cloud metadata. The thrown message includes the resolved IP address for diagnostics. Note: RFC1918 private ranges (10.x, 172.16.x, 192.168.x) are intentionally allowed to support enterprise MinIO/OSS.","triggerScenarios":"URL host is 'localhost' or '127.0.0.1' (resolves to loopback); URL uses '169.254.169.254' (AWS/GCP/Azure cloud metadata endpoint); URL host is '0.0.0.0' or '::1'; DNS rebinding attack where a domain initially resolves to a public IP but resolves to 127.0.0.1 on the server's lookup.","commonSituations":"Developer testing with 'http://localhost:8080/...' as a download URL; misconfigured service URL pointing to loopback; SSRF attack payload targeting cloud metadata for credential theft; legitimate local development environment where the download target is on the same machine.","solutions":["Use the actual external hostname or IP of the target service, not 'localhost' or '127.0.0.1'.","If downloading from a service on the same machine in production, use its LAN IP (e.g., 192.168.x.x) which is allowed.","For cloud deployments, ensure the download URL points to the public-facing endpoint, not the internal metadata IP.","Review the thrown IP address in the error message to identify which internal target was requested."],"exampleFix":"// before\nString fileUrl = \"http://localhost:9000/bucket/file.pdf\";\nSsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl); // throws: 127.0.0.1\n\n// after\nString fileUrl = \"http://192.168.1.100:9000/bucket/file.pdf\";\nSsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl); // passes (RFC1918 allowed)","handlingStrategy":"validation","validationCode":"URI uri = new URI(fileUrl);\nString host = uri.getHost();\nfor (InetAddress addr : InetAddress.getAllByName(host)) {\n    if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {\n        return Result.error(\"禁止访问内部地址: \" + addr.getHostAddress());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);\n} catch (JeecgBootException e) {\n    log.error(\"SSRF attempt blocked — loopback/link-local target in URL: {}\", fileUrl);\n    return Result.error(e.getMessage());\n}","preventionTips":["Use external hostnames or RFC1918 IPs (allowed) for same-network services — never localhost/127.0.0.1.","In cloud deployments, audit all server-side fetch URLs to ensure none target metadata endpoints.","Treat this error as a potential security incident — investigate the source of the URL."],"tags":["ssrf","loopback","link-local","security","cloud-metadata","critical"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}