Skip to content

transport/internet/splithttp: per-request path randomization (pathPool)#6282

Closed
belotserkovtsev wants to merge 1 commit into
XTLS:mainfrom
belotserkovtsev:feat/xhttp-pathpool
Closed

transport/internet/splithttp: per-request path randomization (pathPool)#6282
belotserkovtsev wants to merge 1 commit into
XTLS:mainfrom
belotserkovtsev:feat/xhttp-pathpool

Conversation

@belotserkovtsev

@belotserkovtsev belotserkovtsev commented Jun 6, 2026

Copy link
Copy Markdown

What

Adds an optional pathPool to xhttp settings. When session and seq are carried
off the path (cookie/header/query), each request appends a random entry from the
pool to the base path. A * in an entry is replaced with a random number, so a
short list can cover id/cursor style paths. Empty by default, so nothing changes
unless you set it.

Why

With session and seq off the path (cookies, say), every xhttp request still hits
the same path. A stream of requests to one fixed path is easy for a CDN or WAF to
match on. This pairs with custom session IDs (#6258): #6258 hides the session id
value, pathPool spreads requests across ordinary-looking paths so there's no
single path to key on. It also avoids stuffing junk query params, which just
trades one signature for another.

There's no built-in pool, on purpose. A fixed list baked into the binary would
become a shared signature, so the operator supplies paths that match whatever
traffic they want to blend into.

Example

"xhttpSettings": {
  "mode": "packet-up",
  "path": "/api/v1",
  "extra": {
    "sessionPlacement": "cookie", "sessionKey": "sid",
    "seqPlacement": "cookie", "seqKey": "sq",
    "pathPool": ["timeline", "sync", "items/*", "users/*/feed", "search/suggest"]
  }
}

Requests then look like:

GET /api/v1/items/48213
GET /api/v1/users/9921/feed
GET /api/v1/timeline

instead of every request hitting /api/v1/.

Notes

  • Client side only. The server already accepts anything after the configured path
    prefix (strings.HasPrefix) and reads session/seq from their placement, so it
    works against an unmodified server. No protocol change.
  • Active only when session and seq are both off the path. With path placement the
    pool is ignored.
  • Test added.

## What

Adds an optional `pathPool` to xhttp settings. When session and seq are carried
off the path (cookie/header/query), each request appends a random entry from the
pool to the base path. A `*` in an entry is replaced with a random number, so a
short list can cover id/cursor style paths. Empty by default, so nothing changes
unless you set it.

## Why

With session and seq off the path (cookies, say), every xhttp request still hits
the same path. A stream of requests to one fixed path is easy for a CDN or WAF to
match on. This pairs with custom session IDs (XTLS#6258): XTLS#6258 hides the session id
value, `pathPool` spreads requests across ordinary-looking paths so there's no
single path to key on. It also avoids stuffing junk query params, which just
trades one signature for another.

There's no built-in pool, on purpose. A fixed list baked into the binary would
become a shared signature, so the operator supplies paths that match whatever
traffic they want to blend into.

## Example

```json
"xhttpSettings": {
  "mode": "packet-up",
  "path": "/api/v1",
  "extra": {
    "sessionPlacement": "cookie", "sessionKey": "sid",
    "seqPlacement": "cookie", "seqKey": "sq",
    "pathPool": ["timeline", "sync", "items/*", "users/*/feed", "search/suggest"]
  }
}
```

Requests then look like:

```
GET /api/v1/items/48213
GET /api/v1/users/9921/feed
GET /api/v1/timeline
```

instead of every request hitting `/api/v1/`.

## Notes

- Client side only. The server already accepts anything after the configured path
  prefix (`strings.HasPrefix`) and reads session/seq from their placement, so it
  works against an unmodified server. No protocol change.
- Active only when session and seq are both off the path. With path placement the
  pool is ignored.
- Test added.
@Fangliding

Fangliding commented Jun 6, 2026

Copy link
Copy Markdown
Member

我们倾向于已经发生了什么而不是根据推测继续加功能 不然总是可以找到乱七八糟的点来加固它 config会被各种配置淹没
之前还能装作是什么api 但是把它伪造成浏览序列但是稍微peek一下就会发现它传的根本不是html, oops

@Fangliding Fangliding closed this Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants