发布日期: 2026-04-16
版本号: v1.17.5

Dapr 1.17.5 更新包含一项关键安全修复。该修复针对服务调用路径遍历可能绕过访问控制策略的问题:攻击者可通过编码的路径遍历序列、分隔符或空字符,在ACL策略中制造歧义,从而访问本应被拒绝的API端点(尤其是gRPC API)。根本原因在于方法路径在ACL检查和请求分发时被独立处理,导致两者不一致。修复方案是在服务调用边界统一进行路径规范化,确保ACL策略检查和实际请求使用完全相同的标准化路径,并移除了ACL层中的规范化处理。强烈建议用户升级到此版本。

更新内容 (中文)

Dapr 1.17.5

本次更新包含一项关键安全修复:

安全:服务调用路径遍历绕过访问控制策略

问题

服务调用方法路径中的保留URL字符和路径遍历序列可绕过访问控制策略。能够访问Dapr HTTP或gRPC API的攻击者,可以调用ACL(访问控制列表)配置为拒绝的目标应用程序操作。

影响

所有使用服务调用访问控制策略的部署均受影响。能够访问Dapr API(HTTP或gRPC)的攻击者可能:

  • 使用编码路径遍历(admin%2F..%2Fpublic)访问允许路径(/public),而方法起始于被拒绝的前缀(/admin)。
  • 使用编码的片段分隔符(%23)或查询分隔符(%3F)使ACL评估的路径与实际送达目标应用的路径不同。
  • 使用裸百分号(%)导致ACL标准化崩溃,可能完全绕过策略。

gRPC API是更危险的攻击向量,因为gRPC将方法作为原始字符串传递,无客户端URL清理——#?%../和控制字符均被原样传递。

根因

方法路径在两个地方被独立标准化:

  1. ACL使用purell.NormalizeURLString,将方法视为URL——解码%XX、解析../、将#作为片段分隔符和?作为查询分隔符进行去除。
  2. 调度层(HTTP的constructRequest、gRPC透传)使用原始方法字符串。

这导致了不匹配:ACL授权了一个路径,而目标应用程序接收的是另一个。例如,admin%2F..%2Fpublic被ACL标准化为public(允许),但目标应用程序接收的是原始路径admin/../public

解决方案

方法路径现已在服务调用边缘进行标准化——对于HTTP和gRPC公共API调用在directMessaging.Invoke中处理,对于gRPC内部调用在callLocalValidateACL中处理,对于代理调用在gRPC代理处理器中处理。标准化形式同时用于ACL检查和出站调度,消除了不匹配问题。ACL现在是纯粹的策略评估层,不再执行任何标准化操作。

对于HTTP,Go的net/http服务器在提取方法之前会对r.URL.Path进行百分号编码解码。对于gRPC,方法字符串是原始的(无百分号解码)且被视为不透明——%2F等百分号编码序列被视为字面字符而非路径分隔符。

标准化使用path.Clean解析../和重复斜杠,并拒绝包含#?、空字节或控制字符的方法路径。ACL路径中已移除purell依赖项。

作为纵深防御,HTTP通道中的constructRequest在构建出站URL前会对方法应用path.Clean

强烈建议用户升级到此版本。

更新内容 (原始)

Dapr 1.17.5

This update contains a critical security fix:

Security: Service invocation path traversal bypasses access control policies

Problem

Reserved URL characters and path traversal sequences in service invocation method paths could bypass access control policies. An attacker with access to the Dapr HTTP or gRPC API could invoke operations on a target application that the ACL was configured to deny.

Impact

Any deployment using access control policies for service invocation is affected. An attacker who can reach the Dapr API (HTTP or gRPC) could:

  • Use encoded path traversal (admin%2F..%2Fpublic) to reach an allowed path (/public) while the method started from a denied prefix (/admin).
  • Use encoded fragment (%23) or query (%3F) characters to cause the ACL to evaluate a different path than what was delivered to the target application.
  • Use a bare % to crash the ACL normalization, potentially bypassing the policy entirely.

The gRPC API was the more dangerous vector because gRPC passes the method as a raw string with no client-side URL sanitization — #, ?, %, ../, and control characters were all delivered literally.

Root Cause

The method path was normalized independently in two places:

  1. The ACL used purell.NormalizeURLString which treated the method as a URL — decoding %XX, resolving ../, and stripping # as a fragment delimiter and ? as a query delimiter.
  2. The dispatch layer (constructRequest for HTTP, gRPC passthrough) used the raw method string.

This created a mismatch: the ACL authorized one path while the target application received a different one. For example, admin%2F..%2Fpublic was normalized by the ACL to public (allowed), but the target application received the raw admin/../public.

Solution

The method path is now normalized at the service invocation edge — in directMessaging.Invoke for HTTP and gRPC public API calls, in callLocalValidateACL for gRPC internal calls, and in the gRPC proxy handler for proxied calls. The normalized form is used for both the ACL check and the outbound dispatch, eliminating the mismatch. The ACL is a pure policy evaluation layer and performs no normalization of its own.

For HTTP, Go’s net/http server decodes percent-encoding in r.URL.Path before the method is extracted. For gRPC, method strings are raw (no percent-decoding) and are treated as opaque — percent-encoded sequences like %2F are literal characters, not path separators.

Normalization uses path.Clean to resolve ../ and duplicate slashes, and rejects method paths containing #, ?, null bytes, or control characters. The purell dependency has been removed from the ACL path.

As defense-in-depth, constructRequest in the HTTP channel applies path.Clean to the method before building the outbound URL.

Users are strongly encouraged to upgrade to this release.

下载链接