Skip to content

Commit 3e10f3a

Browse files
committed
server side anti reply attack
1 parent f2e149e commit 3e10f3a

File tree

1 file changed

+72
-4
lines changed

1 file changed

+72
-4
lines changed

proxy/vmess/encoding/server.go

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import (
66
"crypto/md5"
77
"hash/fnv"
88
"io"
9+
"sync"
10+
"time"
911

1012
"golang.org/x/crypto/chacha20poly1305"
11-
1213
"v2ray.com/core/common/buf"
1314
"v2ray.com/core/common/crypto"
1415
"v2ray.com/core/common/errors"
@@ -18,6 +19,63 @@ import (
1819
"v2ray.com/core/proxy/vmess"
1920
)
2021

22+
type sessionId struct {
23+
user [16]byte
24+
key [16]byte
25+
nonce [16]byte
26+
}
27+
28+
type sessionHistory struct {
29+
sync.RWMutex
30+
cache map[sessionId]time.Time
31+
}
32+
33+
func newSessionHistory() *sessionHistory {
34+
h := &sessionHistory{
35+
cache: make(map[sessionId]time.Time, 128),
36+
}
37+
go h.run()
38+
return h
39+
}
40+
41+
func (h *sessionHistory) Add(session sessionId) {
42+
h.Lock()
43+
h.cache[session] = time.Now().Add(time.Minute * 3)
44+
h.Unlock()
45+
}
46+
47+
func (h *sessionHistory) Has(session sessionId) bool {
48+
h.RLock()
49+
defer h.RUnlock()
50+
51+
if expire, found := h.cache[session]; found {
52+
return expire.After(time.Now())
53+
}
54+
return false
55+
}
56+
57+
func (h *sessionHistory) run() {
58+
for {
59+
time.Sleep(time.Second * 30)
60+
session2Remove := make([]sessionId, 0, 16)
61+
now := time.Now()
62+
h.Lock()
63+
for session, expire := range h.cache {
64+
if expire.Before(now) {
65+
session2Remove = append(session2Remove, session)
66+
}
67+
}
68+
for _, session := range session2Remove {
69+
delete(h.cache, session)
70+
}
71+
h.Unlock()
72+
}
73+
}
74+
75+
var (
76+
globalSessionHistory = newSessionHistory()
77+
)
78+
2179
type ServerSession struct {
2280
userValidator protocol.UserValidator
2381
requestBodyKey []byte
@@ -56,8 +114,9 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
56114
if err != nil {
57115
return nil, errors.Base(err).Message("VMess|Server: Failed to get user account.")
58116
}
117+
vmessAccount := account.(*vmess.InternalAccount)
59118

60-
aesStream := crypto.NewAesDecryptionStream(account.(*vmess.InternalAccount).ID.CmdKey(), iv)
119+
aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv)
61120
decryptor := crypto.NewCryptionReader(aesStream, reader)
62121

63122
nBytes, err := io.ReadFull(decryptor, buffer[:41])
@@ -77,8 +136,17 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
77136

78137
v.requestBodyIV = append([]byte(nil), buffer[1:17]...) // 16 bytes
79138
v.requestBodyKey = append([]byte(nil), buffer[17:33]...) // 16 bytes
80-
v.responseHeader = buffer[33] // 1 byte
81-
request.Option = protocol.RequestOption(buffer[34]) // 1 byte
139+
var sid sessionId
140+
copy(sid.user[:], vmessAccount.ID.Bytes())
141+
copy(sid.key[:], v.requestBodyKey)
142+
copy(sid.nonce[:], v.requestBodyIV)
143+
if globalSessionHistory.Has(sid) {
144+
return nil, errors.New("VMess|Server: Duplicated session id. Possibly under reply attack.")
145+
}
146+
globalSessionHistory.Add(sid)
147+
148+
v.responseHeader = buffer[33] // 1 byte
149+
request.Option = protocol.RequestOption(buffer[34]) // 1 byte
82150
padingLen := int(buffer[35] >> 4)
83151
request.Security = protocol.NormSecurity(protocol.Security(buffer[35] & 0x0F))
84152
// 1 bytes reserved

0 commit comments

Comments
 (0)