jeecgboot/JeecgBoot · error · JeecgBootException

非法URL:仅允许 http / https 协议

Error message

非法URL:仅允许 http / https 协议

What it means

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.

Source

Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java:334

     * 1. 仅允许 http / https 协议;
     * 2. 解析主机IP,拒绝 loopback(127.x / ::1)和 link-local(169.254.x,含云元数据 169.254.169.254 / fe80:);
     * 注意:RFC1918 私网段(10/172.16/192.168)允许通过,兼容企业内网 MinIO/OSS/文件服务等合法用途。
     *
     * @param fileUrl HTTP(S) URL
     */
    public static void checkSsrfHttpUrl(String fileUrl) {
        if (StringUtils.isBlank(fileUrl)) {
            throw new JeecgBootException("非法URL:地址为空");
        }
        URI uri;
        try {
            uri = new URI(fileUrl);
        } catch (URISyntaxException e) {
            throw new JeecgBootException("非法URL:格式错误");
        }
        String scheme = uri.getScheme();
        if (scheme == null || !(scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))) {
            throw new JeecgBootException("非法URL:仅允许 http / https 协议");
        }
        String host = uri.getHost();
        if (StringUtils.isBlank(host)) {
            throw new JeecgBootException("非法URL:主机名为空");
        }
        // 去掉 IPv6 的中括号
        if (host.startsWith("[") && host.endsWith("]")) {
            host = host.substring(1, host.length() - 1);
        }
        try {
            for (InetAddress addr : InetAddress.getAllByName(host)) {
                if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
                    throw new JeecgBootException("非法URL:禁止访问本机或链路本地地址 " + addr.getHostAddress());
                }
            }
        } catch (UnknownHostException e) {
            throw new JeecgBootException("非法URL:主机名无法解析");
        }

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Ensure only http:// and https:// URLs are accepted from users — enforce this in front-end validation.
  2. If the URL lacks a scheme, prepend 'https://' only if the host is trusted.
  3. Strip any 'file://', 'ftp://', or other non-http schemes at the data-entry boundary.
  4. Review upstream callers (FileDownloadUtils, AiragChatServiceImpl) to confirm they always pass http(s) URLs.

Example fix

// before
String fileUrl = "file:///etc/passwd";
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl); // throws

// after
String fileUrl = userInput;
if (!fileUrl.toLowerCase().startsWith("http://") && !fileUrl.toLowerCase().startsWith("https://")) {
    return Result.error("仅支持 http/https 链接");
}
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
Defensive patterns

Strategy: validation

Validate before calling

String lowerUrl = fileUrl.toLowerCase();
if (!lowerUrl.startsWith("http://") && !lowerUrl.startsWith("https://")) {
    return Result.error("仅支持 http/https 协议");
}

Try / catch

try {
    SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
} catch (JeecgBootException e) {
    log.warn("Blocked non-HTTP scheme in URL: {}", fileUrl);
    return Result.error(e.getMessage());
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/3736adf8a921c8bd. Report an issue: GitHub.