跳转到帖子

背景

最近在做一轮内网资产梳理时,遇到一个比较典型的问题:某业务系统本身没有直接暴露到公网,但它依赖的管理后台、调试接口、监控页面散落在多个网段里,访问控制策略不一致。安全设备侧先是报了几条“未授权访问”告警,业务同学一开始认为只是误报,后续排查发现确实存在一批接口缺少鉴权或只依赖网络边界。

这类问题在内网很常见:系统上线多年,服务拆分多次,组件升级不完整,历史管理端口没有下线。单点漏洞未必严重,但一旦和弱口令、默认配置、日志泄露、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 暴露面缺少持续治理。很多团队在公网入口上投入较多,但对内网管理端、调试接口、组件控制台的风险感知不足。

比较务实的做法是:先把资产看清楚,再把敏感能力标出来,然后用统一鉴权和网络策略双层控制。安全巡检时不要只追求“发现漏洞”,更重要的是把问题沉淀成可执行的发布规范和检查清单。这样下一次系统拆分、迁移或组件升级时,才不会把同样的问题重新带回生产环境。

代码审计与漏洞定位示意图
代码审计、调用链与关键函数定位示意

0篇意见

推荐意见

没有意见。

游客
抱歉,你的帖子内容包括我们不允许的字词。请编辑你的帖子,删除下面高亮的屏蔽字。
添加意见…