当前位置: 首页 > 后端技术 > Java

突破神奇的Cloudflare防火墙

时间:2023-04-02 09:51:22 Java

背景最近遇到一个神奇的网站,可以在浏览器中打开,但是通过curl或者code访问直接403。我猜这肯定是UA验证,所以请求的时候把浏览器的UA带上,然后发现还是403,不过这个对我来说不难,肯定还有其他请求头需要验证,直接打开在浏览器中network,复制所有的请求头带上,确保我的请求在http协议层面和浏览器完全一致,所以不可能失败,但是运行之后还是403.放个地址:https://pixabay.com认为服务器验证客户端没有什么黑魔法,因为他们都是通过TCP协议通信的,不可能浏览器发一个HTTP报文而我发相同的HTTP消息服务器可以识别它。由于不是HTTP层进行校验,可能只是在TLS层进行校验,所以我用wireshark抓包,看看能不能发现TLS握手的区别。众所周知,有一个客户端向服务器发送的ClientHello消息很可能是用来区分浏览器请求和非浏览器请求的,因为在这个消息中,客户端需要告诉服务器支持的密码套件、TLS版本等等等信息,而这些信息会根据客户端的实现而有所不同。首先抓取一个普通浏览器请求的数据包,如下图:然后通过curl访问抓包,如下图;可以看到两边的消息确实有很大的不同。经过一一比对,发现403很可能是curl请求报文中缺少supported_versions扩展信息造成的。浏览器端的扩展信息内容如图:表示支持TLSv1.2和TLSv1。3、并且最终握手后的协议也切换到了TLSv1.3,从上面两张对比图可以看出,浏览器使用的是TLSv1.3,而curl使用的是TLSv1.2,可能需要使用TLSv1。3访问成功。验证后马上google了一下如何指定curl的TLS版本,发现只需要加上--tlsv1.3参数即可,如下:$curl-I--tlsv1.3'https://pixabay.com/'\>-H'接受语言:zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'\>-H'用户-agent:Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/103.0.5060.114Safari/537.36Edg/103.0.1264.49'HTTP/2200date:Fri,22Jul202202:格林威治标准时间40:35内容类型:文本/html;charset=utf-8cf-ray:72e8cffc18c73d5a-HKGcache-control:s-maxage=86400content-language:envary:接受编码,Cookie,接受语言cf-cache-status:MISScontent-security-policy:frame-ancestorsnoneexpect-ct:max-age=604800,report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"referrer-policy:strict-origin-when-cross-originx-frame-options:DENYset-cookie:__cf_bm=Cy4a751rDND6kHhu.RzEr5DpqnaxRdpUxaMfNfkya0A-1658457635-0-AS1DaewDqNjWHZ/m74A88bNyEG0EFsZAwmsm/ON5QQEuh8B6XOS7PkSnhGgXPLV+LtEvzOKTy/WWHmwY63uGlD0=;路径=/;expires=格林威治标准时间22-Jul-22星期五03:10:35;域名=.pixabay.com;仅限HTTP;安全的;SameSite=Noneserver:cloudflarealt-svc:h3=":443";ma=86400,h3-29=":443";ma=86400经过反复验证,发现除了指定tlsv1.3外,还需要加上accept-language和user-agent头,而且必须是http2协议。三个条件缺一不可上面提到的Nodejs访问必须使用http2协议,而现在市面上流行的http客户端基本只支持http1.1,所以只能直接从基础库入手。有一个官方的http2库。经过一些调整,也是请求成功,代码如下:consthttp2=require("http2");functionget(host,path){returnnewPromise((resolve,reject)=>{constsession=http2.connect(`https://${host}`,{minVersion:"TLSv1.3",maxVersion:"TLSv1.3",});session.on("error",(err)=>{reject(err);});constreq=session.request({[http2.constants.HTTP2_HEADER_AUTHORITY]:主机,[http2.constants.HTTP2_HEADER_METHOD]:http2.constants.HTTP2_METHOD_GET,[http2.constants.HTTP2_HEADER_PATH]:路径,"user-agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/100.0.4896.127Safari/537.36Edg/100.0.1185.50",});req.setEncoding("utf8");让数据="";req.on("数据",(块)=>{数据+=块;});req.on("end",()=>{session.close();if(data){try{resolve(data);}catch(e){reject(e);}}});req.on("error",(err)=>{reject(err);});请求结束();});}(asyncfunction(){constdata=awaitget("pixabay.com","/");console.log(data);})();深入虽然已经请求成功,但是本着探索的精神,继续寻找cloudflare。有官方博客专门介绍这种TLS拦截技术,链接如下:https://blog.cloudflare.com/monsters-in-the-middleboxes/有一段内容也证明了我的猜想,翻译如下如下:也就是说cloudflare会维护一套浏览器TLS指纹。当收到ClientHello请求时,它会检查这组指纹。如果不匹配,则拦截请求,可以拦截大部分不是来自浏览器的请求。不定期在公众号分享JAVA、Golang、前端、docker、k8s等干货知识。