Skip to content

在flutter/dart中使用ip进行https请求

Posted on:2023年12月1日 at 18:22

在flutter/dart中使用ip进行https请求

为什么要这样做

在已知目标服务器的IP情况下, 直接通过IP访问可以避免DNS污染, 避免出现因错误的DNS解析导致无法访问的情况

要解决的问题

Dart的HttpClient没有给出验证证书的接口, 直接通过IP访问会导致证书验证出错.

我们查询google.com, 得到IP地址59.24.3.174, 然后直接进行访问

import "package:http/http.dart" as http

void main(){
    http.get(Uri.parse("https://172.217.163.46"));
}

出现错误

Unhandled exception:
HandshakeException: Handshake error in client (OS Error: 
	CERTIFICATE_VERIFY_FAILED: IP address mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393))

dart的HttpClient给了一个badCertificateCallback的接口, 但很遗憾, 我们没办法校验证书, 只能这样做

import 'dart:io';
import 'package:http/http.dart' as http;

class CustomOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    var client =  super.createHttpClient(context);
    client.badCertificateCallback = (cert, host, port){
      // 没有验证cert的方式
      return true;
    };
    return client;
  }
}

void main() async{
  HttpOverrides.global = CustomOverrides();
  http.get(Uri.parse("https://172.217.163.46"));
}

这样做确实解决了成功访问, 但是安全性存在严重问题

建立代理服务器

要解决这个问题, 我们可以通过代理服务器的方式解决这个问题, 在我们的程序中, 仍然使用域名访问, 在代理服务器中将流量转发至对应的IP

建立代理服务器

直接使用dart:io即可

import 'dart:convert';
import 'dart:io';

class HttpProxyRequest {
  String host;
  int port;

  final void Function() stop;

  HttpProxyRequest(this.host, this.port, this.stop);
}

class _HttpProxyHandler {
  var content = "";
  late Socket client;
  Socket? serverSocket;

  void handle(
      Socket c, void Function(HttpProxyRequest request) onRequest) async {
    try {
      client = c;
      await for (var d in client) {
        if (serverSocket == null) {
          content += Utf8Decoder().convert(d);
          if (content.contains("\n")) {
            if (content.split(" ").first != "CONNECT") {
              // 不知道为什么在Windows上有个程序会发来Get请求
              client
                  .write("HTTP/1.1 400 Bad Request\nContent-Type: text/plain\n"
                      "Content-Length: 29\n\nBad Request: Invalid Request");
              client.flush();
              client.close();
              return;
            }
            var uri = content
                .split('\n')
                .first
                .split(" ")
                .firstWhere((element) => element.contains(":"));
            bool stop = false;
            var request = HttpProxyRequest(
                uri.split(":").first, int.parse(uri.split(":").last), () {
              stop = true;
            });
            onRequest(request);
            if (stop) {
              client.close();
              return;
            }
            forward(request.host, request.port);
          }
        }
        if (serverSocket != null) {
          serverSocket!.add(d);
        }
      }
      close();
    } catch (e) {
      close();
    }
  }

  void close() {
    try {
      client.close();
      serverSocket?.close();
    } catch (e) {
        // 忽视已经关闭的情况
    }
  }

  void forward(String host, int port) async {
    try {
      serverSocket = await Socket.connect(host, port);
      serverSocket?.listen((event) {
        client.add(event);
      }, onDone: () {
        client.close();
        serverSocket = null;
      }, onError: (e) {
        client.close();
        serverSocket = null;
      }, cancelOnError: true);
      client.write('HTTP/1.1 200 Connection Established\r\n\r\n');
      client.flush();
    } catch (e) {
      close();
    }
  }
}

class HttpProxyServer {
  void onRequest(HttpProxyRequest request) {
    if (request.host == "google.com") {
      request.host = "172.217.163.46";
    }
  }


  Future<void> run(int port) async {
    var socket = await ServerSocket.bind(InternetAddress.anyIPv4, port);
    socket.listen((event) => _HttpProxyHandler().handle(event, onRequest));
  }
}

发起请求

import 'dart:io';
import 'package:http/http.dart' as http;
import 'proxy_server.dart';

class CustomOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    var client =  super.createHttpClient(context);
    client.findProxy = (uri) => "PROXY localhost:7891;";
    return client;
  }
}

void main() async{
  HttpProxyServer().run(7891);
  HttpOverrides.global = CustomOverrides();
  http.get(Uri.parse("https://google.com"));
}

这样就成功访问了