In Eclipse Jetty, the Digest authentication server-side component uses ISO-8859-1 to encode the password as bytes. This was done because…
[email protected]·CWE-173·Published 2026-07-22
In Eclipse Jetty, the Digest authentication server-side component uses ISO-8859-1 to encode the password as bytes. This was done because the initial specification for HTTP did not specify explicitly a charset, and it was assumed to be ISO-8859-1 for historical reasons. If the password contains characters that cannot be represented in ISO-8859-1, they are silently replaced by `?`. This happens with passwords that contain Chinese, Cyrillic or Greek characters, for example: `αβ123` converts to `??123`. An attacker can send a request with a digest `Authorization` header crafted with a password made of only `?` characters; the server would match any password of the same length that contains non-ISO-8859-1 characters. Recent HTTP Digest [RFC-7616](https://datatracker.ietf.org/doc/html/rfc7616) supports a `charset` parameters that defaults to UTF-8 that allows for correct encoding/decoding of passwords.
In Eclipse Jetty, the Digest authentication server-side component uses ISO-8859-1 to encode the password as bytes. This was done because the initial specification for HTTP did not specify explicitly a charset, and it was assumed to be ISO-8859-1 for historical reasons. If the password contains characters that cannot be represented in ISO-8859-1, they are silently replaced by `?`. This happens with passwords that contain Chinese, Cyrillic or Greek characters, for example: `αβ123` converts to `??123`. An attacker can send a request with a digest `Authorization` header crafted with a password made of only `?` characters; the server would match any password of the same length that contains non-ISO-8859-1 characters. Recent HTTP Digest [RFC-7616](https://datatracker.ietf.org/doc/html/rfc7616) supports a `charset` parameters that defaults to UTF-8 that allows for correct encoding/decoding of passwords.
### Summary The `DigestAuthentication.apply()` method in Jetty's HTTP client uses `getBytes(StandardCharsets.ISO_8859_1)` at three locations (lines 171, 179, 196) to compute Digest auth response hashes. ISO-8859-1 silently replaces any character above U+00FF (Chinese, Japanese, Cyrillic, Arabic, Emoji, etc.) with 0x3F (`?`), causing all such characters to produce identical hash contributions. An attacker who knows a victim's username can bypass Digest authentication by replacing all non-Latin-1 characters in the password with `?` characters, since the collision password produces the same MD5-based Digest response hash as the original password. ### Details ### Root Cause In `jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/DigestAuthentication.java`, the `apply()` method computes the three Digest auth hashes (H(A1), H(A2), and the final response) using ISO-8859-1 character encoding: ```java // Line 171 — H(A1) String hashA1 = toHexString(digester.digest(a1.getBytes(StandardCharsets.ISO_8859_1))); // Line 179 — H(A2) String hashA2 = toHexString(digester.digest(a2.getBytes(StandardCharsets.ISO_8859_1))); // Line 196 — Final response hash final String hashA3 = toHexString(digester.digest(a3.getBytes(StandardCharsets.ISO_8859_1))); ``` ISO-8859-1 (Latin-1) can only encode characters in the range U+0000–U+00FF. Any character outside this range — including all CJK, Cyrillic, Arabic, Greek, Hangul, and emoji characters — is silently replaced with the byte `0x3F` (`?`). `String.getBytes(ISO_8859_1)` in Java performs this replacement without any warning or exception. ### PoC ``` Password: "我爱Java!密码123★" (7 non-Latin-1 characters) UTF-8 encoding: 45 bytes → MD5 H(A1) = 9a4e61484f228633d5d0f95d1bbb0a99 ISO-8859-1: 31 bytes → MD5 H(A1) = d60ddc903d71913bcc3ab4a94f7fc239 Collision "??...": 31 bytes → MD5 H(A1) = d60ddc903d71913bcc3ab4a94f7fc239 ← IDENTICAL ``` Multi-language confirmation — all four language passwords below produce the same hash: ``` Chinese (密码123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Korean (비번123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Cyrillic(аб123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Greek (αβ123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Attacker(??123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 ← all collide! ``` ### Impact **Scenario 1: Authentication Bypass (Collision Attack)** If a service using Jetty for Digest authentication has a user with a non-Latin-1 password (e.g., Chinese, Japanese, Russian), an attacker can authenticate as that user using a collision password where all non-Latin-1 characters are replaced with `?`: - Original password: `我爱Java!密码123★` - Collision password: `??Java!??123?` - **Both produce identical MD5 hashes under ISO-8859-1** → Authentication succeeds This affects any password containing characters > U+00FF, which covers: - Chinese (CJK): U+4E00–U+9FFF - Japanese (Hiragana/Katakana/Kanji): U+3040–U+30FF, U+4E00+ - Korean (Hangul): U+AC00–U+D7AF - Cyrillic: U+0400–U+04FF (Russian, Ukrainian, Bulgarian, etc.) - Arabic: U+0600–U+06FF - Greek: U+0370–U+03FF - Latin Extended: U+0100–U+024F (accented European characters like ĉ, ğ, ñ when > U+00FF) - Emoji / Symbols > U+00FF **Scenario 2: Denial of Service for Non-Latin-1 Users** Most modern web applications store password hashes computed using UTF-8. When Jetty's Digest client computes a hash with ISO-8859-1, the bytes differ from what the server stored/expects. This means **any user with non-ASCII (Latin-1+) characters in their password can never successfully authenticate via Digest auth** — even the legitimate user. This is not just a security issue but a functional correctness bug that silently breaks authentication for most non-European-language users.
### Summary The `DigestAuthentication.apply()` method in Jetty's HTTP client uses `getBytes(StandardCharsets.ISO_8859_1)` at three locations (lines 171, 179, 196) to compute Digest auth response hashes. ISO-8859-1 silently replaces any character above U+00FF (Chinese, Japanese, Cyrillic, Arabic, Emoji, etc.) with 0x3F (`?`), causing all such characters to produce identical hash contributions. An attacker who knows a victim's username can bypass Digest authentication by replacing all non-Latin-1 characters in the password with `?` characters, since the collision password produces the same MD5-based Digest response hash as the original password. ### Details ### Root Cause In `jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/DigestAuthentication.java`, the `apply()` method computes the three Digest auth hashes (H(A1), H(A2), and the final response) using ISO-8859-1 character encoding: ```java // Line 171 — H(A1) String hashA1 = toHexString(digester.digest(a1.getBytes(StandardCharsets.ISO_8859_1))); // Line 179 — H(A2) String hashA2 = toHexString(digester.digest(a2.getBytes(StandardCharsets.ISO_8859_1))); // Line 196 — Final response hash final String hashA3 = toHexString(digester.digest(a3.getBytes(StandardCharsets.ISO_8859_1))); ``` ISO-8859-1 (Latin-1) can only encode characters in the range U+0000–U+00FF. Any character outside this range — including all CJK, Cyrillic, Arabic, Greek, Hangul, and emoji characters — is silently replaced with the byte `0x3F` (`?`). `String.getBytes(ISO_8859_1)` in Java performs this replacement without any warning or exception. ### PoC ``` Password: "我爱Java!密码123★" (7 non-Latin-1 characters) UTF-8 encoding: 45 bytes → MD5 H(A1) = 9a4e61484f228633d5d0f95d1bbb0a99 ISO-8859-1: 31 bytes → MD5 H(A1) = d60ddc903d71913bcc3ab4a94f7fc239 Collision "??...": 31 bytes → MD5 H(A1) = d60ddc903d71913bcc3ab4a94f7fc239 ← IDENTICAL ``` Multi-language confirmation — all four language passwords below produce the same hash: ``` Chinese (密码123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Korean (비번123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Cyrillic(аб123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Greek (αβ123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 Attacker(??123) → H(A1) = db87f31e8d96cd15f9acec7eabdc4560 ← all collide! ``` ### Impact **Scenario 1: Authentication Bypass (Collision Attack)** If a service using Jetty for Digest authentication has a user with a non-Latin-1 password (e.g., Chinese, Japanese, Russian), an attacker can authenticate as that user using a collision password where all non-Latin-1 characters are replaced with `?`: - Original password: `我爱Java!密码123★` - Collision password: `??Java!??123?` - **Both produce identical MD5 hashes under ISO-8859-1** → Authentication succeeds This affects any password containing characters > U+00FF, which covers: - Chinese (CJK): U+4E00–U+9FFF - Japanese (Hiragana/Katakana/Kanji): U+3040–U+30FF, U+4E00+ - Korean (Hangul): U+AC00–U+D7AF - Cyrillic: U+0400–U+04FF (Russian, Ukrainian, Bulgarian, etc.) - Arabic: U+0600–U+06FF - Greek: U+0370–U+03FF - Latin Extended: U+0100–U+024F (accented European characters like ĉ, ğ, ñ when > U+00FF) - Emoji / Symbols > U+00FF **Scenario 2: Denial of Service for Non-Latin-1 Users** Most modern web applications store password hashes computed using UTF-8. When Jetty's Digest client computes a hash with ISO-8859-1, the bytes differ from what the server stored/expects. This means **any user with non-ASCII (Latin-1+) characters in their password can never successfully authenticate via Digest auth** — even the legitimate user. This is not just a security issue but a functional correctness bug that silently breaks authentication for most non-European-language users.
| Version | Type | Source | Base | Exp | Impact | Vector |
|---|---|---|---|---|---|---|
| 4.0 | Primary | cve.org | 8.7 | — | — | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
| 4.0 | Secondary | GHSA | 8.7 | — | — |
| CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
| 4.0 | Secondary | ENISA EUVD | 8.7 | — | — | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
| 4.0 | Secondary | NVD | 8.7 | — | — | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X |