作者:pluto 更新时间:2026-04-17
摘要
VMess(V2Ray Message)是由 Project V 原创设计的加密代理协议,旨在对抗深度包检测(DPI)。本文从协议设计目标、数据结构、加密机制、认证流程、安全性分析及与同类协议的横向比较等维度,对 VMess 协议进行系统性研究。
1. 背景与设计目标
VMess 协议诞生于 V2Ray 项目(Project V),最初发布于 2015 年前后,其核心设计目标是:
- 对抗基于特征的流量识别(如 GFW 的深度包检测)
- 提供端到端的加密通信
- 支持多种传输层协议(TCP、WebSocket、HTTP/2、gRPC 等)
- 无状态设计,客户端与服务端无需握手即可直接传输数据
VMess 是一个基于 TCP 的应用层协议,所有数据均通过 TCP 传输。协议采用非对称格式,即客户端请求与服务端响应使用不同的数据结构。
2. 基础概念
2.1 用户身份标识(User ID)
VMess 使用 UUID(Universally Unique Identifier)作为用户身份标识,UUID 是一个 16 字节的随机数,格式如下:
de305d54-75b4-431b-adb2-eb6b9e546014
UUID 的功能等同于预共享令牌(Pre-shared Token),客户端与服务端通过 UUID 派生出用于认证和加密的密钥材料。
2.2 CmdKey
CmdKey 是从用户 UUID 派生的 16 字节密钥,用于 AEAD 认证模式下的头部加密:
CmdKey = MD5(UUID.Bytes() + []byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))
2.3 密钥派生函数(KDF)
VMess AEAD 模式使用基于嵌套 HMAC-SHA256 的 KDF:
KDF(key, path...):
hmac_creator = HMAC(SHA256, "VMess AEAD KDF")
for each p in path:
hmac_creator = HMAC(hmac_creator, p)
return hmac_creator(key)
KDF 的盐值常量(Salt Constants)如下表所示:
| 常量名 | 值 |
|---|---|
| KDFSaltConstVMessAEADKDF | "VMess AEAD KDF" |
| KDFSaltConstAuthIDEncryptionKey | "AES Auth ID Encryption" |
| KDFSaltConstVMessHeaderPayloadAEADKey | "VMess Header AEAD Key" |
| KDFSaltConstVMessHeaderPayloadAEADIV | "VMess Header AEAD Nonce" |
| KDFSaltConstVMessHeaderPayloadLengthAEADKey | "VMess Header AEAD Key_Length" |
| KDFSaltConstVMessHeaderPayloadLengthAEADIV | "VMess Header AEAD Nonce_Length" |
| KDFSaltConstAEADRespHeaderLenKey | "AEAD Resp Header Len Key" |
| KDFSaltConstAEADRespHeaderLenIV | "AEAD Resp Header Len IV" |
| KDFSaltConstAEADRespHeaderPayloadKey | "AEAD Resp Header Key" |
| KDFSaltConstAEADRespHeaderPayloadIV | "AEAD Resp Header IV" |
2.4 辅助函数
| 函数 | 说明 |
|---|---|
| MD5(data) | 输入任意长度字节数组,输出 16 字节 |
| HMAC(H, K, M) | H 为哈希函数,K 为密钥,M 为消息 |
| Shake(data) | SHA3-Shake128,输出任意长度字节流 |
| FNV1a(data) | 非加密哈希,用于数据完整性校验 |
3. 认证模式
VMess 协议头部支持两种认证模式,目前仅 AEAD 认证为推荐模式,MD5 认证已被废弃。
3.1 AEAD 认证(当前标准)
AEAD(Authenticated Encryption with Associated Data)认证通过 AES-128-GCM 对协议头部进行加密,能够同时保证机密性与完整性。
3.2 MD5 认证(已废弃)
MD5 认证使用 HMAC-MD5 + AES-128-CFB 对协议头部进行加密,无法保证头部完整性,存在重放攻击漏洞,已于新版本中移除。
4. 通信流程
VMess 是无状态协议,客户端与服务端无需握手即可直接传输数据。整体流程如下:
- 客户端构造请求,包含认证信息、指令段和数据段
- 服务端验证请求合法性
- 验证通过后,服务端转发请求至目标地址
- 服务端将响应数据返回给客户端
5. 客户端请求格式
5.1 AEAD 认证请求结构
+----------+---------------------------+--------------+---------------------------+------------------+
| EAuID | ALength | Nonce | AHeader | Data Section |
| 16 bytes | 2 + 16 bytes (GCM Tag) | 8 bytes | variable + 16 bytes (tag) | remaining |
+----------+---------------------------+--------------+---------------------------+------------------+
各字段说明:
- EAuID:加密的认证 ID,用于标识用户身份
- ALength:加密的指令段长度(2 字节明文 + 16 字节 GCM Tag)
- Nonce:AEAD 加密的随机数
- AHeader:加密的指令段
- Data Section:实际传输的数据
5.2 EAuID(加密认证 ID)
EAuID 明文结构:
+------------------+-----------+----------+
| Timestamp | Rand | CRC |
| 8 bytes (BE) | 4 bytes | 4 bytes |
+------------------+-----------+----------+
- Timestamp:64 位 Unix 时间戳(大端序)
- Rand:随机数
- CRC:CRC32([Timestamp, Rand]),使用 IEEE 多项式
EAuID 加密方式:
- 加密密钥:
KDF(CmdKey, "AES Auth ID Encryption")[:16] - 加密算法:AES-128 块加密(ECB 模式)
5.3 ALength 加密
ALength 使用 AES-128-GCM 加密:
- 加密密钥:
KDF(CmdKey, "VMess Header AEAD Key_Length", EAuID, Nonce)[:16] - Nonce:
KDF(CmdKey, "VMess Header AEAD Nonce_Length", EAuID, Nonce)[:12] - 附加数据(AD):EAuID
5.4 AHeader 加密
AHeader 使用 AES-128-GCM 加密:
- 加密密钥:
KDF(CmdKey, "VMess Header AEAD Key", EAuID, Nonce)[:16] - Nonce:
KDF(CmdKey, "VMess Header AEAD Nonce", EAuID, Nonce)[:12] - 附加数据(AD):EAuID
6. 指令段(Command Header)
指令段在 AEAD 和 MD5 两种认证模式下结构相同,仅加密方式不同。
+-----+--------+---------+---------+--------+----------+-----+---------+---------+-------+-------+
| Ver | BodyIV | BodyKey | RespHdr | Option | Security | Rsv | Command | Address | Pad | FNV1a |
| 1B | 16B | 16B | 1B | 1B | 1B | 1B | 1B | var | 0-15B | 4B |
+-----+--------+---------+---------+--------+----------+-----+---------+---------+-------+-------+
各字段说明:
| 字段 | 大小 | 说明 |
|---|---|---|
| Version | 1 字节 | 固定为 0x01 |
| Body IV | 16 字节 | 数据段加密 IV,随机值 |
| Body Key | 16 字节 | 数据段加密密钥,随机值 |
| Response Auth V | 1 字节 | 随机值,服务端响应时必须回显 |
| Option | 1 字节 | 功能选项位掩码 |
| Security | 1 字节 | 高 4 位为填充长度 P,低 4 位为加密方式 |
| Reserved | 1 字节 | 固定为 0x00 |
| Command | 1 字节 | 0x01=TCP,0x02=UDP,0x03=Mux |
| Port | 2 字节 | 目标端口(大端序) |
| Address Type | 1 字节 | 0x01=IPv4,0x02=域名,0x03=IPv6 |
| Address | 可变 | 目标地址 |
| Padding | 0-15 字节 | 随机填充 |
| Checksum | 4 字节 | FNV1a 哈希,覆盖指令段所有字段(不含自身) |
6.1 Option 字段位掩码
| 位 | 值 | 说明 |
|---|---|---|
| S | 0x01 | 标准格式数据流(默认启用) |
| R | 0x02 | 客户端期望复用 TCP 连接(已废弃) |
| M | 0x04 | 启用元数据混淆(推荐) |
| P | 0x08 | 全局填充,需 M 启用,且加密方式须为 AES-128-GCM 或 ChaCha20-Poly1305 |
| A | 0x10 | 启用认证包长度实验 |
6.2 加密方式编码
| 值 | 加密方式 |
|---|---|
| 0x01 | Legacy(AES-128-CFB,已废弃) |
| 0x03 | AES-128-GCM |
| 0x04 | ChaCha20-Poly1305 |
| 0x05 | None(无加密) |
| 0x06 | Zero(无加密无分块,原始流) |
6.3 地址字段格式
- T = 0x01(IPv4):4 字节 IPv4 地址
- T = 0x02(域名):1 字节长度 L + L 字节域名
- T = 0x03(IPv6):16 字节 IPv6 地址
7. 数据段格式
7.1 标准格式(Standard Format)
当 Option(S) 启用时,数据段采用分块传输格式,每个数据块结构如下:
+----------+------------------+----------+
| Length L | Data Packet | Padding |
| 2 bytes | L-P bytes | P bytes |
+----------+------------------+----------+
- Length L:大端序整数,最大值为 2^14(16384)
- 当 Option(M) 启用时,实际长度 = 传输值 XOR Mask,Mask 由
Shake(Body IV)生成 - Padding P:当 Option(P) 启用时,P =
((RequestMask.NextByte() << 8) + RequestMask.NextByte()) % 64
7.2 各加密方式下的数据包格式
AES-128-GCM:
- 密钥:指令段中的 Body Key
- Nonce:
count(2B) + Body IV[2:12],count 从 0 开始每包递增 - 数据包 = (R-16) 字节实际数据 + 16 字节 GCM 认证标签
ChaCha20-Poly1305:
- 密钥:
MD5(Body Key) + MD5(MD5(Body Key))(扩展为 32 字节) - Nonce:
count(2B) + Body IV[2:12] - 数据包 = (R-16) 字节实际数据 + 16 字节 Poly1305 认证标签
AES-128-CFB(已废弃):
- 整个数据段使用 AES-128-CFB 加密
- 数据包 = 4 字节 FNV1a 哈希 + (R-4) 字节实际数据
Zero 模式(0x06):
- 强制使用基本格式,不进行任何加密
- 协议头部仍使用 AEAD 或 MD5 认证加密
7.3 传输结束信号
当 NoTerminationSignal 未设置时,客户端须在传输结束时发送一个空数据块(L=0 或仅含认证数据长度),以通知服务端传输结束。
8. 服务端响应格式
8.1 AEAD 认证响应
响应加密密钥和 IV 由请求的 Body Key/IV 派生:
Response Body Key = SHA256(Request Body Key)[:16]
Response Body IV = SHA256(Request Body IV)[:16]
响应头部分为长度和内容两部分,均使用 AES-128-GCM 加密:
响应长度加密(2 字节 + 16 字节 GCM Tag):
- 密钥:
KDF(Response Body Key, "AEAD Resp Header Len Key")[:16] - Nonce:
KDF(Response Body IV, "AEAD Resp Header Len IV")[:12]
响应内容加密:
- 密钥:
KDF(Response Body Key, "AEAD Resp Header Key")[:16] - Nonce:
KDF(Response Body IV, "AEAD Resp Header IV")[:12]
8.2 响应头部结构
+----------+--------+---------+---------+---------+
| RespHdr | Option | CmdID | CmdLen | CmdData |
| 1B | 1B | 1B | 1B | var |
+----------+--------+---------+---------+---------+
- RespHdr:必须与请求中的 Response Auth V 一致
- Option:0x01 表示服务端准备复用 TCP 连接(已废弃)
- CmdID:0x01 表示动态端口指令
8.3 动态端口指令
服务端可通过响应中的动态端口指令,通知客户端使用新端口进行通信:
+----------+-------+---------+-----------+-------------+-------------+
| Reserved | Port | User ID | Alter ID | User Level | Valid Time T|
| 1B | 2B | 16B | 2B | 1B | 1B |
+----------+-------+---------+-----------+-------------+-------------+
Valid Time T 单位为分钟,超时后客户端须重新使用主端口。
9. 用户认证与重放防护
9.1 TimedUserValidator
服务端维护一个 TimedUserValidator,对每个入站连接执行以下操作:
- 遍历所有已知用户的 CmdKey,尝试 AES 解密 16 字节 Auth ID
- 验证 CRC32 校验和
- 验证时间戳是否在服务端当前时间 ±120 秒范围内
- 通过重放过滤器检查 Auth ID 是否已被使用
9.2 重放防护机制
VMess 实现了两层重放防护:
- Auth ID 过滤器:维护 120 秒内的 Auth ID 记录,拒绝重复的 Auth ID
- Session History:缓存 3 分钟内的
{user, bodyKey, bodyIV}三元组,防止会话重放
9.3 行为种子(Behavior Seed)
服务端通过 HMAC-SHA256 + CRC64 对所有用户 ID 计算一个确定性的"行为种子",用于控制对非法连接的随机读取长度(drainer 模式),以防止主动探测攻击。
10. 安全性分析
10.1 已知漏洞:MD5 认证重放攻击(CVE 级别,已修复)
2020 年 5 月,研究人员发现 VMess MD5 认证模式存在严重的重放攻击漏洞,主要原因如下:
漏洞一:认证凭据可在有效期内重放
MD5 认证的 Authentication Credential 基于 HMAC-MD5(UserID, UTC Timestamp) 生成,有效期约为 ±60 秒。攻击者可在此窗口内重放合法客户端的认证凭据,绕过身份验证。
漏洞二:AES-128-CFB 不提供认证,MAC-then-Encrypt 设计缺陷
指令段使用 AES-128-CFB 加密,该模式不提供完整性保护。协议采用 MAC-then-Encrypt 机制,但由于 Margin P 字段(填充长度)未经认证,服务端必须先盲目信任 Margin P 的值,读取 P+4 字节后才能验证 Checksum(FNV1a MAC)。
攻击者可利用 AES-CFB 的流密码可塑性(malleability),通过修改密文中特定字节来枚举 Margin P 的所有 16 种可能值,从而构造出能被服务端接受的探测包,识别 VMess 服务端。
漏洞三:不一致的连接关闭行为
研究人员发现,服务端在不同错误类型下关闭连接的行为存在差异,攻击者可通过观察服务端的响应模式(连接立即关闭 vs. 等待更多数据)来识别 VMess 服务端。
修复措施:
- 引入 AEAD 认证模式,使用 AES-128-GCM 对头部进行认证加密,彻底解决完整性问题
- 在所有错误类型下统一使用随机长度的 draining 策略
- 新版本(Xray-core)已完全移除 MD5 认证支持
10.2 TLS 指纹识别问题
早期 V2Ray 客户端在发送 TLS ClientHello 时使用硬编码的密码套件,导致 TLS 指纹极为罕见,可被 GFW 精准识别。后续版本通过使用 Go TLS 库的默认配置加以缓解,但指纹唯一性问题在部分场景下仍存在。
10.3 HTTP 伪装失败
V2Ray 的 HTTP 伪装模式存在以下问题:
- 客户端和服务端仅在每个 TCP 连接的第一个数据包中添加 HTTP 头部,后续数据包不含 HTTP 头部,流量特征明显
- 服务端对各类错误统一返回硬编码的 500 响应,易被主动探测识别
10.4 AEAD 模式的安全性
当前 AEAD 认证模式在以下方面提供了较强的安全保证:
- 机密性:AES-128-GCM 加密,密钥通过 KDF 派生,不可逆
- 完整性:GCM 认证标签覆盖头部和数据,任何篡改均可被检测
- 重放防护:EAuID 中的时间戳 + 随机数 + CRC32 三重校验,配合服务端 120 秒滑动窗口过滤
- 前向安全性:每个会话使用随机生成的 Body Key 和 Body IV,即使长期密钥泄露,历史会话数据仍安全
- 抗探测性:行为种子机制使服务端对非法连接的响应行为随机化,难以通过主动探测识别
11. 流量混淆机制
11.1 元数据混淆(ChunkMasking)
当 Option(M) 启用时,数据块的长度字段通过 SHAKE128 流进行 XOR 混淆:
RequestMask = Shake(Request Body IV)
ResponseMask = Shake(Response Body IV)
wire_length = actual_length XOR (Mask.NextByte() << 8 | Mask.NextByte())
此机制使流量的块长度分布更加随机,增加流量分析难度。
11.2 全局填充(GlobalPadding)
当 Option(P) 启用时,每个数据块后附加 0-63 字节的随机填充:
P = ((RequestMask.NextByte() << 8) + RequestMask.NextByte()) % 64
填充字节由 SHAKE128 流生成,增加流量的随机性。
11.3 认证包长度实验(AuthenticatedLength)
当 Option(A) 启用时,数据块的长度字段本身也使用 AEAD 加密:
length_key = KDF16(Body Key, "auth_len")
此特性进一步防止攻击者通过观察包长度分布来分析流量。
12. 传输层支持
VMess 协议本身工作在应用层,可运行于多种传输层之上:
| 传输方式 | 说明 |
|---|---|
| TCP | 原生支持,最基础的传输方式 |
| WebSocket | 通过 HTTP Upgrade 建立,可穿透 HTTP 代理 |
| HTTP/2 | 基于 h2 协议,支持多路复用 |
| gRPC | 基于 HTTP/2,具有更好的穿透性 |
| QUIC | 基于 UDP,低延迟 |
| mKCP | 基于 UDP 的 KCP 协议,改善弱网性能 |
| UNIX Socket | 本地进程间通信 |
VMess over TLS(如 VMess+WebSocket+TLS)是常见的部署方式,通过 TLS 层提供额外的流量伪装,使流量看起来与正常 HTTPS 流量相似。
13. Mux 多路复用
VMess 支持通过 Mux(多路复用)在单个 TCP 连接上承载多个虚拟连接,以减少 TCP 握手开销。Mux 连接通过虚拟域名 v1.mux.cool 标识,UDP 流量则通过 XUDP 帧格式封装。
14. 与同类协议的比较
14.1 VMess vs. VLESS
VLESS 是 VMess 的后继协议,于 2020 年引入,主要改进如下:
| 特性 | VMess | VLESS |
|---|---|---|
| 内置加密 | 是(AES-128-GCM 等) | 否(依赖传输层 TLS) |
| 协议开销 | 较高(双重加密) | 较低(无冗余加密) |
| 认证机制 | UUID + AEAD | UUID(简化) |
| 设计复杂度 | 较高 | 较低 |
| 适用场景 | 无 TLS 场景 | 配合 TLS/XTLS 使用 |
当 VMess 运行于 TLS 之上时,存在双重加密(VMess 加密 + TLS 加密),造成不必要的 CPU 开销。VLESS 通过去除内置加密层,将加密职责完全交给传输层 TLS,显著降低了延迟和 CPU 消耗。
14.2 VMess vs. Shadowsocks
| 特性 | VMess | Shadowsocks |
|---|---|---|
| 协议设计 | 复杂,功能丰富 | 简单,轻量 |
| 认证方式 | UUID + AEAD | 预共享密钥 |
| 混淆能力 | 内置多种混淆选项 | 依赖插件(obfs 等) |
| 传输层支持 | 多种 | 主要为 TCP/UDP |
| 抗重放 | AEAD 模式下较强 | 依赖实现 |
14.3 VMess vs. Trojan
Trojan 通过完全模拟 HTTPS 流量来规避检测,而 VMess 依赖自身的加密和混淆机制。Trojan 在流量伪装方面更为彻底,但灵活性不如 VMess。
15. 协议演进
| 版本/阶段 | 主要变化 |
|---|---|
| 早期版本 | MD5 认证 + AES-128-CFB,存在重放漏洞 |
| v4.23.4+ | 修复 TLS 指纹问题,使用 Go TLS 默认配置 |
| AEAD 引入 | 新增 AEAD 认证模式,解决头部完整性问题 |
| Xray-core | 完全移除 MD5 认证,仅支持 AEAD;引入 XTLS、REALITY 等新特性 |
| 当前状态 | VMess AEAD 为稳定协议,新项目推荐使用 VLESS+XTLS/REALITY |
16. 总结
VMess 协议是代理协议设计领域的重要实践,其 AEAD 认证模式在机密性、完整性和抗重放方面提供了较为完善的安全保证。协议的分块传输、元数据混淆和全局填充机制有效增加了流量分析的难度。
然而,VMess 的设计复杂性也带来了一定的实现风险,历史上的 MD5 认证漏洞表明,协议设计中的细微缺陷可能导致严重的安全问题。随着 VLESS、XTLS 和 REALITY 等更新协议的出现,VMess 在新部署场景中的使用逐渐减少,但其设计思路和安全机制对后续协议的发展具有重要的参考价值。