Part 1: Defending Against Botnets: Why IP Rate Limiting Fails and How to Implement JA4 Fingerprinting on AWS

P

If you have ever tried to stop a sophisticated scraper or a DDoS attack using standard AWS WAF rate limits, you likely ran into the wall with bigboard: IP rotation.

Traditionally, rate-based rules are simple. The WAF counts requests from a specific key (usually an IP address) in a rolling 5-minute window. If that IP crosses a threshold, the WAF blocks it.

This works great for sloppy scripts. But modern botnets and LLM scrapers are smarter. They rotate their IPs for every few requests. You might see 10,000 requests, but they come from 10,000 different IPs. To the WAF, each IP looks innocent, sending only 1 request per minute.

So, how do we catch a ghost? We stop looking at where they come from (IP) and start looking at what they are (TLS Fingerprint).

Enter JA4 Fingerprinting

JA4 is a fingerprint of the TLS “ClientHello” packet. Think of it as the “shape” of the TLS configuration.

When a browser or a bot connects to your server, it proposes a set of cipher suites, TLS versions, and extensions. This combination creates a unique signature.

  • It is not a user ID: Many people can share the same JA4 (e.g., millions of users on Chrome/Windows share the same hash).
  • It is sticky for bots: While a scanner can rotate IPs every 20 seconds, it rarely rotates its underlying TLS stack or HTTP client library.

This makes JA4 excellent for high-level flood detection. We can catch a scanner that is rotating thousands of IPs because they are all using the exact same (and often weird) TLS profile.

The Missing Link: CloudFront

Here is the catch: AWS WAF does not magically “know” the JA4 fingerprint. It’s calculated at the edge. As of October 2024, CloudFront supports JA4 native forwarding, but you have to explicitly ask for it.

If you don’t forward the header, your WAF sees nothing. Here is the Terraform code to create an Origin Request Policy that forwards the CloudFront-Viewer-JA4-Fingerprint header:

resource "aws_cloudfront_origin_request_policy" "all_viewer_with_ja4" {
  name    = "all-viewer-with-ja4-policy"
  comment = "Policy to forward all viewer headers and the JA4 fingerprint"

  cookies_config {
    cookie_behavior = "all"
  }

  query_strings_config {
    query_string_behavior = "all"
  }

  headers_config {
    # CRITICAL CONFIGURATION
    # "allViewerAndWhitelistCloudFront" ensures that all headers sent by the client
    # are forwarded, PLUS the specific CloudFront headers we list below.
    header_behavior = "allViewerAndWhitelistCloudFront"

    headers {
      items = [
        "CloudFront-Viewer-JA4-Fingerprint",
      ]
    }
  }
}

Once this is applied to your CloudFront distribution, the fingerprint becomes available to WAF as a regular HTTP header. It is also good idea to extend Athena scheme for WAF logs with ja4 new filed, which would be useful at further statistical analyzing.

In Part 2, I will show you the exact WAF rule I use to ban these botnets, and more importantly, how to use Athena to statistically tune your thresholds so you don’t accidentally ban legitimate users.

Want to go deeper? If you are setting up defenses for high-traffic environments, checking the TLS fingerprint is just the start. In my course DevSecOps on AWS: Defend Against LLM Scrapers & Bot Traffic, we go through the full implementation of these signals to build a defense-in-depth strategy.


About the author

sergii-demianchuk

Software engineer with over 18 year’s experience. Everyday stack: PHP, Python, Java, Javascript, Symfony, Flask, Spring, Vue, Docker, AWS Cloud, Machine Learning, Ansible, Terraform, Jenkins, MariaDB, MySQL, Mongo, Redis, ElasticSeach. Udemy instructor with over 35K students.

architecture AWS cluster cyber-security devops devops-basics docker elasticsearch flask geo high availability java machine learning opensearch php programming languages python recommendation systems search systems spring boot symfony

Privacy Overview
Sergii Demianchuk Blog

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.

Strictly Necessary Cookies

Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.

3rd Party Cookies

This website uses Google Analytics to collect anonymous information such as the number of visitors to the site, and the most popular pages.

Keeping this cookie enabled helps us to improve our website.