blog/_posts/2024-03-16-ssl-pinning.md

3.5 KiB
Raw Blame History

layout title tags
post 如何用requests库验证证书
Python
requests
ssl

用Python制作的程序怎么样

起因

之前在抓包某些APP的时候可能会遇到即使信任了抓包软件的CA根证书也无法抓包的情况听说之所以遇到这种情况是因为那些APP使用了“SSL Pinning”的技术可以只信任代码中认为可以信任的证书。不过对于逆向之类的事情我并不擅长这种问题我也不太会解决。但是不能解决问题我可以创造问题啊Java的APP我不会写但是我会用Python写所以今天来看看怎么样用Python实现类似“SSL Pinning”的技术。

实现方案

真正的SSL Pinning似乎是通过预置网站所使用的根证书或者中间证书来实现的这样的好处是即使证书到期换了证书也能继续验证。不过我觉得其实没必要这么麻烦一般Python程序要连接的后端也没必要在浏览器中调用大不了就自签一个证书然后自己验证证书就好了反正中间人攻击重新签的公钥证书的指纹肯定和原来网站公钥证书的指纹不一样用这一点就可以判断有没有被抓包。
不过我搜了一下如果想实现这个功能首先请求的时候就要获得网站的证书很多资料都是直接用socket和ssl这两个包实现的但是在python上请求一般都是用requests用socket操作有点太麻烦了吧再问问AI呢AI给出的回复是response.raw.connection.getpeercert()结果执行了根本没有这个方法不愧是只会东拼西凑这应该是ssl库的函数吧……要么可以用urllib3.contrib.pyopenssl.ssl.get_server_certificate()这个方法获取但是这个方法不是在发起请求的时候获取的证书而是直接先访问了一下服务器然后直接获取的证书这样每次调用接口的时候可能就要请求两次服务器了感觉不怎么好……后来去Stack Overflow上搜了一下还真有关于类似这个问题的讨论,于是我简单改编了一下,最终效果如下:

import requests
import hashlib

HTTPSConnection = requests.packages.urllib3.connection.HTTPSConnection
orig_HTTPSConnection_connect = HTTPSConnection.connect
def new_HTTPSConnection_connect(self):
    orig_HTTPSConnection_connect(self)
    try:
        self.peer_certificate = self.sock.getpeercert(binary_form=True)
    except AttributeError:
        pass
HTTPSConnection.connect = new_HTTPSConnection_connect

def verify_cert_request(url):
    with requests.get(url, stream=True, verify=False) as r:
        result = [ hashlib.sha256(r.raw.connection.sock.getpeercert(binary_form=True)).hexdigest(), r.text ]
    return result

result = verify_cert_request('https://www.baidu.com')
print(result[0])
print(result[1][:10])

用这个代码就能获取到请求的网站中证书的指纹了如果不希望其他人抓包先自己计算一下自己证书的hash指纹然后在代码中执行逻辑的时候先判断一下请求网站的指纹是不是自己网站的指纹如果不是还可以考虑一下反制措施这样就能实现证书的验证了。

后记

不过Python作为解释型语言代码不是随便看😂就算用Cython然后加壳啥的调用的库依然不是加密的大不了修改依赖的库然后让它返回的结果向正确的凑可能也行不过这样至少能防止绝大多数抓包的人了。