Skip to content

fix: restrict SSL certificate bypass to localhost only - #2204

Open
Marnie0415 wants to merge 1 commit into
chen08209:mainfrom
Marnie0415:fix/global-cert-validation-bypass
Open

fix: restrict SSL certificate bypass to localhost only#2204
Marnie0415 wants to merge 1 commit into
chen08209:mainfrom
Marnie0415:fix/global-cert-validation-bypass

Conversation

@Marnie0415

@Marnie0415 Marnie0415 commented Jul 10, 2026

Copy link
Copy Markdown

Problem

lib/common/http.dart:27 disables SSL certificate validation for all HTTP connections:

client.badCertificateCallback = (_, _, _) => true;

This affects subscription downloads, update checks, and WebDAV sync — all of which connect to external servers over HTTPS. An attacker on the same network can perform man-in-the-middle attacks to inject malicious proxy nodes, redirect updates, or steal credentials.

Why the bypass exists

The app routes external traffic through the local ClashMeta proxy via handleFindProxy, which returns PROXY localhost:$mixedPort. The proxy handles outbound TLS. The Flutter app only needs certificate bypass for local proxy connections, not external endpoints.

Fix

Restrict badCertificateCallback to only bypass validation for local addresses:

client.badCertificateCallback = (X509Certificate cert, String host, int port) {
  return host == localhost || host == 'localhost';
};

Why two checks

  • localhost is const localhost = '127.0.0.1' (lib/common/constant.dart:55) — the IP address
  • 'localhost' is the hostname string — this is what handleFindProxy returns in the proxy URL (PROXY localhost:$mixedPort)

When the HTTP client connects through the proxy, it uses the hostname from the proxy URL. Depending on DNS resolution and platform behavior, the host parameter in the callback may receive either '127.0.0.1' or 'localhost'. Both must be handled.

Scope

Connection type Before After
127.0.0.1 (local proxy) Bypass Bypass
localhost hostname (local proxy) Bypass Bypass
External HTTPS (subscriptions, updates, WebDAV) Bypass Validate

IPv6

IPv6 (::1) is not included because the codebase consistently uses IPv4 for local connections. The localhost constant, defaultExternalController, and proxy configuration all use 127.0.0.1. If IPv6 support is added later, the callback can be extended.

@Marnie0415
Marnie0415 force-pushed the fix/global-cert-validation-bypass branch 2 times, most recently from c8e4a85 to 729d98f Compare July 10, 2026 18:34
The previous implementation disabled certificate validation for ALL
HTTP connections via \�adCertificateCallback = (_, _, _) => true\.
This made every outbound request (subscription downloads, update checks,
WebDAV sync) vulnerable to man-in-the-middle attacks.

Certificate bypass is only needed for localhost connections where the
local proxy may use self-signed certificates. External connections
should use proper certificate validation.
@Marnie0415
Marnie0415 force-pushed the fix/global-cert-validation-bypass branch from 729d98f to 3a462e2 Compare July 10, 2026 18:36
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.

1 participant