服务端和客户端证书各种组合下对访问者(浏览器/中间人)的影响

 

今天本来想研究下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("https://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("https://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

posted @ 2021-05-23 18:11  俞正东  阅读(326)  评论(0编辑  收藏  举报