Files
pages/2024/03/16/ssl-pinning.html
2026-02-04 02:55:12 +00:00

328 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>如何用requests库验证证书 | Mayx的博客</title>
<meta name="generator" content="Jekyll v3.9.5" />
<meta property="og:title" content="如何用requests库验证证书" />
<meta name="author" content="mayx" />
<meta property="og:locale" content="zh_CN" />
<meta name="description" content="用Python制作的程序怎么样" />
<meta property="og:description" content="用Python制作的程序怎么样" />
<meta property="og:site_name" content="Mayx的博客" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2024-03-16T00:00:00+08:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="如何用requests库验证证书" />
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
<meta name="yandex-verification" content="fc0e535abed800be" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2024-03-16T00:00:00+08:00","datePublished":"2024-03-16T00:00:00+08:00","description":"用Python制作的程序怎么样","headline":"如何用requests库验证证书","mainEntityOfPage":{"@type":"WebPage","@id":"/2024/03/16/ssl-pinning.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2024/03/16/ssl-pinning.html"}</script>
<!-- End Jekyll SEO tag -->
<link rel="canonical" href="https://mabbs.github.io/2024/03/16/ssl-pinning.html" />
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
<link rel="stylesheet" href="/assets/css/style.css?v=1770173707" />
<!--[if !IE]> -->
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
<!-- <![endif]-->
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
<link rel="me" href="https://github.com/Mabbs" />
<script src="/assets/js/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
var lastUpdated = new Date("Wed, 04 Feb 2026 10:55:07 +0800");
var BlogAPI = "https://summary.mayx.eu.org";
</script>
<script src="/assets/js/main.js"></script>
<!--[if !IE]> -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-137710294-1');
</script>
<script src="/assets/js/instant.page.js" type="module"></script>
<!-- <![endif]-->
</head>
<body>
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript这不影响你的浏览但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer这不影响你的浏览但可能会有一些功能无法使用……</small></marquee><![endif]-->
<div class="wrapper">
<header class="h-card">
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
<p class="p-note">Mayx's Home Page</p>
<form action="/search.html">
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." />&#160;<input type="submit" value="搜索" />
</form>
<br />
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
<ul class="downloads">
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
</ul>
</header>
<section class="h-entry">
<small><time class="date dt-published" datetime="2024-03-16T00:00:00+08:00">16 March 2024</time> - 字数统计1657 - 阅读大约需要5分钟 - Hits: <span id="/2024/03/16/ssl-pinning.html" class="visitors">Loading...</span></small>
<h1 class="p-name">如何用requests库验证证书</h1>
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
<div id="outdate" style="display:none;">
<hr /><p>
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
</p>
</div>
<script>
daysold = Math.floor((new Date().getTime() - new Date("Sat, 16 Mar 2024 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
if (daysold > 90) {
document.getElementById("outdate").style.display = "block";
document.getElementById("outime").innerHTML = daysold;
}
</script>
<hr />
<b>AI摘要</b>
<p id="ai-output">这篇文章讲述了使用Python的requests库实现类似“SSL Pinning”技术的过程。作者发现直接用requests库操作请求时获取证书的常见方法不可行于是通过requests.packages.urllib3的HTTPSConnection连接并修改其connect方法实现了在发起GET请求时获取服务器的证书并计算其SHA256指纹。作者提供了一个函数`verify_cert_request`用于验证网站证书并可作为防抓包策略。文章最后提到虽然Python作为解释型语言其代码可被分析但通过修改依赖库或加密处理可以提高保护程度防止多数抓包行为。</p>
<hr />
<ul><li><a href="#起因">起因</a></li><li><a href="#实现方案">实现方案</a></li><li><a href="#后记">后记</a></li></ul>
<hr />
<main class="post-content e-content" role="main"><p>用Python制作的程序怎么样<!--more--></p>
<h1 id="起因">
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
</h1>
<p>之前在抓包某些APP的时候可能会遇到即使信任了抓包软件的CA根证书也无法抓包的情况听说之所以遇到这种情况是因为那些APP使用了“SSL Pinning”的技术可以只信任代码中认为可以信任的证书。不过对于逆向之类的事情我并不擅长这种问题我也不太会解决。但是不能解决问题我可以创造问题啊Java的APP我不会写但是我会用Python写所以今天来看看怎么样用Python实现类似“SSL Pinning”的技术。</p>
<h1 id="实现方案">
<a href="#实现方案"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 实现方案
</h1>
<p>真正的SSL Pinning似乎是通过预置网站所使用的根证书或者中间证书来实现的这样的好处是即使证书到期换了证书也能继续验证。不过我觉得其实没必要这么麻烦一般Python程序要连接的后端也没必要在浏览器中调用大不了就自签一个证书然后自己验证证书就好了反正中间人攻击重新签的公钥证书的指纹肯定和原来网站公钥证书的指纹不一样用这一点就可以判断有没有被抓包。 </p><p>
不过我搜了一下如果想实现这个功能首先请求的时候就要获得网站的证书很多资料都是直接用socket和ssl这两个包实现的但是在python上请求一般都是用requests用socket操作有点太麻烦了吧再问问AI呢AI给出的回复是<code class="language-plaintext highlighter-rouge">response.raw.connection.getpeercert()</code>结果执行了根本没有这个方法不愧是只会东拼西凑这应该是ssl库的函数吧……要么可以用<code class="language-plaintext highlighter-rouge">urllib3.contrib.pyopenssl.ssl.get_server_certificate()</code>这个方法获取但是这个方法不是在发起请求的时候获取的证书而是直接先访问了一下服务器然后直接获取的证书这样每次调用接口的时候可能就要请求两次服务器了感觉不怎么好……后来去Stack Overflow上搜了一下还真有关于类似这个问题的<a href="https://stackoverflow.com/questions/16903528/how-to-get-response-ssl-certificate-from-requests-in-python">讨论</a>,于是我简单改编了一下,最终效果如下:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="n">HTTPSConnection</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">packages</span><span class="p">.</span><span class="n">urllib3</span><span class="p">.</span><span class="n">connection</span><span class="p">.</span><span class="n">HTTPSConnection</span>
<span class="n">orig_HTTPSConnection_connect</span> <span class="o">=</span> <span class="n">HTTPSConnection</span><span class="p">.</span><span class="n">connect</span>
<span class="k">def</span> <span class="nf">new_HTTPSConnection_connect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">orig_HTTPSConnection_connect</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">peer_certificate</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">getpeercert</span><span class="p">(</span><span class="n">binary_form</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">except</span> <span class="nb">AttributeError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">HTTPSConnection</span><span class="p">.</span><span class="n">connect</span> <span class="o">=</span> <span class="n">new_HTTPSConnection_connect</span>
<span class="k">def</span> <span class="nf">verify_cert_request</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
<span class="k">with</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">[</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">raw</span><span class="p">.</span><span class="n">connection</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">getpeercert</span><span class="p">(</span><span class="n">binary_form</span><span class="o">=</span><span class="bp">True</span><span class="p">)).</span><span class="n">hexdigest</span><span class="p">(),</span> <span class="n">r</span><span class="p">.</span><span class="n">text</span> <span class="p">]</span>
<span class="k">return</span> <span class="n">result</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">verify_cert_request</span><span class="p">(</span><span class="s">'https://www.baidu.com'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][:</span><span class="mi">10</span><span class="p">])</span>
</code></pre></div></div>
<p>用这个代码就能获取到请求的网站中证书的指纹了如果不希望其他人抓包先自己计算一下自己证书的hash指纹然后在代码中执行逻辑的时候先判断一下请求网站的指纹是不是自己网站的指纹如果不是还可以考虑一下反制措施这样就能实现证书的验证了。</p>
<h1 id="后记">
<a href="#后记"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 后记
</h1>
<p>不过Python作为解释型语言代码不是随便看😂就算用Cython然后加壳啥的调用的库依然不是加密的大不了修改依赖的库然后让它返回的结果向正确的凑可能也行不过这样至少能防止绝大多数抓包的人了。</p></main>
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=Python"><em>Python</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=requests"><em>requests</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=ssl"><em>ssl</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2024-03-16-ssl-pinning.md">查看原始文件</a></span></small>
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
<p id="suggest-container">Loading...</p>
<script>
var suggest = $("#suggest-container");
$.get(BlogAPI + "/suggest?id=/2024/03/16/ssl-pinning.html&update=" + lastUpdated.valueOf(), function (data) {
if (data.length) {
getSearchJSON(function (search) {
suggest.empty();
var searchMap = {};
for (var i = 0; i < search.length; i++) {
searchMap[search[i].url] = search[i];
}
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
for (var j = 0; j < data.length; j++) {
var item = searchMap[data[j].id];
if (item) {
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
var contentPreview = item.content.substring(0, 100);
if (item.content.length > 100) {
contentPreview += "……";
}
link.hover(
function(e) {
tooltip.text($(this).data('content'))
.css({
top: e.pageY + 10,
left: e.pageX + 10
})
.show();
},
function() {
tooltip.hide();
}
).mousemove(function(e) {
tooltip.css({
top: e.pageY + 10,
left: e.pageX + 10
});
}).data('content', contentPreview);
suggest.append(link);
suggest.append(' - ' + item.date + '<br />');
}
}
});
} else {
suggest.html("暂无推荐文章……");
}
});
</script>
<br />
<div class="pagination">
<span class="prev">
<a href="/2024/02/24/luckfox.html">
上一篇Luckfox Pico Plus使用体验
</a>
</span>
<br />
<span class="next">
<a href="/2024/04/06/old-pc.html">
下一篇:关于旧电脑的使用探索
</a>
</span>
</div>
<!--[if !IE]> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="/assets/js/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script>
var gitalk = new Gitalk({
clientID: '36557aec4c3cb04f7ac6',
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
repo: 'mabbs.github.io',
owner: 'Mabbs',
admin: ['Mabbs'],
id: '/2024/03/16/ssl-pinning', // Ensure uniqueness and length less than 50
distractionFreeMode: false, // Facebook-like distraction free mode
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
})
gitalk.render('gitalk-container')
</script>
<!-- <![endif]-->
</section>
<!--[if !IE]> -->
<div id="landlord" style="left:5px;bottom:0px;">
<div class="message" style="opacity:0"></div>
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
<div class="live_talk_input_body">
<form id="live_talk_input_form">
<div class="live_talk_input_name_body" >
<input type="checkbox" id="load_this" />
<input type="hidden" id="post_id" value="/2024/03/16/ssl-pinning.html" />
<label for="load_this">
<span style="font-size: 11px; color: #fff;">&#160;想问这篇文章</span>
</label>
</div>
<div class="live_talk_input_text_body">
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
</div>
</form>
</div>
<input name="live_talk" id="live_talk" value="1" type="hidden" />
<div class="live_ico_box" style="display:none;">
<div class="live_ico_item type_info" id="showInfoBtn"></div>
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
<div class="live_ico_item type_music" id="musicButton"></div>
<div class="live_ico_item type_youdu" id="youduButton"></div>
<div class="live_ico_item type_quit" id="hideButton"></div>
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
<input id="duType" value="douqilai" type="hidden" />
</div>
</div>
<div id="open_live2d">召唤伊斯特瓦尔</div>
<!-- <![endif]-->
<footer>
<p>
<small>Made with ❤ by Mayx<br />Last updated at 2026-02-04 10:55:07<br /> 总字数614622 - 文章数178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
</p>
</footer>
</div>
<script src="/assets/js/scale.fix.js"></script>
<!--[if !IE]> -->
<script src="/assets/js/main_new.js"></script>
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
<script src="/Live2dHistoire/live2d/js/message.js"></script>
<!-- <![endif]-->
</body>
</html>