GeoServer 3.0.0 源码安全审计报告
审计对象: GeoServer 3.0.0(开发快照,目录
geoserver-3.0.0/src)
审计日期: 2026-08-12
审计方法: 静态代码审计(漏洞模式 grep 搜索 + 关键点人工核验 + 已知 CVE 交叉比对)
代码规模: 约 7,700 个 Java 文件 / 约 121 万行代码
关键依赖版本: GeoTools 35.0、Spring 7.0.8、Spring Security 7.0.5、XStream 1.4.21
1. 执行摘要
本次审计覆盖六大类漏洞方向(反序列化/RCE、命令执行、SQL 注入、未授权访问/越权、硬编码凭据、XSS、SSRF、路径穿越/任意文件)。核心结论:
- 默认凭据风险突出: 默认管理员
admin/geoserver在首次启动时自动创建,且 remember-me 签名密钥、keystore 主口令均硬编码为geoserver。在未改密部署下,攻击者可直接用默认口令登录管理后台。 - 存在一条完整的高危攻击面: WMS 的
filter/cql_filter参数属性名未做白名单校验即进入 JDBC 数据存储查询(对应 CVE-2023-25158 模式),而 WFS GetFeature 路径却已修复——同一类漏洞在两个入口修复状态不一致。 - OGC Filter 表达式求值在 GeoServer 侧无任何函数/accessor 白名单(CVE-2024-36401 模式);实际可利用性取决于捆绑的 GeoTools 35.0 层是否已内置属性访问限制(该版本远新于修复版本,大概率已内置,故定为条件性)。
- 安全架构整体良好但存在系统性缺口:
- ✅ XStream 已启用白名单(
SecureXStream),CVE-2016-1000027 已封堵 - ✅ FreeMarker 已启用
MemberAccessPolicy,SSTI(CVE-2024-23642/41877)已封堵 - ✅ 全站 URL 抓取采用 GeoTools
URLCheckers默认拒绝架构,SSRF 大面被缓解 - ⚠️ REST 层缺少对象级授权,完全依赖
rest.properties的 URL 规则兜底(默认 admin-only);一旦按官方注释放宽即产生横向越权 - ⚠️ 多处 REST 上传/模板接口存在路径穿越与任意文件读写(默认需 admin 认证)
- ⚠️ GWC 瓦片安全开关
securityEnabled默认关闭,受保护图层可匿名访问
- ✅ XStream 已启用白名单(
风险统计
| 严重性 | 数量 | 主要风险 |
|---|---|---|
| 严重 (Critical) | 1 | 默认管理员凭据可被直接登录利用 |
| 高危 (High) | 9 | SQL 注入、未认证 XSS、未授权访问、任意文件读写、REST 越权、SSRF |
| 中危 (Medium) | 13 | WFS-T filter 注入、SQL 视图参数注入、SLD/OWS SSRF、硬编码默认密钥、存储型 XSS 等 |
| 低危 (Low) | 14 | 硬编码缓存库口令、self-XSS、fail-open 模式、死代码 SSRF 等 |
| 已修复/已缓解 | 8 项 | XStream、FreeMarker、WFS 主入口、URLChecker、类加载白名单等 |
2. 审计范围与方法
审计范围: src/ 全部模块——main(核心)、ows、rest、restconfig、wfs-core、wms-core、gwc、security、web、extension/、community/,以及随发行版附带的 data/ 默认配置。
方法:
- 漏洞模式静态搜索:对 6 大类共 30+ 组 grep 模式递归搜索,排除 test 代码
- 关键命中点人工读取代码段,确认输入可控性、安全边界、认证要求
- 已知 GeoServer CVE 交叉比对,确认修复点在当前快照中的存在状态
- 对高危发现逐条二次核验(本次报告中的"严重/高危"项均已人工复核源码)
约定:
- 「未认证」= 匿名请求可达;「需 admin」= 受 REST/Web 默认安全规则保护,需管理员认证
- 严重性评估同时考虑默认可利用性与实际影响面
- 所有依赖 GeoTools 35.0 层行为的结论均明确标注为「条件性」
3. 风险总览表
| # | 漏洞 | 类别 | 严重性 | 认证要求 | 位置 |
|---|---|---|---|---|---|
| 1 | 默认管理员凭据 admin/geoserver 自动创建 | 硬编码凭据 | 严重 | 无 | GeoServerUser.java:31-40 |
| 2 | WMS GetMap/GetFeatureInfo filter/cql_filter 属性名未校验 |
SQL 注入 | 高 | 无 | GetMap.java:531-535 |
| 3 | 登录页 spring-security-redirect 反射 XSS |
XSS | 高 | 无 | GeoServerBasePage.java:239-253 |
| 4 | GWC 瓦片安全开关默认关闭 | 未授权访问 | 高 | 无(默认配置) | GWCConfig.java:38 |
| 5 | REST external. 上传任意文件读取 |
任意文件读 | 高 | admin | RESTUtils.java:223-245 |
| 6 | Importer 多部分/压缩包文件名穿越写 | 路径穿越 | 高 | admin | Directory.java:109-120 |
| 7 | features-templating 模板名穿越写 | 路径穿越 | 高 | admin(社区) | FileManagerBase.java:81-95 |
| 8 | CoverageStore 远程 harvest URL 无校验 | SSRF | 高 | admin | CoverageStoreFileController.java:110-119 |
| 9 | OGC Filter 表达式无函数/accessor 白名单 | RCE/表达式注入 | 高(条件) | 无 | CQLFilterKvpParser.java:24-34 |
| 10 | REST 控制器普遍缺少对象级授权 | 越权/IDOR | 高(条件) | admin→放宽后任意 | AbstractCatalogController.java:121-127 |
| 11 | WFS-T Transaction/Lock filter 未校验 | SQL 注入 | 中 | 认证 | UpdateElementHandler.java:250 等 |
| 12 | viewparams/env/viewParams SQL 视图参数替换 |
SQL 注入 | 中 | 无 | RenderingVariables.java:89-113 |
| 13 | GetMap SLD= 远程样式(重定向绕过) |
SSRF | 中 | 无 | GetMapKvpRequestReader.java:755,846 |
| 14 | GetLegendGraphic SLD= 远程样式 |
SSRF | 中 | 无 | GetLegendGraphicKvpReader.java:695-707 |
| 15 | GetMap REMOTE_OWS_URL 远程 WFS |
SSRF | 中 | 无 | GetMapKvpRequestReader.java:1466-1485 |
| 16 | WPS wps:Reference 远程输入 |
SSRF | 中 | 无 | RemoteRequestInputProvider.java:100,242 |
| 17 | wps-download 下载流程 Capabilities URL 回显 SSRF | SSRF | 高(条件) | 无 | DownloadMapProcess.java:576-582 |
| 18 | 默认 master/keystore 口令 geoserver |
硬编码密钥 | 中 | – | GeoServerSecurityManager.java:220 |
| 19 | remember-me 签名密钥硬编码 geoserver |
硬编码密钥 | 中 | – | RememberMeServicesConfig.java:21 |
| 20 | /gwc/rest/web/** 落入弱认证链 |
未授权访问 | 中 | 弱(默认匿名) | data/release/security/config.xml:9 |
| 21 | OpenLayers 预览 LAYERS/STYLES 存储型 XSS |
XSS | 中 | admin 查看 | OpenLayersPreviewPanel.java:186-187 |
| 22 | JSONP callback 未转义 |
XSS | 中 | 无(默认关闭) | JSONType.java:183-196 |
| 23 | UserPasswordController 仅校验 AUTHENTICATED_ROLE |
越权 | 中 | 认证 | UserPasswordController.java:52-57 |
| 24 | TaskManager zip 解压 zip-slip | 路径穿越 | 中 | admin(社区) | AppSchemaLocalPublicationTaskTypeImpl.java:106-129 |
| 25-38 | 低危项(见 §4.4) | 多类 | 低 | – | 见明细 |
4. 漏洞详情
4.1 严重 — 默认管理员凭据(可直接利用)
[S-1] 默认管理员 admin/geoserver 首次启动自动创建
- 文件:
src/main/src/main/java/org/geoserver/security/impl/GeoServerUser.java:31-40;落地点src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java:2635-2636、impl/EditableUserDAO.java:96-98 - 严重性: 严重
- 描述: 生产代码定义默认管理员账号,且当用户组服务中不存在 admin 时在首次启动自动创建
admin/geoserver。官方虽有"修改默认密码"横幅提醒,但口令本身未随机化。若部署后未立即改密,任何人可用默认口令登录全部管理界面(REST/WEB),直接获得系统完全控制权。随发行版附带的data/release/security/usergroup/default/users.xml也内置了该默认哈希。 - 代码证据:
public static final String DEFAULT_ADMIN_PASSWD = "geoserver"; public static final String ADMIN_USERNAME = "admin"; public static GeoServerUser createDefaultAdmin() { GeoServerUser admin = new GeoServerUser(ADMIN_USERNAME); admin.setPassword(DEFAULT_ADMIN_PASSWD); ... }// GeoServerSecurityManager.java:2635 if (userGroupService.getUserByUsername(GeoServerUser.ADMIN_USERNAME) == null) { userGroupStore.addUser(GeoServerUser.createDefaultAdmin()); - 利用条件: 任意网络可达者,在未改密的默认部署上直接登录。
- 建议: 首次启动强制改密或生成随机口令;部署文档强制要求修改。
4.2 高危
[H-1] WMS GetMap / GetFeatureInfo filter、cql_filter 属性名未校验 → SQL 注入(CVE-2023-25158 模式)
- 文件:
src/wms-core/src/main/java/org/geoserver/wms/GetMap.java:531-535(filter 直接setFilter)src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:430,1013(parseFilters直接透传)src/wms-core/src/main/java/org/geoserver/wms/featureinfo/VectorBasicLayerIdentifier.java:163(GetFeatureInfo 同路径)- 解析入口
src/wms-gml/src/main/java/org/geoserver/wms/kvp/FilterKvpParser.java:56-101、src/main/src/main/java/org/geoserver/ows/kvp/CQLFilterKvpParser.java:24-34
- 严重性: 高
- 描述: WMS 的
filter(OGC XML)与cql_filter参数解析为 GeoToolsFilter后,未做任何属性名(PropertyName)白名单校验即设置到 FeatureLayer 查询,经渲染器featureSource.getFeatures(query)下发到 JDBC 数据存储(PostGIS 等)。攻击者通过恶意属性名可在 JDBC 层注入 SQL。关键不对称: WFS GetFeature 路径已有GetFeature.validateFilter(遍历 PropertyName 校验其存在,§5 已修复项),而整个 WMS 路径(GetMap/GetFeatureInfo)完全没有。 - 代码证据:
// GetMap.java:531-535 —— 仅有 validateSort(494/586),无 filter 校验 Filter filter = SimplifyingFilterVisitor.simplify(Filters.and(ff, layerFilter, dimensionFilter)); final Query definitionQuery = new Query(source.getSchema().getName().getLocalPart()); definitionQuery.setFilter(filter); definitionQuery.setSortBy(layerSort); - 利用条件: 图层为 JDBC 数据源(PostGIS 等)时,未认证
GET /wms?request=GetMap&layers=<layer>&filter=<恶意PropertyName>或cql_filter=...。实际可利用性与捆绑 GeoTools 35.0 对 SQL 标识符的编码/校验有关。 - 建议: 在 GetMap/GetFeatureInfo 中复用
GetFeature.validateFilter属性名校验;对filter/cql_filter/sortBy统一走白名单。
[H-2] 登录页 spring-security-redirect 反射 XSS(未认证)
- 文件:
src/web/core/src/main/java/org/geoserver/web/GeoServerBasePage.java:239-253、src/web/core/src/main/java/org/geoserver/web/LoginFormHTMLInclude.java:67-93、模板src/web/core/src/main/resources/org/geoserver/web/include_login_form.html - 严重性: 高
- 描述:
workspace请求参数被拼接进redirectUrl,经 FreeMarker 直接写入隐藏域value="${redirectUrl}",无 HTML 属性转义(已人工核验模板与importAsString)。攻击者可构造/web/?workspace="><script>...在登录页执行任意 JS(钓鱼、会话窃取、凭据采集)。 - 代码证据:
// GeoServerBasePage.java:239-241 String ws = getPageParameters().get("workspace").toOptionalString(); if (ws != null && !ws.isEmpty()) { redirectUrl = "/web/?workspace=" + ws; } // LoginFormHTMLInclude.java:80 params.put("redirectUrl", redirectUrl != null ? redirectUrl : ""); template.process(params, writer); // FreeMarker ${} 默认不转义<!-- include_login_form.html --> <input type="hidden" name="spring-security-redirect" value="${redirectUrl}" /> - 利用条件: 未认证
GET /web/?workspace="><script>alert(document.cookie)</script>。 - 建议: 对
redirectUrl做白名单(仅允许站内相对路径)并 HTML 转义;为 FreeMarker 全局开启?html默认转义。
[H-3] GWC 瓦片安全开关默认关闭 → 受保护图层匿名访问
- 文件:
src/gwc/src/main/java/org/geoserver/gwc/config/GWCConfig.java:38、src/gwc/src/main/java/org/geoserver/gwc/dispatch/GeoServerSecurityFilter.java:32-38 - 严重性: 高
- 描述:
private boolean securityEnabled;默认false。GeoServerSecurityFilter.checkSecurity仅在isSecurityEnabled()为真时才执行图层访问校验。即默认部署下,即使 GeoServer 全局安全已开启,GWC 瓦片(含受保护图层)也可匿名访问,绕过图层级权限控制。 - 代码证据:
// GWCConfig.java:38 —— 默认 false private boolean securityEnabled; // GeoServerSecurityFilter.java if (GWC.get().getConfig().isSecurityEnabled()) { ... 权限校验 ... } - 利用条件: 默认配置即生效,匿名请求
GET /gwc/service/wmts?LAYER=<受保护图层>。 - 建议: 默认开启 securityEnabled,或与全局安全策略联动。
[H-4] REST external. 上传接口任意文件读取
- 文件:
src/rest/src/main/java/org/geoserver/rest/util/RESTUtils.java:223-245 - 严重性: 高
- 描述:
handleEXTERNALUpload把请求体直接当路径字符串,new File(stringURL)若存在即作为资源返回——未调用URLCheckers.confirm(对比handleURLUpload走IOUtils.upload有检查)。可读取服务器任意可读文件(/etc/passwd、data/内其他凭据等)。 - 代码证据(已人工核验):
stringURL = IOUtils.getStringFromStream(inStream); inputFile = new File(stringURL); // 任意路径 if (!inputFile.exists()) { URL fileURL = new URL(stringURL); // 也是 URL 读取点 inputFile = IOUtils.URLToFile(fileURL); } return Files.asResource(inputFile); // 直接作为资源返回 - 利用条件: 需管理员认证
PUT /rest/workspaces/{ws}/datastores/{store}/external.{format}。默认 REST 规则为 admin-only,故为"高权限下的任意文件读";一旦 REST 规则放宽则风险进一步放大。 - 建议: 对该路径做路径白名单(限制在数据目录内)并接入
URLCheckers。
[H-5] Importer 多部分/压缩包文件名穿越写
- 文件:
src/extension/importer/core/src/main/java/org/geoserver/importer/Directory.java:109-120,420-434 - 严重性: 高
- 描述:
child(name)直接new File(this.file, name);accept(FileItem)以客户端提供的item.getName()作为目标写文件。文件名含../可越出上传目录写任意文件(WebShell、覆盖配置等)。压缩包解压路径同样未做 zip-slip 防护。 - 代码证据:
public File child(String name) { return new File(this.file, name); } public void accept(FileItem item) throws Exception { lockDirectory(); File dest = child(item.getName()); // 未过滤 ../ item.write(dest.toPath()); - 利用条件: 需 admin
POST /rest/imports/{id}(multipart 上传)。 - 建议: 文件名白名单 + 规范化后校验父目录;解压时逐条目校验路径。
[H-6] features-templating 模板名/模式名穿越写(社区模块)
- 文件:
src/community/features-templating/features-templating-core/src/main/java/org/geoserver/featurestemplating/configuration/FileManagerBase.java:81-95;控制器TemplateRestController.java:103,122,172,192、SchemaRestController.java - 严重性: 高
- 描述:
new File(destDir, getName(info) + "." + getExtension(info)),getName(info)来自@RequestParam templateName/schemaName,未做..校验,可穿越写出模板/模式文件到任意目录。 - 代码证据:
File file = new File(destDir, getName(info) + "." + getExtension(info)); fos.write(rawContent.getBytes()); - 利用条件: 需 admin,社区模块默认不装。
- 建议: 文件名净化 + 路径归一化校验。
[H-7] CoverageStore 远程 harvest URL 无校验 → SSRF
- 文件:
src/restconfig/src/main/java/org/geoserver/rest/catalog/CoverageStoreFileController.java:110-119,454-464 - 严重性: 高
- 描述: 远程 harvest 的 URL 直接来自请求体字符串,无
URLCheckers.confirm,传给StructuredGridCoverage2DReader.harvest(ImageMosaic)拉取。可让服务器访问任意内网地址(内网探测、云元数据169.254.169.254、内网服务)。 - 代码证据:
if (method == UploadMethod.remote) { harvestedResources.add(handleRemoteUrl(request)); } sr.harvest(null, harvestedResources, GeoTools.getDefaultHints()); // handleRemoteUrl: return IOUtils.toString(request.getReader()); // 原始 URL - 利用条件: 需 admin
POST /rest/workspaces/{ws}/coveragestores/{store}/remote.tiff(body=http://169.254.169.254/...)。 - 建议: 该入口接入
URLCheckers并禁止内网地址。
[H-8] OGC Filter / ECQL 表达式求值无函数/accessor 白名单(CVE-2024-36401 模式,条件性)
- 文件:
- 入口
src/main/src/main/java/org/geoserver/ows/kvp/CQLFilterKvpParser.java:24-34、src/main/src/main/java/org/geoserver/util/XCQL.java:28-42、src/wms-gml/src/main/java/org/geoserver/wms/kvp/FilterKvpParser.java:56-101 - 求值链
src/ows/src/main/java/org/geoserver/ows/util/ClassProperties.java:38-48(把全部零参get*/is*方法、含Object.getClass()注册为属性class)、src/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java:127、src/main/src/main/java/org/geoserver/catalog/impl/CatalogPropertyAccessor.java:63-92(SPI 注册)、WPSsrc/extension/wps/wps-core/src/main/java/org/geoserver/wps/property/BeanPropertyAccessor.java:14-41
- 入口
- 严重性: 高(条件性)
- 描述: 用户可控的
cql_filter/FILTER经ECQL.toFilterList/CQL.toFilterList无任何函数白名单/校验;同时 GeoServer 自注册的PropertyAccessor通过ClassProperties将Object.getClass()暴露为属性class,OwsUtils.get反射可达任意 getter。这是 CVE-2024-36401 的典型形态,GeoServer 侧无任何缓解措施。 - 代码证据:
// CQLFilterKvpParser.java —— 直通,无过滤 public Object parse(String value) throws Exception { return XCQL.toFilterList(value); // -> ECQL.toFilterList } // ClassProperties.java:38-44 —— getClass() 被当作 bean 属性 "class" for (Method method : clazz.getMethods()) { if ((name.startsWith("get") || name.startsWith("is") || ...) && params.length == 0) { getters.put(gp(method), method); } } - 利用条件: 未认证
WFS GetFeature&cql_filter=...或WMS GetMap&FILTER=...。实际 RCE 可利用性取决于捆绑的 GeoTools 35.0——该版本远新于 CVE-2024-36401 的修复版本(GEOT-7626,GeoTools 30.x),大概率已在 GeoTools 层限制属性访问,故本项为「GeoServer 侧纵深防御缺失」;若部署时降级到旧 GeoTools 则构成未认证 RCE。 - 建议: 在
CQLFilterKvpParser/FilterKvpParser/ClassProperties增加函数与 getter 白名单作纵深防御;锁定 GeoTools ≥ 修复版本。
[H-9] REST 控制器普遍缺少对象级授权(IDOR,条件性)
- 文件: 代表
src/restconfig/src/main/java/org/geoserver/rest/catalog/AbstractCatalogController.java:121-127;所有 REST 控制器无@Secured/@RolesAllowed/@PreAuthorize - 严重性: 高(条件性)
- 描述: REST 层无任何对象级权限校验,授权完全依赖
data/release/security/rest.properties的 URL 规则(/**;GET...=ROLE_ADMINISTRATOR)。一旦管理员按官方注释放宽为"登录可读/写",任意认证用户即可读取/修改任意 workspace/store/layer(横向越权),且checkFullAdminRequired仅对部分无 workspace 的全局资源生效(如样式删除、图层组删除未调用)。 - 代码证据:
// rest.properties 默认 /**;GET,HEAD,OPTIONS=ROLE_ADMINISTRATOR /**;POST,DELETE,PUT=ROLE_ADMINISTRATOR - 利用条件: 默认 admin-only 缓解;放宽 REST 规则后任意登录用户越权。
- 建议: 引入对象级授权(每个资源操作校验 workspace/用户所有权);在 REST 控制器层加统一鉴权注解。
4.3 中危
[M-1] WFS-T Transaction(Update/Delete/Replace)与 LockFeature 的 filter 未校验 → SQL 注入
- 文件:
src/wfs-core/src/main/java/org/geoserver/wfs/UpdateElementHandler.java:250、DeleteElementHandler.java:73,114、ReplaceElementHandler.java:105、LockFeature.java:118 - 严重性: 中
- 描述: 这些处理器把用户 filter 直接执行(
store.getFeatures(filter)/removeFeatures(filter)/getFeatureWriter),未经过 GetFeature 中的validateFilter属性名校验。若 feature store 为 JDBC 存储,恶意 PropertyName 可被拼入 SQL。Update 仅校验了被更新的属性名,未校验筛选 filter 的属性名。 - 建议: 在 WFS-T/Lock 处理器复用
GetFeature.validateFilter。
[M-2] WMS viewparams / env / WFS viewParams → SQL 视图参数直接替换
- 文件:
src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:533-557、src/wms-core/src/main/java/org/geoserver/wms/RenderingVariables.java:89-113、src/wfs-core/src/main/java/org/geoserver/wfs/GetFeature.java:298-300,1353-1354 - 严重性: 中
- 描述: 参数值无校验地放入
Hints.VIRTUAL_TABLE_PARAMETERS,GeoTools 对 SQL 视图做直接字符串替换。当图层由带%param%占位符的 SQL 视图构成时,攻击者可注入 SQL。已知设计权衡,但需管理员先配置 SQL 视图。 - 建议: 对 viewparams/env 值做注入字符校验;文档明确 SQL 视图风险。
[M-3] GetMap SLD= 远程样式加载(校验后重定向绕过)
- 文件:
src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:755,846-901 - 严重性: 中
- 描述:
SLD参数为用户可控远程样式 URL。L755 有URLCheckers.confirm,但校验通过后getHttpInputStream(L867)使用 Apache HttpClient 裸抓取,默认跟随重定向——放行的公网 URL 可 302 跳转内网;响应经解析可用于盲 SSRF/内网探测。默认拒绝策略下被阻断,配置放行规则后成面。 - 建议: 显式
disableRedirectHandling;放行规则避免宽泛https?://.*。
[M-4] GetLegendGraphic SLD= 远程样式加载
- 文件:
src/wms-core/src/main/java/org/geoserver/wms/legendgraphic/GetLegendGraphicKvpReader.java:695-707 - 严重性: 中
- 描述: 同上模式,L697 显式
URLCheckers.confirm,L699 用url.openStream()(裸 URLConnection,跟随重定向)抓取。默认拒绝下阻断,放行后成面。 - 建议: 同 M-3。
[M-5] GetMap REMOTE_OWS_URL 远程 WFS 级联
- 文件:
src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:1466-1485 - 严重性: 中
- 描述: KVP 参数
REMOTE_OWS_URL用户可控。L1469 有URLCheckers.confirm,随后拼接REQUEST=GetCapabilities走 GeoTools HTTP 层(二次检查)。默认拒绝下阻断;异常被统一掩盖,放行后为盲 SSRF/内网探测。 - 建议: 同 M-3。
[M-6] WPS wps:Reference 远程输入
- 文件:
src/extension/wps/wps-core/src/main/java/org/geoserver/wps/executor/RemoteRequestInputProvider.java:100,139-181,242,253-332 - 严重性: 中
- 描述:
wps:Reference的href用户可控,构造函数 L100 与 body 引用 L242 有显式URLCheckers.confirm,且buildHttpClient显式disableRedirectHandling()(优于其他点);但①系统属性geoserver.wps.remoteInput.insecureHostname可关主机名校验;②非 http 协议走openOtherURL(L321)裸 URLConnection。默认拒绝下阻断。 - 建议: 移除/默认关闭
insecureHostname开关;非 http 协议也走受控客户端。
[M-7] wps-download DownloadMapProcess Capabilities URL 回显 SSRF(核验后上调为高)
- 文件:
src/extension/wps-download/src/main/java/org/geoserver/wps/gs/download/DownloadMapProcess.java:103,161,576-582;输入模型.../download/Layer.java:16-27 - 严重性: 高(条件性;经二次源码核验确认)
- 描述:
Layer[] layers为 WPS@DescribeParameter内联复杂输入,layer.Capabilities(@XmlElement)是用户可控 URL。getServer用new WebMapServer(new URL(layer.getCapabilities()), client)抓取远端 WMS,随后GetMap结果图经RenderedImage以 RawData 直接回显给调用者。整个extension/wps-download源码树无任何显式URLCheckers.confirm,仅依赖HTTPClientFinder.createClient()(GeoTools 受控客户端)的隐式检查——是全部 SSRF 点中唯一"无显式校验且结果回显"的反射型 SSRF。默认拒绝策略下被阻断;一旦管理员配置放行规则即可读取内网 WMS 返回图像内容。 - 代码证据:
private Supplier<org.geotools.http.HTTPClient> httpClientSupplier = () -> HTTPClientFinder.createClient(); @DescribeParameter(name = "layer", min = 1, ...) Layer[] layers, ... server = new WebMapServer(new URL(layer.getCapabilities()), client); - 调用链:
POST /geoserver/wps(gs:Download内联layer/Capabilities)→execute→ 远程 GetCapabilities/GetMap → 图像回显。 - 建议: 增加显式
URLCheckers.confirm;对回显结果做源校验。
[M-8] 默认 master/keystore 口令与 remember-me 签名密钥硬编码
- 文件:
src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java:220;src/main/src/main/java/org/geoserver/security/rememberme/RememberMeServicesConfig.java:21+data/*/security/config.xml(<key>geoserver</key>) - 严重性: 中
- 描述: keystore 主口令(解密数据源口令等敏感配置)默认
geoserver;remember-me cookie 的 HMAC 签名密钥默认geoserver且随发行版配置写死。签名密钥公开可预测时,攻击者可伪造合法用户的 remember-me cookie(已知用户名即可)绕过口令登录。 - 代码证据:
public static final char[] MASTER_PASSWD_DEFAULT = "geoserver".toCharArray(); public static final String DEFAULT_KEY = "geoserver"; - 建议: 首次启动随机化;文档强制修改。
[M-9] /gwc/rest/web/** 落入弱认证链
- 文件:
data/release/security/config.xml:9(web 链path="/web/**,/gwc/rest/web/**,/",含 anonymous + 默认IS_AUTHENTICATED_ANONYMOUSLY) - 严重性: 中
- 描述: GWC 配置端点
/gwc/rest/web/**(TileLayer 配置读写)被并入 admin UI 链而非/gwc/rest/**(basic + admin-only)链,在默认geoserverMetadataSource下匿名可访问,与/gwc/rest/**防护不一致。 - 建议: 将
/gwc/rest/web/**移入受保护链。
[M-10] OpenLayers 预览页 LAYERS/STYLES 存储型 XSS
- 文件:
src/web/wms/src/main/java/org/geoserver/wms/web/data/OpenLayersPreviewPanel.java:186-187、模板ol-load.ftl:9-14 - 严重性: 中
- 描述: 图层/样式名
prefixedName()直接写入 JS 字符串'LAYERS': '${layer}',未做 JS 上下文转义。图层名含'、</script>可逃逸执行。 - 建议: JS 上下文转义或 JSON 序列化注入。
[M-11] JSONP callback 回调名未转义
- 文件:
src/main/src/main/java/org/geoserver/json/JSONType.java:183-196,277-294(默认关闭,:59-62) - 严重性: 中(条件性,需
ENABLE_JSONP=true) - 描述:
callback从FORMAT_OPTIONS读取,outWriter.write(callback + "(")直接拼接,可注入任意 JS。 - 建议: callback 白名单校验(仅
[A-Za-z0-9_$])。
[M-12] UserPasswordController 仅校验 AUTHENTICATED_ROLE
- 文件:
src/restconfig/src/main/java/org/geoserver/rest/security/UserPasswordController.java:52-57 - 严重性: 中
- 描述:
PUT /rest/security/self/password仅要求AUTHENTICATED_ROLE(任何登录用户即可改自己密码)。需确保该端点不落入 REST 链的匿名段,否则存在未认证改密风险。 - 建议: 校验端点不在匿名链;对改密增加旧密码验证。
[M-13] TaskManager zip 解压 zip-slip(社区)
- 文件:
src/community/taskmanager/core/src/main/java/org/geoserver/taskmanager/tasks/AppSchemaLocalPublicationTaskTypeImpl.java:106-129 - 严重性: 中
- 描述: zip 条目名直接拼入
Resources.fromPath(... + entry.getName())并写文件,zip-slip 可穿越写出。 - 建议: 解压逐条目做路径归一化与父目录校验。
4.4 低危
| # | 漏洞 | 位置 | 说明 |
|---|---|---|---|
| L-1 | HSQLDB 缓存库硬编码口令 geoserver/geopass |
extension/kml/.../CachedHierarchyRegionatingStrategy.java:172-173 |
KML 聚合缓存库;作者标注"库内容不敏感" |
| L-2 | H2 内嵌库硬编码 sa/sa |
community/taskmanager/.../H2DbSourceImpl.java:76-79 |
社区模块默认超级用户 |
| L-3 | GeoFence 认证器硬编码 admin/geoserver 字面量 |
extension/geofence/.../GeoFenceAuthenticationProvider.java:85 |
仅日志分支,不授权;但证明默认口令散落生产代码 |
| L-4 | GetFeatureInfo sortBy 属性名未校验 |
wms-core/.../VectorBasicLayerIdentifier.java:146 |
ORDER BY 拼接面 |
| L-5 | GWC CachedLayersPage 关闭转义 | web/gwc/.../CachedLayersPage.java:299-307 |
图层名原样输出(存储型,需目录写入) |
| L-6 | 样式编辑器 replaceSelection self-XSS |
web/wms/.../AbstractStylePage.java:838-841 |
本地取色器输入 |
| L-7 | checkAuthenticationForRole 失败开放 |
security/.../GeoServerSecurityManager.java:1707-1721 |
全局安全关闭时管理接口可访问(设计如此,失败开放) |
| L-8 | checkFullAdminRequired 覆盖不全 |
restconfig/.../AbstractCatalogController.java:121-127 |
样式删除/图层组删除未强制 admin |
| L-9 | SmartDataLoader 数据源名拼接路径 | community/smart-data-loader/.../SmartDataLoaderDataAccessFactory.java:219 |
datastoreName 未过滤 .. |
| L-10 | OSEO 样例 granule 位置 new File |
community/oseo/.../CollectionLayerManager.java:199-213 |
位置来自 DB,弱可控 |
| L-11 | GSR 图片资源目录 | community/gsr/.../ImageResourceController.java:53-88 |
仅取 URI 末段,容器归一化基本防穿越 |
| L-12 | Importer file:// 协议导入 |
extension/importer/rest/.../ImportTaskController.java:300-334 |
仅 file: 协议,本地导入 |
| L-13 | DataTransformer.decodeReferenceData 死代码 SSRF |
extension/wps/.../DataTransformer.java:118-131 |
无校验 URL 抓取,但当前不可达 |
| L-14 | 示例/测试凭据 | community/rest-openapi/.../GeoServerClient.java:52、geofence-default-override.properties |
Javadoc/测试资源示例 |
5. 已修复 / 已缓解项(纵深防御状态)
以下历史高危漏洞在本快照中已被有效封堵,如实记录以免误报:
| 项 | 历史漏洞 | 当前状态 | 关键位置 |
|---|---|---|---|
| XStream 反序列化 RCE | CVE-2016-1000027 | 已修复 SecureXStream 白名单(NoTypePermission.NONE + allowTypes 白名单,可扩展 GEOSERVER_XSTREAM_WHITELIST) |
config/util/SecureXStream.java:148-191、XStreamPersister.java:296 |
| FreeMarker SSTI | CVE-2024-23642 / CVE-2023-41877 | 已修复 GeoServerMemberAccessPolicy:isConstructorExposed=false + Class/ClassLoader/reflect/security 黑名单 + 静态访问默认关闭 |
template/GeoServerMemberAccessPolicy.java:61-77 |
| WFS GetFeature 属性名 SQL 注入 | CVE-2023-25157 | 已修复 GetFeature.validateFilter(遍历 PropertyName 校验) + validateSortBy + collectPropertyNames |
wfs-core/.../GetFeature.java:1491-1514 |
| REST 类加载注入 | – | 已缓解 AllowedAuthenticationProviderClasses 白名单,resolveConfigClassUncached 先校验再实例化 |
restconfig/.../AuthenticationProviderRestController.java:488 |
| SSRF 大面 | CVE-2024-36404/36405 等 | 已缓解 GeoTools URLCheckers 默认拒绝(enabled=true 空规则 → 全拒)+ HTTP 客户端/渲染层强制检查 |
URLChecksConfiguration、GeoTools 35.0 |
| 命令执行 | – | 未发现未认证触发面;外部工具调用(ogr/gdal/wps)均限管理员配置且多走 ProcessBuilder(List) |
extension/ogr、extension/importer |
| JNDI 注入 | CVE-2022-24847 | 未发现用户可控 JNDI 名;lookup 均来自管理员数据源配置 |
taskmanager、security/ldap |
| Java 原生反序列化 | – | readObject 命中点均为服务端内部文件/克隆,无 HTTP 直连 |
AbstractRoleStore.java:312 等 |
6. 已知 CVE 交叉比对
| CVE | 描述 | 本快照状态 |
|---|---|---|
| CVE-2016-1000027 | XStream 反序列化 RCE | ✅ 已修复(SecureXStream 白名单) |
| CVE-2022-24847 | WPS JNDI 注入 RCE | ✅ 未发现用户可控 JNDI 点 |
| CVE-2023-25157 | WFS 属性名 SQL 注入 | ⚠️ WFS GetFeature 已修复;WFS-T/Lock 未修复(M-1) |
| CVE-2023-25158 | WMS 属性名 SQL 注入 | ❌ 未修复(H-1:GetMap/GetFeatureInfo 无属性名校验) |
| CVE-2023-35042 | WMS 反射 SQL 注入 | ⚠️ 同类根因(H-1/M-1/M-2) |
| CVE-2023-41877 | FreeMarker SSTI | ✅ 已修复(MemberAccessPolicy) |
| CVE-2023-51444 | 日志注入 | ⚠️ 未单独验证(非本次重点) |
| CVE-2024-23642 | FreeMarker ?new() SSTI |
✅ 已修复(构造函数禁用) |
| CVE-2024-36401 | OGC Filter 表达式 RCE | ⚠️ 条件性(H-8:GeoServer 侧无白名单,依赖 GeoTools 35 修复) |
| CVE-2024-36403 | 日志注入 | ⚠️ 未单独验证 |
| CVE-2024-36404 | 外部 OWS 数据源 SSRF | ⚠️ 部分缓解(H-7/M-3~M-7,默认 URLChecker 拒绝) |
| CVE-2024-36405 | WMTS 远程资源 SSRF | ⚠️ 同上 |
7. 安全架构说明(理解本报告的前提)
- REST 层认证默认依赖 URL 规则:
data/release/security/rest.properties默认/**=ROLE_ADMINISTRATOR,这是大量 REST 侧漏洞(H-4~H-7、H-9)被"admin-only"缓解的根本原因;一旦该文件被放宽(官方注释即演示如何放宽),上述漏洞严重性立即升级。 - URL 抓取默认拒绝:
URLCheckers(GEOT-6512)默认enabled=true、空规则 → 一切远程 URL 被拒;管理员需显式配置urlchecks.xml放行规则,而规则仅做正则匹配、不解析 DNS/内网 IP,且多处校验后仍跟随重定向。 - WFS 与 WMS 修复不对称: 同一类属性名注入漏洞,WFS GetFeature 有
validateFilter而 WMS 全链路没有——这是本快照最值得警惕的"半修复"状态。 - GWC 与主安全体系隔离: GWC 瓦片访问默认不走全局安全策略(securityEnabled=false)。
8. 修复建议(按优先级)
P0 — 立即处置(阻断直接利用)
- 修改默认管理员口令(admin/geoserver)、master 口令、remember-me 密钥;部署初始化脚本强制改密或随机生成。
- 在 WMS
GetMap/GetFeatureInfo/WFS-T/Lock 复用GetFeature.validateFilter属性名校验,封堵 SQL 注入(CVE-2023-25158 同源)。 - 修复登录页
spring-security-redirect反射 XSS(白名单 + HTML 转义)。
P1 — 高优先级
4. 将 GWC securityEnabled 默认设为 true 并与全局安全联动。
5. RESTUtils.handleEXTERNALUpload 与 CoverageStoreFileController 远程 harvest 接入 URLCheckers 与路径白名单。
6. Importer/features-templating/TaskManager 的文件名与 zip 条目做路径归一化校验(防穿越写)。
7. 为 CQLFilterKvpParser/FilterKvpParser/ClassProperties 增加函数与 getter 白名单(纵深防御,锁定 GeoTools ≥ 修复版本)。
8. REST 层引入对象级授权;checkFullAdminRequired 覆盖所有全局资源删除操作。
P2 — 中期加固
9. SLD/REMOTE_OWS_URL/WPS 远程输入的 URL 抓取显式禁止重定向;DownloadMapProcess 增加显式 URLCheckers.confirm。
10. /gwc/rest/web/** 移入受保护认证链。
11. OpenLayers 预览/JSONP/样式编辑器输出转义;sortBy 纳入属性名校验。
12. 移除 geoserver.wps.remoteInput.insecureHostname 逃生通道。
P3 — 常规整改
13. 低危硬编码口令(HSQLDB/H2)改为随机生成或配置化;移除 GeoFence 认证器中默认口令字面量。
14. DataTransformer.decodeReferenceData 死代码删除或补校验;SmartDataLoader/OSEO/GSR 路径拼接净化。
15. 文档明确 SQL 视图(viewparams)、REST 规则放宽的固有风险。
9. 附录:关键文件索引
默认凭据 src/main/.../security/impl/GeoServerUser.java
WMS SQL 注入 src/wms-core/.../wms/GetMap.java
src/wms-core/.../wms/map/GetMapKvpRequestReader.java
src/wms-core/.../wms/featureinfo/VectorBasicLayerIdentifier.java
登录页 XSS src/web/core/.../web/GeoServerBasePage.java
src/web/core/.../web/LoginFormHTMLInclude.java
src/web/core/src/main/resources/org/geoserver/web/include_login_form.html
GWC 未授权 src/gwc/.../gwc/config/GWCConfig.java
任意文件读 src/rest/.../rest/util/RESTUtils.java
路径穿越 src/extension/importer/core/.../importer/Directory.java
src/community/features-templating/.../configuration/FileManagerBase.java
SSRF src/restconfig/.../rest/catalog/CoverageStoreFileController.java
src/wms-core/.../wms/map/GetMapKvpRequestReader.java
src/wms-core/.../wms/legendgraphic/GetLegendGraphicKvpReader.java
src/extension/wps/wps-core/.../wps/executor/RemoteRequestInputProvider.java
src/extension/wps-download/.../wps/gs/download/DownloadMapProcess.java
表达式注入 src/main/.../ows/kvp/CQLFilterKvpParser.java
src/ows/.../ows/util/ClassProperties.java
src/main/.../catalog/impl/CatalogPropertyAccessor.java
REST 越权 src/restconfig/.../rest/catalog/AbstractCatalogController.java
已修复示例 src/main/.../config/util/SecureXStream.java
src/main/.../template/GeoServerMemberAccessPolicy.java
src/wfs-core/.../wfs/GetFeature.java(validateFilter)
报告完毕。本报告基于静态源码审计,未进行动态验证;标注为「条件性」的项受捆绑的 GeoTools 35.0 实际行为影响,建议在真实部署环境复测确认。