|8 min read|Yvann Lièvre

E-commerce Credential Stuffing: WAF Rules for ATO Defense (JA4, ASN Thresholds)

Credential stuffing detection for e-commerce WAF: JA4 fingerprinting, ASN thresholds, and graduated responses that stop bots without blocking customers.

WAFE-commerceCredential StuffingATO
E-commerce Credential Stuffing: WAF Rules for ATO Defense (JA4, ASN Thresholds)

An online store logs tens of thousands of login attempts every night on /login. No targeted phishing, no manual guessing: credentials stolen in a third-party breach, replayed en masse to see which ones still work. That is credential stuffing, and retail absorbs a disproportionate share of it, because a compromised customer account unlocks stored cards, loyalty points, and resellable personal data.

The naive answer, blocking by source IP after N failures, collapses against modern bot fleets. Here is the method that actually works, with the rules to deploy.

Why single-IP blocking no longer works

Credential stuffing operators rent residential proxy pools: tens of thousands of IP addresses belonging to real households, harvested through ad SDKs or compromised mobile apps. Each login attempt originates from a different IP, sometimes a single request per address before rotation. A threshold like "5 failures in 10 minutes per IP" never fires, because no individual IP ever exceeds one or two attempts.

This distributed, low-and-slow pattern is the signature of professional credential stuffing, distinct from a classic brute-force attack hammering from a handful of addresses. The countermeasure cannot rely on IP alone: requests belonging to the same fleet need to be grouped, and thresholds need to operate at an aggregation level broader than the IP.

JA4/JA4H fingerprinting: grouping a fleet despite rotating IPs

The JA4 fingerprint (TLS negotiation) and JA4H (HTTP headers) identify the software stack issuing a request, independent of the source address. An automation tool (headless Chrome driven by Puppeteer, a custom HTTP client, a credential-stuffing framework like OpenBullet) leaves a stable TLS and HTTP signature, even as the operator cycles requests across thousands of different residential IPs.

In practice, two requests coming from two completely distinct IPs but sharing the same JA4 are very likely issued by the same tool, and therefore possibly the same operator. That is what lets you reconstruct a distributed fleet as a single logical group.

Example of enriched WAF logging (simplified format):

event: login_attempt
timestamp: 2026-07-17T02:14:33Z
src_ip: 88.161.203.44
ja4: t13d1516h2_8daaf6152771_02713d6af862
ja4h: ge11nn05enus_2b7f4a1c8e9d
uri: /login
status: 401

When hundreds of distinct IPs converge on the same JA4/JA4H pair against /login, the signal is unambiguous: this is no longer "a user mistyping a password," it is a fleet. That grouping becomes the aggregation key for every threshold that follows, replacing the IP.

Threshold by ASN and network block, not by single IP

Even with JA4 fingerprinting, a second aggregation axis is needed: the autonomous system number (ASN) and the origin network block (CIDR). Residential proxy pools are, by construction, concentrated on a limited number of providers and IP ranges, even as the individual addresses change on every request.

A reasonable threshold looks like this, as rule logic rather than a fixed implementation:

# Pseudo-rule: aggregated threshold by ASN on /login
# 5-minute sliding window
IF uri == "/login" AND method == "POST"
   AND count(requests GROUP BY asn) > 200 IN 5m
   AND fail_rate(asn) > 0.85
THEN action = challenge

The ASN threshold catches what a single-IP threshold never sees: any given IP may appear only once, but if three hundred requests from the same ASN fail on /login within five minutes, with a failure rate above 85 percent, the source is a bot pool, not three hundred unlucky customers of the same provider.

Correlating abnormal /login failure with /checkout behavior

The most reliable signal is not an isolated threshold, it is a behavioral divergence between two endpoints of the same customer journey. A real customer who fails to log in retries two or three times, then abandons or uses "forgot password." A credential-stuffing bot fires hundreds of attempts per minute against /login, with a failure rate near 90 to 95 percent, and never reaches /checkout, because its only goal is validating username/password pairs, not buying anything.

The useful correlation, then, is: abnormally high failure rate on /login, paired with a conversion rate to /checkout near zero for the same group (JA4 or ASN). Legitimate traffic, even during a sale spike, converts a meaningful share of successful logins into cart visits. Credential-stuffing traffic never converts, by nature.

Avoiding false positives: CGNAT and graduated response

The classic trap of ASN or network-block thresholds: blocking an entire mobile carrier running Carrier-Grade NAT (CGNAT), where tens of thousands of real customers share a handful of public IPs. An overly aggressive threshold turns an attack into a major customer-impacting incident.

Three safeguards are non-negotiable:

  • Graduated response, never a hard block on first contact. The first reaction to a threshold breach is a challenge (JavaScript check, CAPTCHA), not an HTTP 403. A real customer clears the challenge without noticing; a basic bot fails and stops there.
  • Detection mode before blocking mode. Every new threshold rule ships in pure observation first, for a proving period (at least two weeks, ideally spanning a traffic spike), to measure the false-positive volume against real traffic before enabling the block.
  • Whitelist known mobile ASNs. Major mobile carriers and consumer ISPs identified as CGNAT get a relaxed threshold or a reduced weight in the score, so a legitimate spike (product launch, influencer campaign) does not trigger a mass block.

Before/after example on a real over-aggressive configuration:

Before (naive per-IP threshold, no JA4/ASN distinction): 5 failures per IP in 10 minutes, immediate block. Result: the residential fleet stays entirely under the threshold (1 to 2 attempts per IP), zero detections, and a mobile subscriber behind a shared CGNAT gets blocked after three other customers on the same IP block happened to fail a login the same day.

After (aggregated JA4 + ASN threshold, graduated response): the fleet sharing one JA4 across 800 distinct IPs breaches the ASN frequency threshold within minutes and receives a CAPTCHA challenge, which it fails consistently (automation frameworks do not solve modern CAPTCHAs at scale). The legitimate CGNAT customer does not share the fleet's JA4 and stays under the individual threshold: no impact.

The engine: OWASP CRS plus custom logic, detection before blocking

The technical implementation rests on the OWASP Core Rule Set as a base layer (generic protection against injection and request anomalies), complemented by a custom rule layer dedicated to frequency and enriched fingerprinting. A SecRule combines frequency counting by group (ASN, JA4) with the enriched variable carrying the fingerprint:

SecRule REQUEST_URI "@streq /login" \
    "id:100301,phase:2,\
    setvar:'ip.login_fail_count=+1',\
    expirevar:'ip.login_fail_count=300',\
    chain"
    SecRule RESPONSE_STATUS "@eq 401" \
        "chain"
        SecRule TX:JA4_FINGERPRINT "@within %{TX.KNOWN_STUFFING_JA4_LIST}" \
            "setvar:'tx.stuffing_score=+10'"

The sensitivity parameter (paranoia level in CRS terminology) is rolled out progressively: pure detection first, full logging of potential false positives, then actual blocking once the false-positive rate is validated against real traffic. That validation discipline, not the sophistication of the rule itself, is what separates a WAF that protects from one teams eventually turn off because it blocks real customers.

Seasonal playbook: harden before the peaks

Credential stuffing follows a predictable calendar, mirroring retail's own. Summer sales, Black Friday, and the year-end holidays concentrate new account creation, average basket value, and therefore bot operators' appetite for testing their credential lists before customers get around to changing passwords.

Three actions before each seasonal peak:

  • Temporarily lower challenge-trigger thresholds, since higher legitimate volume makes frequency anomalies harder to distinguish at a constant threshold.
  • Actively monitor for freshly republished credential dumps in the weeks leading up to the peak: a new leak mechanically triggers a testing wave against every e-commerce site, yours included.
  • Refresh the list of ASNs known for hosting residential pools before the peak, not during it: adjusting thresholds mid-spike is when false positives cost the most.

In summary

Blocking credential stuffing without breaking real customers means abandoning the single-IP threshold in favor of two complementary aggregation axes: JA4/JA4H fingerprinting to identify the fleet despite IP rotation, and ASN or network block to capture distributed volume. Graduated response (challenge before block) and a pure detection phase before enabling blocking are what protect legitimate customers, especially those behind a shared CGNAT.

That is exactly the logic packaged into the ThreatClaw WAF pack: rules validated against real traffic, ASN thresholds and JA4 signatures kept current, ready to deploy in detection mode before blocking on your engine.

Related articles