Tracking abuse cases at code level
If one or several abuse cases are handled at code level, put a special comment in the classes/scripts/modules to indicate which abuse cases are addressed. Dedicated annotations like @AbuseCase(ids={"ABUSE_CASE_001","ABUSE_CASE_002"}) can be used to facilitate tracking and allow identification within integrated development environments.
Tracking abuse cases at design, infrastructure or network level
If one or several abuse cases are handled at design, infrastructure or network level, make a note in the documentation or schema to indicate which abuse cases are addressed by that design, network, or infrastructure (e.g., 'This design/network/infrastructure takes into account the abuse cases ABUSE_CASE_001, ABUSE_CASE_002, ABUSE_CASE_xxx').
Abuse cases in security requirements and acceptance criteria
The abuse cases selected to be addressed must become security requirements in each feature specification section (waterfall) or User Story acceptance criteria (agile) in order to allow additional cost/effort evaluation, identification and implementation of the countermeasures.
Do not assume race condition windows are too fast to exploit
Do not rationalize that a window between check and update is too small for exploitation. Concurrent request tools can send dozens of requests to arrive within a millisecond of each other, or send them as a single multiplexed HTTP/2 request to land simultaneously. If the window exists at all, assume it is exploitable.
Make check-and-act operations atomic using database transactions
Any operation that reads a value, makes a decision, and writes a value is a potential race condition. Use a SERIALIZABLE transaction isolation level so the database detects conflicts and rolls back competing transactions, or use an explicit row lock like SELECT ... FOR UPDATE in PostgreSQL, MySQL InnoDB, and similar databases. The check and update must run as a single atomic operation.
Use conditional update pattern for race condition prevention
For operations that must be atomic, use a conditional update such as UPDATE ... WHERE balance >= amount that succeeds only if the predicate still holds at write time. Check the number of affected rows. If zero, the condition failed and the caller should receive an error.
Use idempotency keys for external non-idempotent operations
For operations that talk to external systems such as charging a card or issuing a voucher, a retry from the client should not result in a duplicate action. Accept a client-supplied idempotency key, store it with the result, and return the cached result on retry. This pattern prevents duplicate charges or credits when requests are retried.
Unique constraint on one-per-user bonus prevents duplicates
Create a unique constraint on (user_id, bonus_type) in the database to enforce that only one bonus of each type can be granted per user. Let the database reject duplicate bonus grants.
Invisible risk scoring as CAPTCHA alternative
Cloudflare Turnstile, reCAPTCHA v3, and hCaptcha Enterprise are invisible risk scoring services. The provider returns a score; the application decides the threshold. This is preferable to visible CAPTCHAs which are accessibility-hostile, machine-solvable by ML, and outsourceable to human solver farms.
Proof of Work challenge algorithm and parameters
In Proof of Work anti-bot defense, the server issues a challenge consisting of a random 16-byte hex-encoded challenge string and a difficulty in bits (e.g., 18). The client must find a nonce such that sha256(challenge concatenated with nonce) starts with at least the specified number of leading zero bits. The server verifies by counting leading zero bits in the resulting hash. Tune difficultyBits so a real client spends a few hundred milliseconds; raise it under attack. Set an expiration time (e.g., 60 seconds) on each issued challenge.
Honeypot hidden form field implementation
Add a hidden form field such as <input type="text" name="website" /> styled with display:none and labeled 'leave blank'. Bots fill the field; legitimate users do not. Reject submissions where the honeypot field is non-empty. The field should be positioned off-screen using position:absolute, left:-10000px, width:1px, height:1px, overflow:hidden, and aria-hidden="true". Set tabindex="-1" and autocomplete="off" on the input.
Robots.txt trap for malicious crawlers
Disallow a bait path in robots.txt. Treat any traffic to the disallowed path as malicious. Well-behaved crawlers respect robots.txt directives; abusive crawlers do not. This cheap, effective control has zero impact on legitimate users.
Tarpit response strategy for detected bots
For detected bots, do not return 403 Forbidden immediately. Instead, slow responses progressively (e.g., add a 5-second delay with jitter). The bot's throughput collapses without telegraphing detection. Tarpitting is cheap and effective for high-confidence detections.
Canary content for scraping detection
Embed unique, watermarked records on listing pages. If these records appear in third-party datasets or on competitor sites, you have proof of scraping and a fingerprint of the scraper. This provides evidence for legal action or DMCA takedowns.
Account creation (OAT-019) defense controls
Verify email before the account is usable; do not just send confirmation, gate features behind verification. Check email against disposable-domain lists (refresh weekly). For phone verification, check the carrier type as VoIP numbers are abundant and cheap. Apply a per-IP, per-ASN, per-device-fingerprint signup velocity limit (e.g., 3 per hour). Reject signup if the email's local-part has high entropy and comes from a recently-created domain.
Login (OAT-008) defense controls
Apply per-username and per-IP limits with separate windows. Check the submitted password against breach corpora using HaveIBeenPwned k-Anonymity API; do not block but require a step-up (MFA, CAPTCHA). On suspicious patterns, require MFA even for low-risk users. See the Credential Stuffing Prevention Cheat Sheet for full guidance.
Inventory and scalping (OAT-005, OAT-015) defense controls
For limited drops, implement a waiting room or virtual queue with randomized admission and tokens bound to session and identity. Enforce per-account purchase limits server-side, including identity proxies (same payment method, same shipping address, same device). Set a hold time: inventory in cart must be paid for within N seconds or is released, preventing cart-camping. At order time, deduplicate address and payment using normalized hashes (street + zip for addresses, BIN + last 4 digits + holder hash for payments).
Public API anti-bot controls
Use API keys with rotating secrets, not static bearer tokens checked into client code. Implement per-key quotas advertised in X-RateLimit-* headers so well-behaved clients self-throttle. Require request signing (e.g., HMAC of method + path + timestamp + body) to prevent replay and require a stable secret. Tier APIs explicitly: public catalog endpoints may serve cached, slightly-delayed data; partner APIs serve realtime data with an authenticated key.
Graduated response strategy by confidence level
Low confidence (suspicious): log and serve normally but flag the session. Medium confidence: step-up with CAPTCHA, MFA, or PoW challenge. High confidence: tarpit (slow responses), serve stale or randomized data. Very high confidence: soft-block specific actions (e.g., disable checkout, allow browsing). Confirmed abuse: account hold plus manual review; do not delete to preserve forensics. Hard blocks teach attackers what worked; graduated responses are more durable.
Poisoned data strategy for scrapers
For detected scrapers, return plausible but slightly wrong data (price ±1%, fake stock counts, minor content variations). This poisons the attacker's dataset and is often more damaging to the business case for scraping than a hard 403 Forbidden block.
Privacy and compliance for anti-bot processing
Document the lawful basis (legitimate interest is typical) and the categories of data collected. Apply data minimization: collect what is needed to score the request and discard the rest. Set a short retention period for raw signals; aggregate for longer-term analytics. If using a third-party anti-bot vendor, list them as a sub-processor and review their DPIA. Do not block users solely because their browser is hardened (privacy-respecting users often look 'bot-like'); prefer challenge to block. Provide an accessible alternative when challenging users with CAPTCHAs (audio CAPTCHA, support contact).
Anti-patterns to avoid in anti-bot defenses
Do not block all traffic with non-standard User-Agents, as this breaks legitimate research, accessibility, and integration tools. Do not rely solely on a single edge vendor; when it tunes wrong, your entire site goes down or opens up. Do not store raw fingerprints indefinitely. Do not place CAPTCHAs at every login attempt, as this destroys conversion and trains users to solve mechanically. Do not hide anti-bot rules with no logging; you cannot tune what you cannot see. Do not hard-block on first signal with no graduated response, as this gives attackers a clean signal to iterate against.
Privacy guidelines for device and network fingerprinting
Document fingerprinting in privacy notices; some jurisdictions (EU/UK ePrivacy, CCPA) require disclosure or opt-out. Hash or truncate fingerprints before storage; do not retain raw values that enable re-identification. Set short retention windows (hours to days) for anti-bot signals, long enough to detect abuse but short enough to limit surveillance risk. Avoid fingerprinting authenticated, low-risk traffic such as a logged-in user reading their own profile.
Rate limiting keys and scope recommendations
Apply rate limiting at multiple keys: per IP (coarse, defeated by residential proxy networks but useful as a floor), per session/cookie (defeated by cookie clearing, useful against unsophisticated bots), per authenticated identity (most reliable after login), per endpoint (login deserves tighter limits than home page), and per ASN or geolocation (useful when datacenter ASN traffic is unexpected). Always use a token-bucket or sliding-window algorithm; avoid fixed-window counters as they allow bursts at boundary times.
Rate limit response and error detail guidance
When a rate limit is hit, return HTTP 429 Too Many Requests. Avoid Retry-After header values precise enough to schedule retries against. Do not include diagnostic detail such as which bucket fired or remaining attempts, as this information is useful only to attackers tuning their tooling.
CAPTCHA alternatives: Privacy Pass and cryptographic attestation
Privacy Pass (RFC 9576), Apple Private Access Tokens, and emerging device-attestation APIs allow the client to prove 'I am a real device on a known platform' without identifying the user. These cryptographic attestation tokens are preferable to visible CAPTCHAs for modern anti-bot defenses.
Service Worker HTTPS and cache-busting filename
Only register Service Workers from your own origin and only serve the worker script over HTTPS with a long-cache-busting filename such as sw.<hash>.js.
Application Cache deprecated and removed
The HTML5 Application Cache (html manifest="..." and .appcache files) has been removed from all major browsers (Firefox 85, Chrome 93). Do not use it for new applications and migrate any remaining usage to Service Workers with the Cache API.
X-Frame-Options header for clickjacking prevention
To prevent Clickjacking attacks and unsolicited framing, use the header X-Frame-Options which supports the deny and same-origin values. Frame-busting code like 'if(window!==window.top) { window.top.location=location;}' is not recommended.
Referrer-Policy header for tabnabbing prevention
Add the HTTP response header Referrer-Policy: no-referrer to every HTTP response sent by the application to ensure that no referrer information is sent along with requests from the page, preventing tabnabbing attacks.
Tabnabbing prevention with window.open
For the JavaScript window.open function, add the values noopener,noreferrer in the windowFeatures parameter and set newWindow.opener = null to prevent tabnabbing attacks and cut the back link between parent and child pages.
Tabnabbing prevention with rel attribute
To prevent tabnabbing attacks from newly opened pages accessing parent page content via the opener object, add the attribute rel="noopener noreferrer" to HTML links that use target attributes not replacing the current location. The rel="noopener" attribute cuts the back link, and rel="noreferrer" also removes referrer information.
Flash Player and Java applets end of life
Adobe Flash Player reached end-of-life on 31 December 2020 and is removed from all browsers. Java applets, Silverlight, and ActiveX are likewise unsupported. Native HTML5 (video, audio, canvas, WebAssembly) covers these legacy use cases.
Service Worker no sensitive data caching
Do not cache responses that contain sensitive data. Send Cache-Control: no-store on those responses so the Cache API will not retain them.
Service Worker kill-switch documentation
A malicious or compromised Service Worker can intercept every request from its scope until it is unregistered or the cache TTL expires. Have a documented kill-switch such as an unregister flow that can be shipped in a hotfix.
Service Worker scope restriction
Validate that the scope of the Service Worker is restricted using the scope option or the Service-Worker-Allowed response header so a compromised worker cannot intercept unrelated paths.
SQL injection prevention: use PreparedStatement with parameterized queries
To prevent SQL injection, use prepared statements with query parameterization. Create the SQL query with placeholders (?), then set parameters using setString(), setInt(), and similar methods. This applies to SELECT, INSERT, UPDATE, and DELETE operations. Example: PreparedStatement pStatement = con.prepareStatement("select * from color where friendly_name = ?"); pStatement.setString(1, userInput);
JPA injection prevention: use parameterized queries with named parameters
To prevent JPA injection, use Java Persistence Query Language with named parameters for query parameterization. Create the query string with named parameters prefixed with colon (e.g., :colorName), then use setParameter() to bind values. Example: String queryPrototype = "select c from Color c where c.friendlyName = :colorName"; queryObject.setParameter("colorName", userInput);
Operating System command injection prevention: use technology stack API instead of string concatenation
To prevent OS command injection, do not build OS commands as strings. Instead, use the API provided by the Java technology stack. For example, instead of building a ping command string and executing it, use InetAddress.getByName() and isReachable() methods.
XPath injection prevention: use XPathVariableResolver
To prevent XPath injection, use XPathVariableResolver to define parameters for XPath expressions instead of building expressions as strings. Implement XPathVariableResolver to manage variables in a Map, add variables using addVariable(QName, Object), and set the resolver on the XPath object using xpath.setXPathVariableResolver(). Then compile XPath expressions with variable references like //book[@id=$bookId].
HTML/JavaScript/CSS injection prevention: use strict input validation (allowlist) and output sanitizing+escaping
To prevent HTML/JavaScript/CSS injection, apply strict input validation using allowlist approach with regex patterns to ensure only expected characters are allowed. Additionally, use output sanitizing and escaping before sending data to the browser. Use OWASP Java HTML Sanitizer API (HtmlPolicyBuilder) to sanitize HTML and OWASP Java Encoder API (Encode.forHtml()) to escape HTML tags.
NoSQL injection prevention: validate input for special characters and use API to build expressions
To prevent NoSQL injection, ensure user input does not contain special characters that have meaning in the target NoSQL database API syntax. For MongoDB, the special characters are: ' " \ ; { } $. Also, do not use string concatenation to build API call expressions; instead, use the database API to create expressions. Example: Use Bson eq("borough", userInput) instead of building a query string.
Log injection prevention: use structured logging formats like JSON instead of unstructured text
To prevent log injection attacks that exploit CRLF (Carriage Return/Line Feed) characters, use structured log formats such as JSON instead of unstructured text formats. This prevents attackers from injecting fake log entries. Additionally, limit the size of user input values used in log messages.
Log4j configuration for preventing log injection: use JsonTemplateLayout with maxStringLength
For Log4j Core 2.14.0 and later, use JsonTemplateLayout with a Socket appender to send structured JSON logs to a network socket. Set the maxStringLength configuration attribute to 500 bytes to limit string field sizes and prevent log injection. Configure the appender: <Socket name="SOCKET" host="localhost" port="12345"><JsonTemplateLayout maxStringLength="500" nullEventDelimiterEnabled="true"/></Socket>
Logback configuration for preventing log injection: use JsonEncoder with rolling file appender
For Logback 1.3.8 and later, use JsonEncoder to produce structured JSON logs. Configure a RollingFileAppender with JsonEncoder and set size-based triggering. Example: use FixedWindowRollingPolicy with fileNamePattern="app-%i.log" and minIndex=1, maxIndex=10; use SizeBasedTriggeringPolicy with maxFileSize=5MB; use JsonEncoder class.
Logback best practice: use parameterized logging with compile-time constant patterns
When using Logback via SLF4J, use parameterized logging to add user data to log messages. The pattern must be a compile-time constant. Good: logger.warn("Login failed for user {}.", username). Bad: logger.warn("Failure for user " + username + " and role {}.", role, ex) because if username contains {}, the exception will leak into the message.
Never write custom cryptographic functions
Never write your own cryptographic functions. The risk of introducing security errors is extremely high. Instead, use pre-existing secret management solutions, cloud provider secret management services, or trusted cryptographic libraries rather than JCA/JCE built-in libraries.
Symmetric encryption with Google Tink: use AES-GCM
For symmetric encryption, use Google Tink library with AES-GCM algorithm. Generate a keyset using tinkey command: tinkey create-keyset --key-template AES128_GCM --out-format JSON --out aead_test_keyset.json. Parse the keyset using TinkJsonProtoKeysetFormat.parseKeyset(), register AeadConfig, get the Aead primitive from the KeysetHandle, and call aead.encrypt(plaintext, metadata) and aead.decrypt(ciphertext, metadata).
Symmetric encryption with JCA/JCE: use AES-GCM with 256-bit key and 96-bit nonce
If using built-in JCA/JCE classes for AES-GCM encryption, use: ALGORITHM="AES", CIPHER_ALGORITHM="AES/GCM/NoPadding", KEY_SIZE=256 bits, TAG_LENGTH=128 bits, IV_LENGTH=12 bytes (96 bits). Use KeyGenerator to create a 256-bit key with SecureRandom. Generate a unique 12-byte nonce for every encryption operation using SecureRandom. Create GCMParameterSpec with TAG_LENGTH and nonce, then initialize Cipher with Cipher.ENCRYPT_MODE.
Symmetric encryption with JCA/JCE: use different nonce for every encryption operation
When encrypting with AES-GCM using JCA/JCE, it is critical to use a different nonce for every encryption operation with the same key. Using the same nonce with the same key severely weakens the encryption and can lead to key recovery attacks.
Asymmetric encryption with Google Tink: use hybrid encryption with DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM
For asymmetric encryption between two parties, use Google Tink hybrid encryption. Generate keypairs using tinkey: tinkey create-keyset --key-template DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM --out-format JSON. Register HybridConfig, parse private and public keysets. Get HybridEncrypt primitive from partner's public keyset to encrypt messages. Get HybridDecrypt primitive from own private keyset to decrypt messages. Include metadata context that must match on decryption.
Asymmetric encryption with JCA/JCE: use ECDH with secp256r1 curve and AES-GCM
For asymmetric encryption using JCA/JCE, use Elliptic Curve Diffie-Hellman (ECDH) key agreement with secp256r1 curve. Generate EC keypairs, exchange public keys, and use KeyAgreement to derive a shared secret. Use KeyAgreement.getInstance("ECDH"), initialize with private key, perform doPhase() with partner's public key, and generateSecret(). Use the first 32 bytes of the secret as the AES key for AES-GCM encryption.
LDAP injection prevention
LDAP injection prevention is covered in a dedicated OWASP Cheat Sheet: LDAP_Injection_Prevention_Cheat_Sheet.md
SQL injection prevention detailed cheat sheet
For detailed SQL injection prevention guidance, refer to the SQL_Injection_Prevention_Cheat_Sheet.md from OWASP.
Input validation general guidance
For general input validation guidance, refer to the Input_Validation_Cheat_Sheet.md from OWASP. General advice to prevent injection includes: 1) Apply input validation using allowlist approach combined with output sanitizing and escaping. 2) Use API features provided by your technology stack instead of building commands.
XSS prevention guidance
For comprehensive XSS prevention guidance, refer to the Cross_Site_Scripting_Prevention_Cheat_Sheet.md from OWASP. When viewing log files in a web browser, ensure all XSS defenses are applied.
Cryptographic storage algorithm guidance
For recommended cryptographic algorithms for storage, refer to the Cryptographic_Storage_Cheat_Sheet.md#algorithms from OWASP.
Secrets management cheat sheet
For guidance on secret management solutions and approaches, refer to the Secrets_Management_Cheat_Sheet.md from OWASP. Best practice is to use pre-existing secret management solutions or cloud provider secret management services instead of writing custom code.
LDAP bind authentication defense
If LDAP protocol is configured with bind authentication, LDAP injection attacks are prevented through verification and authorization checks against valid credentials. However, attackers can still bypass this through anonymous bind or unauthenticated bind.
LDAP injection additional defenses
Additional defense-in-depth measures against LDAP injection include: least privilege for LDAP binding accounts, enable bind authentication to perform verification and authorization checks, and allow-list input validation.