{"record":{"id":"3736adf8a921c8bd","repo":"jeecgboot/JeecgBoot","slug":"url-http-https","errorCode":null,"errorMessage":"非法URL：仅允许 http / https 协议","messagePattern":"非法URL：仅允许 http / https 协议","errorType":"validation","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java","lineNumber":334,"sourceCode":"     * 1. 仅允许 http / https 协议；\n     * 2. 解析主机IP，拒绝 loopback（127.x / ::1）和 link-local（169.254.x，含云元数据 169.254.169.254 / fe80:）；\n     * 注意：RFC1918 私网段（10/172.16/192.168）允许通过，兼容企业内网 MinIO/OSS/文件服务等合法用途。\n     *\n     * @param fileUrl HTTP(S) URL\n     */\n    public static void checkSsrfHttpUrl(String fileUrl) {\n        if (StringUtils.isBlank(fileUrl)) {\n            throw new JeecgBootException(\"非法URL：地址为空\");\n        }\n        URI uri;\n        try {\n            uri = new URI(fileUrl);\n        } 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        }","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java#L316-L352","documentation":"Thrown by checkSsrfHttpUrl when the URL scheme is not http or https (case-insensitive). This prevents server-side requests via dangerous protocols like file://, gopher://, dict://, ftp://, jar://, netdoc:// that could read local files or perform SSRF-adjacent attacks. The check explicitly blocks any non-HTTP scheme.","triggerScenarios":"Passing a file:// URL (e.g., 'file:///etc/passwd'), a gopher:// URL for protocol smuggling, an ftp:// URL, or a URL missing its scheme entirely (uri.getScheme() returns null for scheme-less URIs).","commonSituations":"User supplies a local file path with 'file://' prefix expecting local file access; legacy code constructs URLs from user input that may include protocol other than http; front-end allows free-text URL input without scheme restriction.","solutions":["Ensure only http:// and https:// URLs are accepted from users — enforce this in front-end validation.","If the URL lacks a scheme, prepend 'https://' only if the host is trusted.","Strip any 'file://', 'ftp://', or other non-http schemes at the data-entry boundary.","Review upstream callers (FileDownloadUtils, AiragChatServiceImpl) to confirm they always pass http(s) URLs."],"exampleFix":"// before\nString fileUrl = \"file:///etc/passwd\";\nSsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl); // throws\n\n// after\nString fileUrl = userInput;\nif (!fileUrl.toLowerCase().startsWith(\"http://\") && !fileUrl.toLowerCase().startsWith(\"https://\")) {\n    return Result.error(\"仅支持 http/https 链接\");\n}\nSsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);","handlingStrategy":"validation","validationCode":"String lowerUrl = fileUrl.toLowerCase();\nif (!lowerUrl.startsWith(\"http://\") && !lowerUrl.startsWith(\"https://\")) {\n    return Result.error(\"仅支持 http/https 协议\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);\n} catch (JeecgBootException e) {\n    log.warn(\"Blocked non-HTTP scheme in URL: {}\", fileUrl);\n    return Result.error(e.getMessage());\n}","preventionTips":["Restrict URL input fields to http/https schemes on the front-end.","Reject file://, ftp://, gopher:// and other schemes at the data entry boundary.","Never construct server-side download URLs from unvalidated user input."],"tags":["ssrf","validation","scheme","security","url"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}