服務端和客戶端證書各種組合下對訪問者(瀏覽器/中間人)的影響

 

今天本來想研究下nginx下如果獲取SSL指紋,但是環境沒有裝成功

image

就嘗試了下如果不用nginx直接在服務端拿到SSL指紋, 沒想到從造自簽名證書到如何開啟證書, 以及服務端證書和客戶端證書各種組合校驗的測試就花了我很長時間。

所以SSL指紋就下一次在研究吧,有這方面經驗的朋友歡迎加我微信交流(文末)!

本文介紹了服務端證書和客戶端證書各種組合下,對於訪問者(瀏覽器/中間人)的影響

1. 開啟服務端SSL


  public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseKestrel(options =>
            {
                options.ListenLocalhost(5002, listenOption =>
               {
                //設置證書
                   var httpsOptions = new HttpsConnectionAdapterOptions();
                   var serverCert = new X509Certificate2("server.pfx", "1234");
                   httpsOptions.ServerCertificate = serverCert;
                   listenOption.UseHttps(httpsOptions);
               });
            });
            webBuilder.UseStartup<Startup>();
        });

server.pfx是自己創建的證書,需要加到根信任中心

image

  • 瀏覽器訪問https沒問題
  • 中間人抓包沒問題
  • 客戶端HttpClient訪問沒問題

image

2. 開啟客戶端證書驗證

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseKestrel(options =>
        {
            options.ListenLocalhost(5002, listenOption =>
           {
            //設置證書
               var httpsOptions = new HttpsConnectionAdapterOptions();
               //設置需要驗證客戶端正常開始
               httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
               //設置需要驗證客戶端正常結束
               
               
               var serverCert = new X509Certificate2("server.pfx", "1234");
               httpsOptions.ServerCertificate = serverCert;
               listenOption.UseHttps(httpsOptions);
           });
        });
        webBuilder.UseStartup<Startup>();
    });

只需要加一句就搞定

但是注意 如果開啟了客戶端證書驗證。瀏覽器就無法訪問了。 中間人攻擊如果沒有證書的話 也無法完成!

image

  • 瀏覽器無法訪問
  • 中間人無證書無法抓包
  • 客戶端HttpClient得配置證書才能訪問(下面)

3. 客戶端用HttpClient加證書可以訪問成功


private static async Task<string> GetApiDataAsync()
{
    try
    {
        //注意哈 這裡要和服務端用的證書一樣才行
        var cert = new X509Certificate2("server.pfx", "1234");
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(cert);

        var client = new HttpClient(handler);
        
        var request = new HttpRequestMessage()
        {
            RequestUri = new Uri("//localhost:5002/WeatherForecast"),
            Method = HttpMethod.Get,
        };

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            var responseContent = await response.Content.ReadAsStringAsync();
            return responseContent;
        }

        throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
    }
    catch (Exception e)
    {
        throw new ApplicationException($"Exception {e}");
    }
}
    

image

4. 服務端開啟客戶端證書校驗


public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseKestrel(options =>
        {
            options.ListenLocalhost(5002, listenOption =>
           {
            //設置證書
               var httpsOptions = new HttpsConnectionAdapterOptions();
               //設置需要驗證客戶端正常開始
               httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
               //設置需要驗證客戶端正常結束
               
               //設置服務端驗證客戶端 開啟SSL Pinning start
               httpsOptions.CheckCertificateRevocation = true;
               httpsOptions.ClientCertificateValidation +=
                   (certificate2, chain, arg3) =>
                   {
                       //return true;
                       //this is where we verify the thumbprint of a connected client matches the thumbprint we expect
                       //NOTE: this is just a simple example of verifying a client cert.
                       // 2A39D43A8FE2CAE54542C768F61AE79097FAB6F5 這個是我那個證書的 測試的話需要換下
                       return certificate2.Thumbprint.Equals(
                          "2A39D43A8FE2CAE54542C768F61AE79097FAB6F5",
                          StringComparison.InvariantCultureIgnoreCase);
                   };               
               
               //設置服務端驗證客戶端 開啟SSL Pinning end
               var serverCert = new X509Certificate2("server.pfx", "1234");
               httpsOptions.ServerCertificate = serverCert;
               listenOption.UseHttps(httpsOptions);
           });
        });
        webBuilder.UseStartup<Startup>();
    });

注意:服務端開啟驗證的前提是需要客戶端傳證書

相比上面一步,更加保證了客戶端傳的證書的安全性。

而且只要約束了客戶端傳證書,中間人抓https的包如果拿不到證書是無法抓的!!

  • 瀏覽器無法訪問
  • 中間人無證書無法抓包
  • 客戶端HttpClient得配置證書才能訪問(同上)

5. 客戶端開啟SSL Pinning

不需要客戶端傳證書(不需要服務端強制開啟客戶端傳證書)

只在客戶端驗證服務端證書。

客戶端HttpClient訪問增加開啟 SSL Pinning


var handler = new HttpClientHandler();
var client = new HttpClient(handler);
//增加開啟 SSL Pinning
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
    //2A39D43A8FE2CAE54542C768F61AE79097FAB6F5 這個是我那個證書的 測試的話需要換下
    return cert.Thumbprint.Equals("2A39D43A8FE2CAE54542C768F61AE79097FAB6F5",
        StringComparison.CurrentCultureIgnoreCase);
};

var request = new HttpRequestMessage()
{
    RequestUri = new Uri("//localhost:5002/WeatherForecast"),
    Method = HttpMethod.Get,
};

var response = await client.SendAsync(request);

image

試下對中間人攻擊有沒有效果

開啟並設置Fiddler代理

handler.Proxy = new WebProxy("127.0.0.1:8888");

image

如上圖,啟動中間人攻擊後的服務端證書簽名變了

  • 瀏覽器可訪問
  • 中間人無證書情況無法抓包

綜上 在客戶端啟動SSL pinning 最簡單,成本最小,其他語言都有比較簡單的實現

上面演示的證書鏈只有一條,驗證的時候也可以對整個證書鏈遍歷,每個都驗證最為穩妥!

不過不能以為客戶端開啟了SSL Pinning就安全了,因為客戶端有Hook(xpose,frida).

SSL指紋有感興趣的可以加我交流,備註下ssl。

image

Tags: