做云原生安全评估这几年,我遇到最多的情况不是测不出漏洞,而是看着一切正常、实则风险已经埋下。API网关和服务网格就是最容易出现这种错觉的地方:前者是南北向流量的统一入口,后者是东西向通信的透明底座,两者叠加起来,几乎决定了整个架构的信任边界。但很多团队对这块的安全测试,还停留在扫端口、看版本、查公开CVE的层面,真正架构级的信任边界问题,往往被忽略了。
这篇文章想把我的完整思路从头到尾梳理一遍。内容包括:网关和网格的职能边界、攻防视角下的威胁建模方法、一套可复现的实验环境搭建,以及我在实际项目中反复遇到的六类高频架构级漏洞——它们是怎么产生的、怎么验证、怎么修。适合准备做云原生架构安全评估的安全工程师、计划加固基础设施的SRE,以及想弄明白“为什么配置安全那么重要”的微服务开发者。不夸张地说,读完你能少走我当年至少两个月的弯路。
1. 先弄清楚你在测什么:网关、网格与架构级漏洞
1.1 南北向与东西向:两类入口的职能边界
很多人把API网关和服务网格混在一个安全域里,这是个误区。它们的控制点不同,面对的威胁也不同。
API网关是南北向入口,站在外部调用方和内部服务之间。核心职能是协议转换、路由分发、认证鉴权、限流熔断。外部流量在它这一层被“收编”,所以它的安全职责非常明确:把不可信的东西挡在外面。相当于小区大门,进得来的人必须经过登记。
服务网格是东西向底座,负责服务到服务的通信。它通过在每个业务Pod旁边注入一个sidecar代理,接管服务间的所有HTTP/gRPC流量,实现超时、重试、灰度、mTLS、访问控制。它不直接面对外部用户,但它看到的流量几乎等于全部内部流量。相当于每栋楼内部的安保系统,楼里每个人去哪个房间它都清楚。
这个职能边界对漏洞挖掘的意义在于:攻击者如果想打穿体系,往往不会只盯着网关,而是会先找一个风险更低、更容易下手的内部服务作为跳板,再通过网格的信任链路横向移动。所以你测的不只是网关这一扇门,还要测门里面的走廊是不是全部可控。如果安全团队只把精力放在给网关打补丁,却对网格内部的开放策略不管不问,那等于大楼门口守得严严实实,楼内每层反而全透明的。
1.2 架构级漏洞和单点漏洞的区别
传统Web漏洞,比如SQL注入、文件上传、命令执行,关注的是单个应用自身的逻辑缺陷和代码实现问题。这类漏洞特征明显,配合扫描器就能批量发现,修复之后影响面通常可控。
但架构级漏洞的“病灶”不在某个函数的实现里,而在组件与组件之间的连接方式、信任假设、默认动作里。举个例子:网关通过JWT完成了用户认证,然后把用户角色写进X-User-Role请求头转发给下游服务。下游服务因为“网关已经验过”,就直接信任这个头。如果攻击者发现某个内部服务可以绕过网关被直接访问,他自己伪造一个X-User-Role: admin头,是不是就直接管理员了?这不是SQL注入那样的单点弱点,而是“认证逻辑发生了信任传导,下游没有进行二次校验”的架构级问题。
这类问题的特点是:
- 单靠扫描器发现不了,需要完整梳理数据流;
- 修复成本往往比想象中高,因为要同时改调用方、网关和接收方;
- 影响范围通常是整条链路,而不是一个接口。
用一个类比来说:单点漏洞是某扇门的锁坏了,架构级漏洞是整个门禁系统用了同一把万能钥匙。你修好一扇门没用,所有门都能用这钥匙打开。
1.3 动手前先回答的三个威胁建模问题
开始任何评估之前,我都会先在白板上画一张简单的关系图,回答下面三个问题:
第一个问题:谁可能是被攻陷的组件?外部攻击者可能要拿网关、暴露的管理接口;内部失陷服务可能是某个写着旧框架的业务Pod。每个候选组件,都要问一句:如果它被控制,能发出哪些合法请求?能触达哪些数据?
第二个问题:信任边界画在哪?网关和sidecar之间、sidecar和业务容器之间、控制平面和数据平面之间,分别有哪些信任假设?比如“sidecar转发过来的流量都可信”就是最常见的危险假设。
第三个问题:配置和数据是否可被篡改?网关、网格的配置本身就是攻击面。谁能修改路由规则?谁能下发证书?这些修改有没有审计?权限最小化做到了吗?
这三个问题回答完,评估范围就清晰了。后面的所有测试动作,都在这个范围里展开,既不盲目,也不漏项。
2. 从入门开始:搭建一套可复现的实验环境
2.1 环境拓扑与组件选型思路
如果你想真正理解架构级漏洞,强烈建议别直接用云托管的网关和网格产品,而是自己在本地搭一套最小环境。托管产品把底层细节都封装了,排查问题时隔着一层黑盒子,很难建立直觉。
我这边的建议拓扑是这样的:一套轻量级容器集群,里面跑一个企业级API网关,再部署一套开源服务网格,把网格的数据平面代理接入到两个模拟业务服务上。外部流量路径是“测试客户端 → API网关 → 虚拟服务路由规则 → sidecar代理 → 业务Pod”。这套环境在普通开发机上就能跑起来,资源占用不高,完全可以满足后续所有实验。
组件选型就两个原则。第一,选社区活跃、资料多的项目,后续查问题容易。第二,优先选你能看到内部配置的项目——配置能不能导出、能不能实时查看,直接决定了你能不能做架构审计。如果选了个纯黑盒的商业网关,很多实验就没法做了。
2.2 让安全现状“显现”出来:日志、配置与基线流量
环境搭好之后,第二步不是急着找漏洞,而是把观测手段先建起来。我见过太多评估做到一半才发现日志没开、配置导不出来,只能靠猜,效率极低。
需要准备三样东西:
- sidecar代理的访问日志。很多网格默认不打印访问日志,导致流量来没来过、被谁拒绝了,完全不可见。必须先在网格配置里打开访问日志,并确认每条请求都会留下来源、目标、动作、响应码。
- 网格控制面的配置导出能力。把“当前生效的路由规则、授权策略、目标服务定义”整体导出来,作为后续审计的基线。
- 一套手工基线流量。写几行脚本,分别模拟合法请求、未认证请求、错误签名的请求,并记录正常响应长什么样。基线有了,后面测试时才能立刻看出差异。
观测手段没建好的时候,我先不发一个测试请求,因为就算结果不对,也定位不了问题在哪一层。
2.3 刻意制造不同信任层级的模拟业务服务
实验环境里如果所有服务都“绝对安全”,你什么都学不到。我会故意把模拟业务服务设计成几种不同的信任等级:
第一个服务严格执行JWT认证,每次请求都校验签名和有效期,模拟成熟团队的标准实现。
第二个服务模拟“已经信任了网关”的典型写法,直接读取请求头里的用户角色做权限判断,自己不校验签名。这种做法在真实项目里非常常见,也是后面讲信任传导时的关键靶点。
第三个服务故意暴露一个内部管理接口,没有做外部网络隔离,模拟那种“以为只有内网能访问就懒得鉴权”的历史债务服务。
三个服务放在一起,等于在环境里划出了三个信任层级。评估时就能清晰对比:同样一个攻击请求,打到不同信任层级的服务上,结果天差地别。这种对比,比单测一个“安全”环境有教育意义得多。
3. 六类高频架构级漏洞的成因与验证思路
3.1 控制平面与数据平面之间的信任边界问题
服务网格架构里最核心的信任关系,就是控制平面和数据平面之间的信令通道。控制平面负责计算并下发配置,数据平面侧的每个代理都把这些配置当成“真理”来执行。用“大脑指挥手脚”来理解就够了:只要大脑说的话被当成无条件真理,大脑出了问题,手脚就会完全失控。
这个信任边界的典型风险点有三个:
第一,控制平面的调试接口暴露范围过大。很多网格控制面默认开启调试和发现端口,这些端口如果可以通过网络访问,攻击者就可能用伪造身份向控制面查询集群信息,甚至进一步尝试下发配置。验证方法很简单:在集群外或非管理网段访问控制面的调试端口,看是否有信息泄露或未鉴权的响应。
第二,控制面和数据面之间缺乏双向身份认证。理论上代理应该只接受来自合法控制面的配置。如果这条链路没做双向TLS,在同一网络里伪造一个代理身份去订阅配置下发,就等于进入了一个IKEA式的“自助组装”流程——你要什么配置,它就给你下发什么。
第三,配置下发的全链路审计缺失。规则被改过、被回滚、被异常推送,但没有任何日志记录。评估时我一般会在控制面配置里打开审计,再人为改动一条规则,确认审计记录完整。
这类问题的核心修复方向是:把控制面的端口收回到最小网络可达范围,强制控制面与数据面的双向TLS,为配置变更增加审计和告警。
3.2 网关与sidecar的路由解析差异
这是我在真实评估里命中率最高的一类问题,也是典型的“配置语义”漏洞:网关和网格对同一个URL的理解不一致,导致同一请求在不同层走到了不同规则。
具体成因是:API网关、sidecar代理和业务应用,各自对URL路径做规范化处理的算法可能是不同的。有的组件会解码%2e%2e为..,有的不会;有的组件会把/v1/api//admin压缩成/v1/api/admin,有的不会;有的组件把;当普通字符,有的当成参数分隔符。正常请求下这些差异没用,但攻击者构造URL变体时,差异就成了绕过拦截的缝。
我举一个最经典的场景:网关侧有一条路由规则,把所有/admin前缀的请求转发到管理后台,并且做严格鉴权。但网关对URL路径的处理和网格不完全一致。攻击者构造/admin/../user/info或者/%61dmin/info这类变体时,网关可能认不出来是管理路径,直接放行,而网格和业务服务却把路径还原成了/admin。结果就是:网关以为自己在放行普通请求,网格和业务实际在处理管理请求,鉴权形同虚设。
验证这类问题不需要复杂工具,就是构造一批URL变体,请求打到网关,再对比三层各自看到的“规范化后的路径”。如果发现某条变体在网关是A路径、在业务是B路径,且A路径没有鉴权保护,那就是一条可以记录的架构级风险。
修复思路是保证整个链路所有组件对路径的规范化语义完全一致,并且在网关这层对所有路径变体做归一化,统一后再做路由和鉴权。
3.3 JWT认证链路中的信任传导风险
JWT本身并不是一个很难实现的技术,但它在微服务架构里的信任链路特别容易被打破。最常见的错误,就是把JWT当成“内部通行证”来传导信任,而不是当成“加密的令牌”来独立验证。
先说说几个典型的JWT自身校验绕过思路:
- 签名算法降级。JWT默认支持多种签名算法,如果服务端算法白名单没配置好,攻击者可能把
RS256改成HS256,让服务端用公钥当作HMAC密钥来验签,自己用公钥就能伪造合法令牌。这是JWT领域最经典的问题。 - 算法为
none。有些实现保留了空签名算法,攻击者去掉签名部分,只保留Header和Payload,服务端可能就认了。 iss/aud校验缺失。服务端只验签不验“签发给谁”,攻击者在一个服务里拿到的合法令牌,直接拿去调另一个服务,跨服务横向越权。
但上面这些还是在“JWT采用环节”里的问题。架构级问题更常出在“JWT验证结果被转化为信任头”这一步:网关验完JWT后,把用户ID和角色塞进请求头转发给下游;下游不验签,直接信任请求头。攻击者的思路立刻就转向“绕网关直连下游服务”或者“内网服务间互相调用时直接伪造头”,根本不需要破解任何签名。
评估时我会重点看两类证据:一是下游服务是否信任外部可控的认证头;二是是否有网络路径可以绕过网关触达下游服务。只要这两个条件同时满足,无论JWT本身多安全,整条链路的认证就不成立。
修复方向是:网格内部服务之间也要强制验签,或者至少把认证头改成内部专用名称并在入口处剥离外部同名请求头,同时在网络层阻断绕过网关直连服务的路径。
3.4 授权策略的默认动作与优先级陷阱
服务网格的授权模型,表面看是“允许谁、拒绝谁”,但实际踩坑的地方在“规则之外的请求去哪里了”。很多团队把精力花在写几条ALLOW规则上,却忽略了默认策略是允许还是拒绝——这直接决定了整个授权体系的可靠性。
举个典型场景:某服务为了安全,自定义了一条规则,拒绝访问/admin路径的请求。这条规则确实生效了,但团队忘了配置“默认拒绝所有未显式允许的请求”。结果攻击者访问/admin-backup、/admin-console这些不在规则路径列表里的接口时,因为默认是全放行,一律通过。你以为自己写了“防火墙”,实际只是给某个门贴了张“此门关闭”——其他门全敞着。
另一个常见问题是多条规则的优先级语义混乱。有些授权系统里“拒绝”优先级高于“允许”;有些系统则是按规则顺序先到先得。如果团队没吃透这个语义,就可能出现:全局有一条放行规则,后面又加了一条自定义拒绝规则,结果放行规则永远先命中,自定义拒绝根本轮不到执行。
验证方法很直接:整理出该命名空间下全部生效的授权策略,找出三条关键信息——默认动作是什么、规则顺序如何、路径匹配是否覆盖了完整路径空间。然后针对每一个“未匹配到显式规则”的路径,实际发一个请求看是否被放行。
我的加固建议永远都是同一句话:默认拒绝,显式允许。任何白名单之外的路径、方法、来源,全部拒绝,然后逐条把“确实要放行”的接口加到规则里。这样就算规则写得有遗漏,网络边界也是收敛的。
3.5 外部可控字段引发的配置注入问题
API网关和网格里的很多扩展能力,比如自定义插件、响应头改写、日志模板、动态上游选择,都会接收外部可控字段。一旦这些字段被直接拼接到配置模板或指令里,就可能出现“配置注入”类的效果。
举个生活中常见的例子:网关提供了自定义日志模板功能,管理员把模板配置成request from {{client_ip}} to {{upstream}}。如果实现逻辑是先拼接字符串再渲染模板,那么client_ip或upstream里包含的模板语法就会被解析执行。外部攻击者虽然控制不了自己的IP,但可能有办法控制Host头、某个重定向参数、或某个上游地址字段,把它变成模板语法的payload,最后在网关进程里被渲染。
上游外部可控不仅影响日志,还可能影响转发行为。有些网关支持“根据请求头动态选择后端集群”,例如通过X-Backend头指定目标服务。如果这块没有白名单校验,攻击者就可能把流量转发到任意内网地址,变成一次架构级的SSRF。
评估思路是把所有“外部数据进入配置或指令”的位置找出来,画一条清晰的数据流:来源字段在哪里,拼接进什么位置,最后以什么方式执行。只要链条上任何一个环节没有做输入校验和输出编码,就可能存在问题。
这类问题的修复重点不是给某个字段加过滤器,而是在设计上减少甚至禁止外部字段进入模板和路由选择逻辑。能用白名单映射的,就不要用字符串拼接。
3.6 TLS与明文回退的边界问题
服务网格的mTLS是保护内部流量的重要手段,但我见过不止一个环境,mTLS配置是“宽容模式”,全链路明文和加密流量同时接受。这种模式的初衷是方便灰度迁移,结果长期不收紧,就等于内部走廊装的是单向玻璃——你从里面看外面很清楚,外面看你也一清二楚。
具体风险点有几个:
- 网格允许明文回退。攻击者只要在目标服务同一网络里伪造一个普通客户端发起明文HTTP请求,就可能绕过加密信道的完整性保护,甚至配合中间人手段,把原本加密的流量降级成明文再读取。
- 证书签发信任链问题。网格里的证书由控制面统一签发,如果签发服务的访问控制太弱,攻击者可以为自己想要的服务身份申请证书,从而伪装成任意服务跟其他服务通信。这就像一个门禁系统,只要你能伪造工作牌,就能进任何房间。
- 网关TLS终止点选择不当。网关对外部终止TLS后,到网格内部的这一段如果被错误地当成“已经安全”,又放松了mTLS校验,那中间任何一跳都可能成为明文暴露点。
验证这类问题,要做的不是“能不能看到加密流量”,而是观察双向TLS认证是否被强制、是否严格拒绝了明文回退。我会直接用一个绕过sidecar的普通客户端,试着用明文HTTP访问启用了mTLS的服务,看服务端是接受还是拒绝。如果接受了,说明网格内网的信任模型已经被削弱了一半。
加固方向很明确:把mTLS从宽容切到严格模式,并确保所有服务身份证书的签发动作都有权限控制和审计。
4. 实操过程:从信息收集到规则验证的完整链路
4.1 信息收集:从“配置面”入手而非直接打端口
评估第一步,我基本不会直接发恶意请求,而是先做配置面信息收集。这一步的价值在于:弄清楚“系统认为自己在做什么”,是后面所有测试的基准。
收集清单包括:
- 集群里有哪些命名空间、哪些工作负载、每个工作负载是不是都注入了网格代理;
- 网关和服务网格当前生效的路由规则、目标服务定义、授权策略、端口监听列表;
- 控制面注册发现的调试端口在当前环境中是否可以访问;
- 服务账号与访问权限的绑定关系,特别是哪些角色可能具备修改网格配置的权限。
实际操作上,我会优先使用平台命令行工具把上述内容导出为文件,然后像读代码一样慢慢审计,而不是在集群里敲几条查询命令就完事。导出的配置我会保存为基线,后续测试如果改了配置,第一时间diff就能发现。
这里有个经验:所有安全评估,都要从回答“上帝视角下系统长什么样”开始。你以为的拓扑和实际生效的拓扑往往存在偏差,而偏差本身就是漏洞的藏身之处。
4.2 路由绕过测试:路径规范化矩阵
在基线配置跑通之后,第二组实操动作就是构造一批URL变体,逐条打向入口,观察网关、sidecar和业务服务各自解析出来的路径。这里的关键不是“请求被拒绝”或“请求成功”,而是“各层看到的路径是否一致”。
常见的测试变体包括:
| 变体类型 | 示例 | 可能造成的影响 |
|---|---|---|
| 编码点号 | /v1/..%2f..%2fadmin | 网关不还原,业务还原,鉴权失效 |
| 分号参数 | /v1/admin;foo=/user/info | 网关视为普通路径,业务截断参数 |
| 超长路径 | /v1///admin//// | 各层压缩逻辑不同导致匹配差异 |
| 大小写切换 | /v1/Admin/info | 部分组件大小写敏感,部分不敏感 |
| 绝对路径混入 | //v1/admin | 解析器可能重路由到其他主机 |
每批请求发完之后,我都会对比三份数据:网关访问日志里记录的原始请求、sidecar访问日志里记录的路由路径、业务服务收到的实际路径。如果出现差异,就把这条差异记录成一个待验证风险点。
这一步要耐心,批量发几十种变体是常态。一次测试就能发现两三个差异,是很正常的。
4.3 最小化规则篡改实验:可复现、可回滚
当信息收集确认“当前角色具备修改路由或授权策略的权限”时,我会做一个最小化的规则变更实验,来验证配置层被篡改的影响范围。这里有一条铁律:所有实验必须在专用的测试环境里进行,并且每一步必须可逆。
具体做法是:先把当前生效的配置完整导出备份,然后修改一条不重要的路由规则,比如把某个测试服务的一部分流量复制一份发往一个“记录服务”,用于确认“配置变更能够实际影响数据面转发”。确认完成后,立即把配置恢复为备份版本,并再次导出配置做diff,确保零残留。
这个实验的价值在于证明一件事:如果攻击者获得了配置层权限,他不是只能看点信息,而是可以直接操纵流量的走向。流量走向被操纵,意味着可以做到:把敏感接口的响应复制出去、把某服务的访问全量拦截、把认证规则临时放宽。这些效果,比单独打穿某个服务要严重得多。
我不会在这个实验里做任何影响其他服务可用性的操作,也不会劫持真实业务流量。所有变更都限定在测试用的虚拟服务和记录服务之间。因为安全评估的目标是验证风险、推动修复,不是制造事故。
4.4 从入口到内部:横向触达与影响范围确认
最后一步实操,是把前面发现的问题串起来,确认一条完整的攻击路径能走多远。这一步我不追求打穿所有链路,而是只要验证“从一个低信任入口可以触达高价值内部目标”,就已经足够说明问题严重。
常见的验证路径有两类:
第一类是绕过网关直连内部服务。如果某个业务服务的网络策略没有限制来源,攻击者绕开API网关,直接访问服务在集群内部暴露的实例IP,再用伪造的认证请求头试一下下游是否信任。只要响应不再是401/403,就说明认证信任链已经可以被绕过。
第二类是通过网关的开放能力向内探测。如果网关支持动态上游或者重定向,我可以把它当作探测工具,请求一批内部服务名,看哪些能返回网段信息。比如请求http://网关/内部服务名/health,如果响应里带出内部版本号、节点信息,就说明入口组件具备了向内探测的能力。
这两类验证做完,一般就能画出一条“入口 → 内部服务 → 敏感数据”的完整链路图。这份链路图,就是推动管理层重视问题的关键证据。
5. 常见误判、高价值攻击面与加固建议
5.1 三个容易误判的地方
第一个误判:开启mTLS就等于内部流量安全。我前面提过宽容模式的例子。实际环境里,很多网格为了兼容老服务,长期跑在宽容模式下,明密文同时放行。有些团队做了评估,看到“mTLS已开启”就直接判安全,完全没发现里面有大量明文请求在裸奔。验证时一定要区分是严格模式还是宽容模式,宽容模式等于一半没穿防弹衣。
第二个误判:只测网关入口就等于测完了全部。网关只是第一道门。攻击者可以通过内部失陷服务、暴露的负载均衡IP、乃至一个运维跳板直接触达后端Service。如果安全测试只盯着入口,内网横向路径基本是睁眼瞎。评估范围必须覆盖“入口到服务”“服务到服务”两层。
第三个误判:没有异常日志就等于没有风险。很多环境的sidecar访问日志默认是关的,控制面的配置变更也无审计。测试时你发了大量恶意请求,控制台没有任何告警,于是你觉得“没问题”——实际是“看不到问题”。安全评估的前提是可见性,先解决日志和审计缺失,再谈漏洞挖掘。
5.2 高价值攻击面速查表
| 攻击面 | 风险点 | 为什么高价值 |
|---|---|---|
| 控制面调试端口 | 未鉴权即可查询集群信息 | 一次请求摸清全部拓扑 |
| 控制面配置修改权限 | 可篡改路由和授权规则 | 直接操纵数据面全部流量 |
| 服务账号令牌 | 大权限角色可能具备集群管理能力 | 横向提权的关键材料 |
| sidecar管理接口 | 可查看配置、证书、运行时状态 | 泄露内部服务和证书信息 |
| 配置热更新Webhook | 输入校验不足可能触发拒绝服务或注入 | 变更入口比静态规则更容易绕过 |
| 网关自定义插件执行环境 | 外部字段进入模板或指令 | 配置注入和内部探测的起点 |
这张表每次做评估都会用到。它代表我在实际项目中碰过的、最容易翻车的位置。你可以把它当成“架构评估前的默认检查清单”,逐项确认,再往下深入。
5.3 落地加固建议
前面讲了这么多漏洞,最后还是要落到怎么修。我的建议按优先级排序:
第一,强制mTLS严格模式,切断明文回退通道。这一步是基础中的基础,会让攻击者在内部网络的观测成本大幅提高。
第二,控制面最小化暴露。调试接口只在故障时开放,并且必须加认证;所有配置变更操作必须双人复核并记录审计日志。
第三,授权模型统一为“默认拒绝+显式允许”。同时定期用自动化脚本检查“未匹配规则的路径”是否真的被拒绝。
第四,对外部可控请求头做白名单过滤。认证信息必须使用内部专用头,并在入口处剥离外部同名头。
第五,同步路径解析语义。网关、网格、业务三层对路径规范化必须保持一致,把不同组件能处理同一URL变体纳入CI回归测试。
第六,配置版本化。把当前生效的网关和网格配置纳入代码仓库管理,定期diff实际配置与仓库配置,有差异就告警。
这套东西做下来,不敢保证绝对攻不破,但能让攻击者的成本明显上升。安全从来不是追求不可入侵,而是追求对方觉得“打这里不划算”。
最后再说点实操体会
整个流程走下来,我最大的体会是:架构级漏洞挖掘,本质上不是找某个单一组件的bug,而是在找组件与组件之间的信任落差。网关信任了请求头,下游就危险;控制面信任了网络环境,配置就会被劫持;网格信任了默认动作,白名单就是摆设。每一条链路里都藏着一句“默认情况下它是安全的”,而这句默认假设,恰恰是要被反复质疑的东西。
另外有个细节我始终强调:配置一定要版本化。评估时我几乎必做的一件事,就是把当前生效的配置和代码仓库里提交的版本做diff。很多团队改配置不提交,导致你测试时看到的规则和负责人以为的规则根本不是一回事。配置可追溯、可回滚,比你在任何单一漏洞上打补丁都管用。这个习惯我从第一次踩坑之后就一直保留,现在也推荐给你。