PACKETSTORM

📄 Bichon 1.0.2 Bearer Access Token Disclosure_PACKETSTORM:221272

Description

Bichon version 1.0.2 accepts Bearer access tokens via GET requests which has the negative side affect of being disclosed in logs, REFERER headers, and more...
Visit Original Source

Basic Information

ID PACKETSTORM:221272
Published May 18, 2026 at 00:00

Affected Product

Affected Versions Bichon 1.0.2 Bearer Access Token Accepted via Query String + Logged
===================================================================

Vendor: rustmailer
Product: Bichon - self-hosted email archiving server (Rust + TypeScript)
Project URL: https://github.com/rustmailer/bichon
Affected: All versions through HEAD as of 2026-05-18
Commit: 9daab241b0220e81e43d4b98616d77fa45ad58c7
Release: 1.0.2
Patched: Pending vendor fix
Severity: Medium
CVSS 3.1: 6.5 (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N)
CWE: CWE-598 (Use of GET Request Method With Sensitive
Query Strings)
CWE-532 (Insertion of Sensitive Information into
Log File)
CWE-522 (Insufficiently Protected Credentials)
CVE: Pending (requested via GitHub CNA)
Discovered: 2026-05-18 (manual source review + live verification)
Researcher: AoxLir <[email protected]>
Disclosure: Coordinated (Project Zero 90-day standard)


I. Background
=============

Bichon's REST API uses opaque Bearer access tokens (issued by the
/api/login endpoint or the /api/v1/access-token endpoint) for
authorization on /api/v1/* paths. The standard delivery mechanism is
the Authorization: Bearer header.


II. Vulnerability Detail
========================

In crates/server/src/common/auth.rs lines 97-106
(extract_client_context):

let bearer = req
.headers()
.typed_get::<Authorization<Bearer>>()
.map(|auth| auth.0.token().to_string())
.or_else(|| req.params::<Param>()
.ok()
.map(|param| param.access_token));
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Fallback: token read from the URL query string */

let token = bearer.ok_or_else(|| {
create_api_error_response("Valid access token not found",
ErrorCode::PermissionDenied)
})?;

Where Param is:

#[derive(Deserialize)]
struct Param {
access_token: String,
}

This is explicitly discouraged by RFC 6750 (sections 2.3 and 5.3) due
to leakage via access logs, browser history, Referer headers, and
shared screenshots.

Live testing additionally confirmed that Bichon's own logger
(bichon_server::common::log) writes the full token to stdout on every
request - meaning that running `docker logs bichon` (which any
container operator has access to) exposes valid bearer tokens of all
users.


III. Proof of Concept
=====================

Verified live against rustmailer/bichon:1.0.2 (Docker).

Step 1: Make an authenticated API call using ONLY the query string
(no Authorization header):

$ curl -i \
'http://localhost:15630/api/v1/current-user?access_token=mFa5HSBUHDjG5vTDqBYAADXH'

HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
...

{"username":"bob","email":"[email protected]", ...}

The query-string fallback fully replaces the header.

Step 2: Inspect Bichon's own application log:

$ docker logs bichon-poc 2>&1 | grep current-user | tail -2
2026-05-18T12:01:53.748+00:00 INFO request{remote_addr=172.17.0.1
method=GET path=/api/v1/current-user
query=Some("access_token=mFa5HSBUHDjG5vTDqBYAADXH")
referer=None content_length=None}:
request completed successfully status=200 duration=53.351us

The token is written verbatim to stdout by the application itself.
No reverse proxy, CDN, or browser is needed to leak it.


IV. Impact
==========

* Any operator with `docker logs` / `journalctl -u bichon` access
immediately obtains every active bearer token. Bichon
deployments commonly include a non-Bichon operator (DevOps, SRE,
log aggregator service account). Such operator can impersonate
any user, including admin, until the token is revoked or expires.

* Log shippers (Datadog, CloudWatch, Splunk, ELK, Loki, Promtail)
forward the raw stdout - tokens land in third-party log
repositories where retention, ACLs, and indexing may not be
aligned with the trust model of Bichon's user data.

* The legacy URL-leakage paths (nginx access log, browser history,
Referer to third parties) remain valid additional vectors.


V. Solution
===========

Two changes are recommended:

1. Remove the URL-query fallback in extract_client_context():

let bearer = req
.headers()
.typed_get::<Authorization<Bearer>>()
.map(|auth| auth.0.token().to_string());
/* drop .or_else(...) */

If a separate code path genuinely needs a download-friendly token
in the URL (e.g. signed share links), implement a separate
short-lived, single-use download-token mechanism with its own scope.

2. Redact sensitive query parameters in the request-log middleware:

In bichon_server::common::log, before emitting the `query` field,
sanitize any `access_token`, `token`, `api_key`, or `password`
parameter to a placeholder such as "[REDACTED]".

VI. Credit
===========

Discovered and reported by MrOruc, independent security researcher.
GitHub: https://github.com/MrOruc
Email: [email protected]

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.