go标准库net/rpc不可用于生产rpc通信,因其默认无加密、无认证、无tls,明文传输、无身份校验、不支持双向证书、无超时限流且服务名暴露;生产环境必须使用grpc启用tls并强制双向认证,或在net/rpc上手动加tls与token校验,同时确保服务发现、地址解析、连接建立及方法调用全链路可信。

Go标准库的net/rpc默认不加密、无认证、无TLS,直接暴露在公网等于裸奔;生产环境必须用gRPC或自建TLS+鉴权层,否则连基础通信安全都不可控。
为什么net/rpc不能直接用于生产RPC通信
Go自带的net/rpc是教学级实现,不是为安全设计的:
- 所有请求和响应明文传输,中间人可直接读取/篡改
Args和Reply结构体字段 - 没有服务端身份校验机制,客户端无法确认连接的是真实服务端还是伪造节点
- 不支持客户端证书双向认证,无法限制哪些客户端能发起调用
- 没有内置超时、限流、重试策略,易被恶意连接耗尽goroutine资源
- 注册的服务名(如
"HelloService")会暴露在协议层面,可能被枚举攻击
用gRPC启用TLS并强制客户端证书验证
这是目前最主流、最可控的安全方案。关键点不在“配TLS”,而在“强制双向认证”:
- 服务端启动时必须设置
credentials.NewTLS(&tls.Config{ClientAuth: tls.RequireAndVerifyClientCert, ...}) - 客户端连接时需传入自己的
*tls.Certificate,且服务端CA证书必须提前加载进tls.Config.RootCAs -
protoc生成的stub不自动携带证书,需显式构造grpc.Dial时传入grpc.WithTransportCredentials - 避免混用
grpc.WithInsecure()——哪怕只在测试环境,也建议统一用TLS+自签名CA模拟
示例片段(服务端):
creds, err := credentials.NewServerTLSFromFile("server.pem", "server.key")
if err != nil {
log.Fatal(err)
}
server := grpc.NewServer(grpc.Creds(creds))
在net/rpc上手动加TLS和简单token校验
若因历史原因必须用net/rpc,只能自己补安全层,但要注意边界:
- 监听必须用
tls.Listen("tcp", ":1234", config)替代net.Listen,否则底层仍是明文TCP - 连接建立后,可在
rpc.ServeConn(conn)前做一次握手:读取首字节判断是否为合法token头,不匹配则conn.Close() - 不要在RPC方法里做鉴权——
net/rpc不提供上下文透传机制,无法把认证结果带入Hello方法参数 - 序列化仍用
gob,它不校验数据来源,攻击者若突破TLS层,仍可伪造任意结构体反序列化(存在潜在反序列化风险)
服务发现与通信链路中的隐性风险点
安全不止在传输层,容易被忽略的环节包括:
- 服务注册中心(如etcd、Consul)本身未启用TLS或ACL,导致服务地址被恶意篡改
- 客户端从注册中心拉取到服务端地址后,未校验其域名是否匹配证书中的
DNSNames,易受DNS污染劫持 - gRPC的
WithBlock()阻塞模式在DNS解析失败时可能无限等待,应配合WithTimeout和健康检查兜底 - 日志中意外打印
Args或ctx.Value,泄露敏感字段(如token、密码),需全局过滤或禁用结构体日志
真正难的不是配通TLS,而是让每个环节——从服务注册、地址解析、连接建立到方法调用——都保持身份可信且数据机密。少一环,整条链路就降级为“纸面安全”。











