[dart:io] Enable insecure connection ban for HttpClient.

We are reverting socket level checks at https://dart-review.googlesource.com/c/sdk/+/161581.

Instead, we are moving the checks to upper protocols
such as http and grpc.

Change-Id: Ice1a6bf818810f59937c19b48f5a68bb512c018d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161801
Commit-Queue: Mehmet Fidanboylu <mehmetf@google.com>
Reviewed-by: Jonas Termansen <sortie@google.com>
This commit is contained in:
Mehmet Fidanboylu 2020-09-10 17:42:29 +00:00 committed by commit-bot@chromium.org
parent 1c21e4763a
commit ae0fbde71c
7 changed files with 209 additions and 0 deletions

View file

@ -2412,6 +2412,9 @@ class _HttpClient implements HttpClient {
}
bool isSecure = uri.isScheme("https");
if (!isSecure && !isInsecureConnectionAllowed(uri.host)) {
throw new StateError("Insecure HTTP is not allowed by platform: $uri");
}
int port = uri.port;
if (port == 0) {
port =

View file

@ -0,0 +1,73 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
import "http_bind_test.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHttps() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithLoopback(),
testWithIPv6(),
testWithHttps(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,29 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
Future<void> testWithHostname() async {
final httpClient = new HttpClient();
final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
asyncExpectThrows(
() async => await httpClient.getUrl(uri),
(e) =>
e is StateError &&
e.message.contains("Insecure HTTP is not allowed by platform"));
}
main() {
asyncStart();
testWithHostname().then((_) => asyncEnd());
}

View file

@ -16,6 +16,7 @@ no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_la
[ $system == android ]
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.

View file

@ -0,0 +1,73 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
import "http_bind_test.dart";
Future<String> getLocalHostIP() async {
final interfaces = await NetworkInterface.list(
includeLoopback: false, type: InternetAddressType.IPv4);
return interfaces.first.addresses.first.address;
}
Future<void> testBanHttp(String serverHost,
Future<void> testCode(HttpClient client, Uri uri)) async {
final httpClient = new HttpClient();
final server = await HttpServer.bind(serverHost, 0);
final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
try {
await testCode(httpClient, uri);
} finally {
httpClient.close(force: true);
await server.close();
}
}
Future<void> testWithLoopback() async {
await testBanHttp("127.0.0.1", (httpClient, uri) async {
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
await asyncTest(() async =>
await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
});
}
Future<void> testWithIPv6() async {
if (await supportsIPV6()) {
await testBanHttp("::1", (httpClient, uri) async {
await asyncTest(() => httpClient.getUrl(uri));
});
}
}
Future<void> testWithHttps() async {
await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
asyncExpectThrows(
() => httpClient.getUrl(Uri(
scheme: 'https',
host: uri.host,
port: uri.port,
)),
(e) => e is SocketException || e is HandshakeException);
});
}
main() {
asyncStart();
Future.wait(<Future>[
testWithLoopback(),
testWithIPv6(),
testWithHttps(),
]).then((_) => asyncEnd());
}

View file

@ -0,0 +1,29 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test file disallows VM from accepting insecure connections to all
// domains and tests that HTTP connections to non-localhost targets fail.
// HTTPS connections and localhost connections should still succeed.
// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
Future<void> testWithHostname() async {
final httpClient = new HttpClient();
final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
asyncExpectThrows(
() async => await httpClient.getUrl(uri),
(e) =>
e is StateError &&
e.message.contains("Insecure HTTP is not allowed by platform"));
}
main() {
asyncStart();
testWithHostname().then((_) => asyncEnd());
}

View file

@ -16,6 +16,7 @@ no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_la
[ $system == android ]
entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.