CVE-2026-103030
Dio: Cross-origin redirect leaks sensitive request headers in `dio_http2_adapter`
Dio is a powerful HTTP client for Dart and Flutter. From 1.0.0 until 2.8.0, dio_http2_adapter follows redirects in Http2Adapter._fetch by copying the original RequestOptions.headers into the redirected HTTP/2 request without comparing the source and destination origins. A server contacted by an application can return a cross-origin Location redirect to a different-origin destination, which then receives preserved Authorization, Cookie, and Proxy-Authorization headers because RequestOptions.copyWith retains the full header map. The issue applies when applications use Http2Adapter for authenticated requests, and redirect following is enabled by default. This issue is fixed in version 2.8.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.5
Dio: Cross-origin redirect leaks sensitive request headers in `dio_http2_adapter`
Dio is a powerful HTTP client for Dart and Flutter. From 1.0.0 until 2.8.0, dio_http2_adapter follows redirects in Http2Adapter._fetch by copying the original RequestOptions.headers into the redirected HTTP/2 request without comparing the source and destination origins. A server contacted by an application can return a cross-origin Location redirect to a different-origin destination, which then receives preserved Authorization, Cookie, and Proxy-Authorization headers because RequestOptions.copyWith retains the full header map. The issue applies when applications use Http2Adapter for authenticated requests, and redirect following is enabled by default. This issue is fixed in version 2.8.0.
- Record
CVE-2026-103030- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-98921
7.5
Dio is a powerful HTTP client for Dart and Flutter. From 1.0.0 until 2.8.0, dio_http2_adapter follows redirects in Http2Adapter._fetch by copying the original RequestOptions.headers into the redirected HTTP/2 request without comparing the source and destination origins. A server contacted by an application can return a cross-origin Location redirect to a different-origin destination, which then receives preserved Authorization, Cookie, and Proxy-Authorization headers because RequestOptions.copyWith retains the full header map. The issue applies when applications use Http2Adapter for authenticated requests, and redirect following is enabled by default. This issue is fixed in version 2.8.0.
- Record
EUVD-2026-98921- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cr65-55v9-xhvv
7.5
Dio: Cross-origin redirect leaks sensitive request headers in `dio_http2_adapter`
Summary
dio_http2_adapter follows HTTP Location redirects without stripping sensitive request headers when the redirect target is a different origin. An attacker who can cause any origin server to return a cross-origin 302 redirect receives the victim's Authorization, Cookie, and Proxy-Authorization headers verbatim. This vulnerability has a CVSS v3.1 base score of 7.5 (High) because followRedirects is enabled by default, no authentication is required to exploit it, and the confidentiality impact is high.
Details
When Dio is configured to use Http2Adapter, the adapter's internal _fetch method constructs HTTP/2 request headers by unconditionally copying all entries from options.headers:
// plugins/http2_adapter/lib/src/http2_adapter.dart:111
headers.addAll(
options.headers.entries.map((entry) {
...
return Header.ascii(entry.key.toLowerCase(), v);
}).toList(),
);
On receiving a 3xx response, the adapter reads the Location header and resolves the redirect target URI:
// plugins/http2_adapter/lib/src/http2_adapter.dart:254-263
final url = responseHeaders.value('location');
final uri = Uri.parse(url ?? '');
final String path = resolveRedirectUri(options.uri, uri).toString();
return _fetch(options.copyWith(path: path, maxRedirects: --options.maxRedirects), ...);
options.copyWith is called without an explicit headers argument; dio/lib/src/options.dart:629 therefore preserves the full original header map via headers ?? Map.from(this.headers). resolveRedirectUri (http2_adapter.dart:290) returns an attacker-controlled cross-origin URI without any origin comparison. The combination means that on every redirect, the full set of original sensitive headers — including Authorization, Cookie, and Proxy-Authorization — is forwarded to the new authority with no sanitization.
Data flow (source → sink):
dio/lib/src/dio_mixin.dart:383— publicDio.request<T>(String path, ...)entry point.dio/lib/src/options.dart:331— base and per-request headers merged intoRequestOptions.dio/lib/src/dio_mixin.dart:596—httpClientAdapter.fetch(reqOpt, ...)dispatches toHttp2Adapter.plugins/http2_adapter/lib/src/http2_adapter.dart:254—Locationheader is read from the network response (attacker-controlled source).plugins/http2_adapter/lib/src/http2_adapter.dart:258— redirect target URI resolved; cross-origin check absent.plugins/http2_adapter/lib/src/http2_adapter.dart:260—options.copyWith(path: path, ...)called withoutheadersoverride.dio/lib/src/options.dart:629—copyWithpreservesMap.from(this.headers)— all sensitive headers retained.plugins/http2_adapter/lib/src/http2_adapter.dart:111— preserved headers emitted verbatim to the cross-origin HTTP/2 request (sink).
Suggested patch — strip sensitive headers before following a cross-origin redirect:
--- a/plugins/http2_adapter/lib/src/http2_adapter.dart
+++ b/plugins/http2_adapter/lib/src/http2_adapter.dart
+ static const _sensitiveRedirectHeaders = {
+ 'authorization',
+ 'cookie',
+ 'proxy-authorization',
+ };
+
- final String path = resolveRedirectUri(options.uri, uri).toString();
+ final redirectUri = resolveRedirectUri(options.uri, uri);
+ final redirectOptions = options.copyWith(
+ path: redirectUri.toString(),
+ maxRedirects: --options.maxRedirects,
+ );
+ if (!_sameOrigin(options.uri, redirectUri)) {
+ redirectOptions.headers.removeWhere(
+ (name, _) => _sensitiveRedirectHeaders.contains(name.toLowerCase()),
+ );
+ }
return _fetch(
- options.copyWith(path: path, maxRedirects: --options.maxRedirects),
+ redirectOptions,
list != null ? Stream.fromIterable(list) : null,
cancelFuture,
redirects,
);
+
+ static bool _sameOrigin(Uri a, Uri b) =>
+ a.scheme ==…
- Record
GHSA-cr65-55v9-xhvv- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
NVD
7.5
Dio is a powerful HTTP client for Dart and Flutter. From 1.0.0 until 2.8.0, dio_http2_adapter follows redirects in Http2Adapter._fetch by copying the original RequestOptions.headers into the redirected HTTP/2 request without comparing the source and destination origins. A server contacted by an application can return a cross-origin Location redirect to a different-origin destination, which then receives preserved Authorization, Cookie, and Proxy-Authorization headers because RequestOptions.copyWith retains the full header map. The issue applies when applications use Http2Adapter for authenticated requests, and redirect following is enabled by default. This issue is fixed in version 2.8.0.
- Record
CVE-2026-103030- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSV
7.5
Dio: Cross-origin redirect leaks sensitive request headers in `dio_http2_adapter`
Dio is a powerful HTTP client for Dart and Flutter. From 1.0.0 until 2.8.0, dio_http2_adapter follows redirects in Http2Adapter._fetch by copying the original RequestOptions.headers into the redirected HTTP/2 request without comparing the source and destination origins. A server contacted by an application can return a cross-origin Location redirect to a different-origin destination, which then receives preserved Authorization, Cookie, and Proxy-Authorization headers because RequestOptions.copyWith retains the full header map. The issue applies when applications use Http2Adapter for authenticated requests, and redirect following is enabled by default. This issue is fixed in version 2.8.0.
- Record
CVE-2026-103030- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGHSA-cr65-55v9-xhvv
7.5
Dio: Cross-origin redirect leaks sensitive request headers in `dio_http2_adapter`
Summary
dio_http2_adapter follows HTTP Location redirects without stripping sensitive request headers when the redirect target is a different origin. An attacker who can cause any origin server to return a cross-origin 302 redirect receives the victim's Authorization, Cookie, and Proxy-Authorization headers verbatim. This vulnerability has a CVSS v3.1 base score of 7.5 (High) because followRedirects is enabled by default, no authentication is required to exploit it, and the confidentiality impact is high.
Details
When Dio is configured to use Http2Adapter, the adapter's internal _fetch method constructs HTTP/2 request headers by unconditionally copying all entries from options.headers:
// plugins/http2_adapter/lib/src/http2_adapter.dart:111
headers.addAll(
options.headers.entries.map((entry) {
...
return Header.ascii(entry.key.toLowerCase(), v);
}).toList(),
);
On receiving a 3xx response, the adapter reads the Location header and resolves the redirect target URI:
// plugins/http2_adapter/lib/src/http2_adapter.dart:254-263
final url = responseHeaders.value('location');
final uri = Uri.parse(url ?? '');
final String path = resolveRedirectUri(options.uri, uri).toString();
return _fetch(options.copyWith(path: path, maxRedirects: --options.maxRedirects), ...);
options.copyWith is called without an explicit headers argument; dio/lib/src/options.dart:629 therefore preserves the full original header map via headers ?? Map.from(this.headers). resolveRedirectUri (http2_adapter.dart:290) returns an attacker-controlled cross-origin URI without any origin comparison. The combination means that on every redirect, the full set of original sensitive headers — including Authorization, Cookie, and Proxy-Authorization — is forwarded to the new authority with no sanitization.
Data flow (source → sink):
dio/lib/src/dio_mixin.dart:383— publicDio.request<T>(String path, ...)entry point.dio/lib/src/options.dart:331— base and per-request headers merged intoRequestOptions.dio/lib/src/dio_mixin.dart:596—httpClientAdapter.fetch(reqOpt, ...)dispatches toHttp2Adapter.plugins/http2_adapter/lib/src/http2_adapter.dart:254—Locationheader is read from the network response (attacker-controlled source).plugins/http2_adapter/lib/src/http2_adapter.dart:258— redirect target URI resolved; cross-origin check absent.plugins/http2_adapter/lib/src/http2_adapter.dart:260—options.copyWith(path: path, ...)called withoutheadersoverride.dio/lib/src/options.dart:629—copyWithpreservesMap.from(this.headers)— all sensitive headers retained.plugins/http2_adapter/lib/src/http2_adapter.dart:111— preserved headers emitted verbatim to the cross-origin HTTP/2 request (sink).
Suggested patch — strip sensitive headers before following a cross-origin redirect:
--- a/plugins/http2_adapter/lib/src/http2_adapter.dart
+++ b/plugins/http2_adapter/lib/src/http2_adapter.dart
+ static const _sensitiveRedirectHeaders = {
+ 'authorization',
+ 'cookie',
+ 'proxy-authorization',
+ };
+
- final String path = resolveRedirectUri(options.uri, uri).toString();
+ final redirectUri = resolveRedirectUri(options.uri, uri);
+ final redirectOptions = options.copyWith(
+ path: redirectUri.toString(),
+ maxRedirects: --options.maxRedirects,
+ );
+ if (!_sameOrigin(options.uri, redirectUri)) {
+ redirectOptions.headers.removeWhere(
+ (name, _) => _sensitiveRedirectHeaders.contains(name.toLowerCase()),
+ );
+ }
return _fetch(
- options.copyWith(path: path, maxRedirects: --options.maxRedirects),
+ redirectOptions,
list != null ? Stream.fromIterable(list) : null,
cancelFuture,
redirects,
);
+
+ static bool _sameOrigin(Uri a, Uri b) =>
+ a.scheme ==…
- Record
GHSA-cr65-55v9-xhvv- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
dio_http2_adapterPub |
1.0.0 to 2.7.1 | 2.8.0 |
dio_http2_adapterPub |
1.0.0 up to 2.8.0, plus listed versions 1.0.12.0.02.0.0-beta12.0.0-beta22.0.0-prev1 Show 14 moreShow less2.1.02.2.02.3.02.3.12.3.1+12.3.22.4.02.5.02.5.12.5.22.5.32.6.02.7.02.7.1 |
2.8.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-103030 is a high vulnerability (CVSS 7.5) in Cfug Dio, published October 13, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 30th percentile. Fixed in dio_http2_adapter 2.8.0.
References
github.com/cfug/dio/security/advisories/GHSA-cr65-55v9-xhvv
github.com/cfug/dio/commit/9d7ac913340084f3b4987b121e076f28d637a81e
github.com/cfug/dio/releases/tag/http2_adapter_v2.8.0
nvd.nist.gov/vuln/detail/CVE-2026-103030
github.com/advisories/GHSA-cr65-55v9-xhvv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/103xxx/CVE-2026-103030.json
github.com/cfug/dio
Left out here: the rest of 2 source descriptions. The JSON record has them.