boringhyper
Impersonate chrome BoringSSL JA3 fingerprint to pass request sent using Rustโขยฎ (
Live in harmony with nature and don't waste more valuable energy on running headless browser to bypass simple thing!
Symptoms of website using JA3 fingerprint that this repo can help solve:
- returns 403 and requires you to pass js/captcha challenge when requested programmatically even with same headers as browser
- page when visited first time from modern browser loads instantly without "Checking your browser" WAF.
Credits and resources
- https://github.com/4JX/reqwest-impersonate - it is fork of reqwest, hyper and h2 which requires to be updated to upstream. Currently, in my case I can't even manage it to work due to different versions of hyper in project and, unfortunately, I can't keep it in sync with reqwest, hyper and h2 upstream. It is more advanced, and it also tries to impersonate chrome http2 fingerprint, but I think my composition of raw hyper and boring will be easier to maintain (basically no maintenance related to reqwest updates) and it is currently enough for my usage.
- https://scrapfly.io/web-scraping-tools/ja3-fingerprint - easily compare ja3 fingerprint without struggling with wireshark
Usage
use boringhyper::ChromeHeadersExt;
use hyper::{Body, Request};
async fn visit_cf() {
const ENTERPRISE_CF_URL: &str = "https://www.canva.com/pl_pl/";
let client = boringhyper::create_client();
let req = Request::builder()
.uri(ENTERPRISE_CF_URL)
.with_chrome_headers()
.body(Body::empty())
.unwrap();
let mut resp = client.request(req).await.expect("Could not do request");
assert_eq!(resp.status(), 200);
// Since WAF requires header "accept-encoding: gzip(, deflate, br)" to be sent,
// `boringhyper` contains extension on hyper::Response to read body decompressed if
// server replied with compressed content.
// See: `ReadBodyExt::read_body`.
let body = resp.read_body().await.expect("Could not read body");
let body_str = String::from_utf8_lossy(&body);
println!("Response body: {body_str}");
}