← Back to list
● General attack technique · card-testing (BIN) attacks

Finding valid card numbers with small charges —
I'm publishing the code that stops it

Card testing (a BIN attack) brute-forces the digits after a known BIN (bank identification number), using tiny authorization attempts to find valid card numbers. I reproduced this with the real pentesting tool hydra and verified how a velocity check (rate limit) stops it.

See live demo → Jump to code

Who this is for

A learning resource for backend developers building payment systems whose card-authorization API has no limit on the number of attempts.

OWASP
A textbook case ("Carding"/"Card Cracking") under OWASP's Automated Threats to Web Applications
hydra
Attack reproduced and verified with hydra, a real pentesting tool with 10k+ GitHub stars
$0
What this code costs right now — free

Code — unlimited-charge-attempts.js

A vulnerable payment API that checks card validity with no limit on attempts.

unlimited-charge-attempts.js
// Vulnerable payment API — checks a card's validity as many times
// as asked. Each individual charge looks small (or $0), but an
// attacker abuses the response itself as a valid-card finder
// (verified directly with hydra).
class UnlimitedChargeAttempts {
  constructor(validCards) {
    this.validCards = validCards; // Set<cardNumber>
  }

  charge(cardNumber) {
    if (this.validCards.has(cardNumber)) {
      return { status: 'card_valid' };
    }
    return { status: 'card_declined' };
  }
}

module.exports = { UnlimitedChargeAttempts };

Code — velocity-check-guard.js

The fix: once a source racks up enough failures, every further attempt from it is blocked outright.

velocity-check-guard.js
// The fix: count failures per source (IP, etc.), and once a source
// crosses a threshold, every further attempt is blocked outright —
// without even looking at the card number.
class VelocityCheckGuard {
  constructor(validCards, blockAfter = 3) {
    this.validCards = validCards;
    this.blockAfter = blockAfter;
    this.failuresBySource = new Map();
  }

  charge(cardNumber, source) {
    const fails = this.failuresBySource.get(source) || 0;
    if (fails >= this.blockAfter) {
      return { status: 'rate_limited' }; // won't say even for a real card now
    }
    if (this.validCards.has(cardNumber)) {
      return { status: 'card_valid' };
    }
    this.failuresBySource.set(source, fails + 1);
    return { status: 'card_declined' };
  }
}

module.exports = { VelocityCheckGuard };

Price

Free (for now)
A deeper version (device fingerprinting, issuer integration) ships later — pay-what-you-want, starting at $0.
goalsgo7574@gmail.com
This isn’t a purchase — just email me "notify me about the paid version" and I’ll reach out when it ships.
Note: This is a scaled-down learning implementation. An IP-only rate limit can be routed around with proxy rotation — a real system also needs device fingerprinting and more.

Leave feedback

Questions or feedback about this product? Leave your email if you'd like a reply (optional).