背景
最近在做一轮内网资产梳理时,遇到一个比较典型的问题:某业务系统本身没有直接暴露到公网,但它依赖的管理后台、调试接口、监控页面散落在多个网段里,访问控制策略不一致。安全设备侧先是报了几条“未授权访问”告警,业务同学一开始认为只是误报,后续排查发现确实存在一批接口缺少鉴权或只依赖网络边界。
这类问题在内网很常见:系统上线多年,服务拆分多次,组件升级不完整,历史管理端口没有下线。单点漏洞未必严重,但一旦和弱口令、默认配置、日志泄露、SSRF 等问题组合起来,风险会被放大。
本文不讨论攻击利用链,重点放在如何识别、验证和治理内网 Web 服务暴露面,适合作为内部安全巡检或代码审计前的检查清单。
问题场景
这次告警涉及三类资产:
- 业务管理后台:部分接口只校验前端页面登录状态,后端接口缺少统一鉴权。
- 中间件控制台:历史部署遗留,访问路径不明显,但端口对办公网开放。
- 监控与调试接口:如健康检查、指标、线程栈、配置查看等接口,缺少访问限制。
表面看都是“内网可访问”,但实际风险并不低。尤其是微服务体系里,一个低权限入口可能暴露服务名、配置项、内部地址、队列 Topic、数据库连接标识等信息,为后续横向风险评估提供线索。
技术分析
未授权访问通常不是单一成因,常见问题可以分为几类。
1. 鉴权逻辑分散
很多系统在早期只做了页面级登录校验,例如访问 /admin/index 时会检查 Session,但后端 API 如 /api/admin/user/list 没有接入统一鉴权中间件。前端路由隐藏按钮并不等于后端安全控制。
代码审计时可以重点看这些位置:
- Controller 层是否存在未标注鉴权注解的管理接口。
- Filter、Interceptor、Middleware 的路径匹配是否覆盖完整。
- 是否存在
excludePathPatterns、白名单、调试开关导致的放行。 - 接口是否仅依赖前端传入的用户 ID、角色 ID、租户 ID。
例如在 Spring 项目里,常见风险配置类似下面这种,白名单写得过宽:
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(
"/login",
"/logout",
"/static/**",
"/api/**/public/**",
"/actuator/**"
);这里 /actuator/** 是否应该全部放行,需要结合实际端点判断。健康检查可以开放给探针,但环境变量、线程、日志级别等敏感端点不应对普通网段开放。
2. 只依赖网络边界
“只在内网访问”经常被当作免鉴权理由,但在真实环境中,办公网、测试网、容器网、VPN 网段之间的边界并不总是清晰。只要一个入口点暴露过宽,就可能导致原本受限的管理功能被访问。
建议把内网服务至少分成几类:
| 类别 | 示例 | 建议控制 |
|---|---|---|
| 业务接口 | 订单、用户、配置查询 | 统一身份认证、权限校验、审计日志 |
| 管理后台 | 用户管理、任务调度、开关配置 | 强鉴权、最小权限、来源限制 |
| 运维接口 | 监控、健康检查、日志级别 | 仅对运维网段或探针开放 |
| 调试接口 | Debug、Mock、Swagger、测试回调 | 生产禁用或强限制访问 |
3. 调试与文档接口未收敛
Swagger、Actuator、Druid Monitor、Job Console、GraphQL Playground、内部 API 文档等工具本身不是漏洞,但如果在生产环境未做控制,会把接口结构、参数、响应字段完整暴露出来。
检查时可以从路径和响应特征入手,但不要对非授权目标做扫描。内部自查可参考以下思路:
# 仅用于授权范围内的资产自查
# 从资产清单中读取 host:port,检查常见敏感路径是否存在
while read target; do
echo "[*] $target"
for path in "/swagger-ui/" "/v3/api-docs" "/actuator" "/druid/index.html" "/graphql"; do
code=$(curl -sk -m 3 -o /dev/null -w "%{http_code}" "
if [ "$code" != "404" ] && [ "$code" != "000" ]; then
echo " $code $path"
fi
done
done < assets.txt这类脚本的价值不是“扫出越多越好”,而是帮助安全和运维确认哪些端点需要纳入访问控制和监控。
关键步骤
步骤一:整理可信资产清单
不要只依赖 CMDB。实际巡检时建议综合以下来源:
- CMDB 或云资产 API。
- Kubernetes Ingress、Service、Endpoint 信息。
- Nginx、网关、负载均衡配置。
- Prometheus Target、服务注册中心。
- 代码仓库中的部署配置、端口配置、Context Path。
如果资产清单不完整,后续鉴权治理会反复漏项。
步骤二:按功能标记接口风险等级
对 Web 服务不要只记录 IP 和端口,最好补充功能标签:
- 是否涉及用户数据。
- 是否可以修改配置或触发任务。
- 是否可以查看日志、环境变量、线程栈。
- 是否连接生产数据库或消息队列。
- 是否存在测试、Mock、Debug 能力。
风险等级不是为了做报表,而是为了决定修复优先级。比如一个只能返回 UP 的健康检查端点,优先级通常低于一个可查看配置项的管理端点。
步骤三:验证鉴权是否在服务端生效
验证时建议直接请求后端接口,而不是只看页面是否能打开。常见检查点包括:
- 无 Cookie、无 Token 请求是否返回 401 或 403。
- 低权限账号访问高权限接口是否被拒绝。
- 修改用户 ID、租户 ID、组织 ID 是否越权。
- OPTIONS、HEAD、POST 与 GET 的鉴权逻辑是否一致。
- 网关层和应用层鉴权是否存在不一致。
一个简单的判断方式是保存同一接口在不同身份下的响应状态码、响应长度和关键字段,避免只凭页面现象判断。
# 授权环境内验证示例:比较匿名与登录态响应差异
curl -sk -i " | head
curl -sk -i " \
-H "Authorization: Bearer <test_token>" | head注意测试账号应使用专门创建的低权限账号,避免误操作生产数据。
步骤四:从代码侧确认统一控制点
配置层修补可以快速止血,但长期还是要看代码里有没有统一控制点。以常见 Web 框架为例:
- Java/Spring:关注 Filter、Interceptor、Spring Security 配置、注解权限校验。
- Go/Gin:关注 Router Group 中间件是否覆盖管理路由。
- Node/Express:关注 app.use 顺序、路由挂载顺序、异常分支。
- Python/FastAPI:关注 Dependency 是否统一注入,文档接口是否生产关闭。
比较推荐的方式是“默认拒绝,显式放行”。公开接口需要明确标注,管理接口默认要求鉴权和权限点。
// 示例:伪代码,表达思路
func AuthRequired() Middleware {
return func(ctx Context) {
user := ParseToken(ctx.Request)
if user == nil {
ctx.Abort(401)
return
}
ctx.Set("user", user)
ctx.Next()
}
}
admin := router.Group("/admin")
admin.Use(AuthRequired())
admin.Use(PermissionRequired("admin"))步骤五:网关和网络层做第二道约束
应用鉴权是第一道控制,网络和网关策略适合作为补充。对于管理端和运维端点,建议做来源限制,至少不要对办公大网段无差别开放。
# Nginx 示例:仅表达访问控制思路
location /actuator/ {
allow 10.20.30.0/24;
deny all;
proxy_pass
}
location /admin/ {
auth_request /auth/check;
proxy_pass
}如果使用 Kubernetes,可以配合 NetworkPolicy、Ingress 白名单、ServiceAccount 权限收敛,避免服务在集群内部被任意访问。
注意点
- 不要把隐藏路径当成安全措施。路径复杂只能降低被发现概率,不能替代鉴权。
- 不要只修入口页面。真正敏感的是后端接口、批量导出、配置修改、任务触发等能力。
- 不要在生产环境长期保留调试端点。确实需要时,应限制来源、加鉴权、记录审计。
- 不要忽略只读接口。只读接口也可能泄露内部拓扑、凭据片段、业务数据。
- 不要把 403、401 统一改成 200 加错误码后就算修复。网关、监控和安全设备往往依赖标准状态码判断。
- 不要让白名单无限膨胀。每个放行路径都应有负责人、用途和下线时间。
修复建议
针对这类未授权访问问题,建议按短期止血和长期治理拆开做。
短期止血
- 对敏感管理端点增加网关鉴权或来源限制。
- 关闭生产环境不必要的 Swagger、Debug、Mock、测试回调接口。
- 对 Actuator、Druid、任务调度后台等组件收敛访问范围。
- 检查默认账号、弱口令和历史共享账号。
- 开启访问日志,重点关注匿名访问管理路径的行为。
长期治理
- 建立统一鉴权中间件,避免每个接口各写一套判断。
- 权限模型细化到功能点,而不是只有“已登录”和“管理员”。
- 接口上线前加入安全检查项,包括鉴权、越权、敏感字段、审计日志。
- 把常见敏感路径检查纳入 CI 或发布前巡检。
- 定期从资产清单反查服务负责人,清理无人维护的历史服务。
复盘结论
这次问题本质不是某个单点配置失误,而是内网 Web 暴露面缺少持续治理。很多团队在公网入口上投入较多,但对内网管理端、调试接口、组件控制台的风险感知不足。
比较务实的做法是:先把资产看清楚,再把敏感能力标出来,然后用统一鉴权和网络策略双层控制。安全巡检时不要只追求“发现漏洞”,更重要的是把问题沉淀成可执行的发布规范和检查清单。这样下一次系统拆分、迁移或组件升级时,才不会把同样的问题重新带回生产环境。
推荐意见