Spigniv73 opened a new issue, #14151:
URL: https://github.com/apache/apisix/issues/14151
## Summary
`ai-cache`'s request fingerprint omits two things that determine the
upstream response:
the client headers it forwards and the instance `auth` (header/query). Its
own comment calls the
fingerprint the *"Identity of the EFFECTIVE upstream request"* — but
`ai-proxy` forwards
`core.request.headers(ctx)` verbatim and uses `auth.header`/`auth.query`, so
two requests with an
identical body can hit the same cache cell while the effective upstream
request differs.
## Where (pin `b6deb41`; same file on `main` and in 3.19.0)
`apisix/plugins/ai-cache/key.lua:59-116` — the hashed representation
contains only:
```lua
return {
client = client, -- protocol, params, messages
effective = {
provider = inst.provider,
model = ...,
options = inst.options,
llm_options = ov.llm_options,
request_body = ov.request_body,
endpoint = ov.endpoint,
},
}
```
No `client_headers`, no `auth`. The counterpart is explicitly the opposite:
* `apisix/plugins/ai-proxy/base.lua:291` — `extra_opts.client_headers =
core.request.headers(ctx)`
* `apisix/plugins/ai-transport/http.lua:93-96` — every `client_headers`
entry is copied into the
outbound header set
* `apisix/plugins/ai-proxy/schema.lua` — `auth.header` / `auth.query` /
`instances.auth.*` are part of
the instance definition
## Proof of the collision (unit, real `key.lua`)
```
digest anthropic-version=2023-06-01 : 26e80b5b…d2409
digest anthropic-version=2099-01-01 : 26e80b5b…d2409
identical? true
repr fields:
client =
{"protocol":"anthropic","params":{"stream":false},"messages":{}}
effective =
{"options":{...},"model":"claude-3-5-sonnet","provider":"anthropic"}
```
Repro (resty against the checkout, stubs only for `apisix.core`):
```bash
resty --no-stream -I <apisix-checkout> poc_cache.lua
```
## Impact
* A response can be replayed for a request whose `anthropic-version` /
`anthropic-beta` (or any
other response-determining header) differs → wrong or outdated answer.
* With `ai-proxy-multi` (or `share_across_routes`) two instances that share
provider/model/endpoint but
differ in `auth` collide, so a response fetched with one credential can be
served to a caller routed
to another instance.
* CVSS 3.1 `AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N` = 6.5. Client-controlled:
the header comes from the
downstream caller.
## Suggestion
Add `client_headers` (at least a deterministic subset/normalised form) and
the instance `auth`
material to `build_repr`. The existing `llm_options`/`endpoint` handling
shows the pattern: anything
`build_request` consumes belongs in the fingerprint.
## Notes
Found while auditing `b6deb41`. `apisix/plugins/ai-cache/key.lua` on `main`
still has the same
structure, and there is no matching issue in the tracker. The digest proof
is hard for the collision
itself; the exact per-provider blast radius (which upstreams vary on which
header) is our inference.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]