paseto不能替代tls,因其仅为应用层身份令牌而非传输层加密方案;它只解决“谁在调用”和“有何权限”,不提供信道机密性、完整性、前向保密或防重放能力,必须与tls配合使用。

Paseto 不能直接替代 TLS,它不是传输层加密方案,而是应用层令牌格式;想用它做“服务间私有通信加密凭证”,必须明确它是用于身份断言(如 RPC 调用时携带的 bearer token),而非加密通道本身。
为什么不能把 Paseto 当成 TLS 用
常见误解是拿 Paseto 替代 tls.Config 或 credentials.NewTLS——这会导致明文传输、中间人可截获、签名可重放。Paseto 本质是结构化 token(类似 JWT,但设计更克制),只解决「谁在调用」和「附带哪些声明」,不提供信道机密性或完整性保护。
- 它不加密 payload(除非显式启用
encrypt模式,但该模式需共享密钥,不适合服务间动态协商) - 它不绑定连接上下文(比如无法防重放,除非你手动加 nonce + 时间窗口)
- 它不验证对端证书、不协商密码套件、不提供前向保密(PFS)
服务间用 Paseto 的典型位置:gRPC 的 Per-RPC Credentials
真正落地场景是配合 TLS 已启用的前提下,在每次 RPC 调用中附加可信身份凭证。Go 官方 credentials.PerRPCCredentials 接口就是为此设计的。
- 服务端必须先跑在
grpc.Creds(credentials.NewTLS(...))之上(即已有双向 TLS 或至少服务端认证) - 客户端实现
GetRequestMetadata方法,返回map[string]string{"authorization": "Bearer xxx"},其中xxx是 Paseto token - 服务端拦截器解析该 token,校验
footer、aud、exp,并映射到具体服务权限(如service-a@corp.internal)
示例关键片段:
func (c *pasetoAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
token, err := c.maker.CreateToken("service-a", time.Hour)
if err != nil {
return nil, err
}
return map[string]string{
"authorization": "Bearer " + token,
}, nil
}
Paseto 密钥管理必须区分本地与远程服务
服务间通信若跨信任域(如不同团队、不同 Kubernetes 命名空间),绝不能共用同一对密钥。Paseto 的安全模型依赖密钥隔离:
-
public模式(推荐):服务 A 用私钥签名,所有下游服务用对应公钥验证——此时每个服务应有独立密钥对,且公钥通过 ConfigMap/Secret 分发,而非硬编码 -
local模式(仅限同信任域内短生命周期通信):所有服务共享一个 AES 密钥,但该密钥必须通过 KMS 或 Vault 动态获取,不可写死在代码或镜像中 - 密钥轮换时,服务端需支持多版本公钥并存(例如同时加载
paseto.pub.v1和paseto.pub.v2),避免滚动更新期间请求失败
容易被忽略的校验点:footer、aud、iss 和时间窗口
Paseto token 默认不强制校验 footer 字段,但服务间通信必须启用——它可用于嵌入服务实例标识(如 Pod UID)、签名时间戳或 nonce,防止 token 被跨服务复用。
-
aud必须精确匹配接收方预期值(如"payment-service"),不能是宽泛的"backend" -
iss应为颁发方唯一标识(如"authz-gateway.corp.svc.cluster.local"),服务端需白名单比对 -
exp和iat必须校验,且建议服务端额外限制最大有效期(如 ≤ 5 分钟),避免长期有效 token 泄露后危害扩大 - 务必禁用
none算法(Paseto 不支持,但某些旧库封装层可能留后门)
真正难的不是生成 token,而是让每个服务在收到 token 后,能快速、确定地判断「这个 token 是谁签的、能访问什么、是否已被撤销」——这需要配套的密钥分发机制和轻量级吊销检查(如 Redis 缓存已知失效 jti)。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











