Vulnerability Remediation Patterns
Injection Remediation
SQL Injection
- •Parameterized queries ALWAYS. No exceptions. No string concatenation for SQL.
- •ORM usage preferred -- but verify the ORM parameterizes under the hood (raw queries in ORMs are still vulnerable).
- •Stored procedures: use parameters, not dynamic SQL inside the procedure.
- •If building dynamic column/table names: strict allowlist validation, never user input directly.
Command Injection
- •Avoid shell execution entirely. Rewrite with language-native libraries (file ops, HTTP clients, etc.).
- •If shell is unavoidable:
subprocesswith array args (['cmd', 'arg1', 'arg2']), NEVERshell=True. - •Allowlist commands and arguments. Reject anything not on the list.
- •Never pass user input to
eval(),exec(),os.system(), or backtick execution.
XSS (Cross-Site Scripting)
- •Output encoding by context -- these are ALL different:
- •HTML body: HTML entity encode (
<becomes<) - •HTML attribute: attribute encode, always quote attribute values
- •JavaScript context: JavaScript encode (hex escape)
- •URL parameter: URL encode
- •CSS value: CSS hex encode
- •HTML body: HTML entity encode (
- •CSP headers as defense in depth:
default-src 'self', avoidunsafe-inline/unsafe-eval. - •DOM manipulation: use
textContent, NEVERinnerHTMLwith user data. - •Frameworks with auto-escaping (React, Angular) help but do not prevent
dangerouslySetInnerHTMLor[innerHTML]bypasses.
Template Injection (SSTI)
- •Never pass user input as the template source. User input goes into template variables, not the template itself.
- •Use logic-less templates (Mustache, Handlebars) where possible.
- •Sandbox template engines. Restrict available functions/objects in template context.
Path Traversal
- •Canonicalize the path first (
os.path.realpath,Path.resolve()), THEN verify it starts with the allowed prefix. - •Never trust user-supplied filenames. Generate filenames server-side, store original name as metadata.
- •Reject paths containing
.., null bytes, or encoded variants.
Authentication Fixes
Password Storage
- •bcrypt or argon2id with appropriate work factor. Target 250ms+ hash time.
- •NEVER MD5, SHA-1, or SHA-256 for passwords (fast hashes enable brute force).
- •Use the library's built-in salt generation -- do not roll your own.
Session Management
- •Session IDs: cryptographically random, minimum 128 bits of entropy.
- •Cookie flags:
HttpOnly(no JS access),Secure(HTTPS only),SameSite=LaxorStrict. - •Server-side session expiry. Do not rely only on cookie expiry.
- •Regenerate session ID on login (prevents session fixation).
Token Handling
- •Short-lived access tokens (15 min or less) + refresh token rotation.
- •Store the hash of refresh tokens, not plaintext.
- •Invalidate all tokens on password change.
- •JWTs: validate signature, issuer, audience, and expiry. Reject
alg: none.
MFA Implementation
- •TOTP preferred over SMS (SIM swap risk).
- •Backup codes: hash them like passwords. Single-use. Show only once.
- •Rate limit verification attempts (5 attempts, then lockout with backoff).
- •Do not reveal whether MFA is enabled during login flow (information leakage).
Password Reset
- •Constant-time email lookup -- same response time whether account exists or not (prevents enumeration).
- •Time-limited tokens (15-30 min). Single-use.
- •Hash the stored token (if DB is compromised, tokens are useless).
- •Invalidate all existing reset tokens when a new one is issued.
Authorization Fixes
BOLA (Broken Object Level Authorization)
- •Verify ownership at the data layer, not just the route/middleware.
- •Every query that fetches by ID must also filter by the authenticated user's scope.
- •Pattern:
SELECT * FROM orders WHERE id = ? AND user_id = ?-- never justWHERE id = ?.
Privilege Escalation
- •Role checks at the function/action level, not just the UI.
- •Server-side enforcement is mandatory. Hiding a button is not access control.
- •Verify role on every request. Do not trust a role claim from the client.
- •Principle of least privilege: default deny, explicitly grant.
Mass Assignment
- •Explicit allowlists for assignable fields. Never pass raw request body to model update.
- •Example:
user.update(request.body)is vulnerable. Useuser.update(pick(request.body, ['name', 'email'])). - •Separate DTOs for create vs update if different fields are assignable.
CORS
- •Explicit origin allowlist. Never
Access-Control-Allow-Origin: *with credentials. - •Validate the
Originheader server-side against the allowlist. - •Do not reflect the request origin back as the allowed origin without checking.
- •Restrict allowed methods and headers to what is actually needed.
Data Exposure Fixes
Error Handling
- •Generic messages to users: "Something went wrong. Please try again."
- •Detailed errors server-side only (structured logging with request ID for correlation).
- •Never expose: stack traces, SQL errors, internal file paths, dependency versions.
- •Custom error pages for 4xx/5xx. Framework defaults often leak info.
API Response Shaping
- •Explicit serialization: return only the fields the client needs.
- •Never dump model/ORM objects directly to JSON response.
- •Different serializers for different roles (admin sees more fields than regular user).
- •Paginate list endpoints. Never return unbounded result sets.
Logging Hygiene
- •Scrub before logging: PII, tokens, passwords, credit card numbers.
- •Structured logging with sensitivity tags so automated scrubbing can catch what you miss.
- •Log access events (who accessed what, when) for audit trail.
- •Do not log full request bodies -- they may contain secrets.
Security Headers
- •
Strict-Transport-Security: max-age=31536000; includeSubDomains(HSTS) - •
X-Content-Type-Options: nosniff - •
X-Frame-Options: DENY(orSAMEORIGINif you need iframes) - •
Content-Security-Policy: lock down script/style/image sources - •Remove
Server,X-Powered-By, and other version-disclosing headers.
Cryptography Fixes
Core Rules
- •Use standard libraries (libsodium, OpenSSL, Web Crypto API). Never roll your own.
- •AES-256-GCM for symmetric encryption (authenticated encryption -- integrity + confidentiality).
- •RSA-OAEP (2048+ bit) or ECDH (P-256+) for key exchange.
- •TLS 1.2+ only. Disable TLS 1.0, 1.1, SSLv3.
Key Management
- •Build in key rotation from day one. Hardcoded keys are a ticking time bomb.
- •Secrets in environment variables or a vault (HashiCorp Vault, AWS Secrets Manager).
- •Never commit secrets to version control. Use
.gitignoreand pre-commit hooks to prevent it. - •Rotate keys immediately if any suspected compromise.
Common Mistakes
- •Using ECB mode (patterns visible in ciphertext). Use GCM or CBC with HMAC.
- •Reusing IVs/nonces. Every encryption operation needs a unique IV.
- •Comparing hashes with
==instead of constant-time comparison (timing attacks). - •Storing encryption keys next to the encrypted data.
Fix Verification Patterns
How to confirm a fix actually works:
Write the Exploit Test
- •Create a test that reproduces the original vulnerability. It should fail before the fix and pass after.
- •Example: a test that sends
' OR 1=1 --to the search endpoint and verifies it returns 0 results, not all results.
Test the Bypass
- •Can the fix be circumvented with encoding changes? (
%27instead of', double encoding, Unicode normalization) - •Can it be bypassed with case changes, null bytes, or alternate representations?
- •Try the fix with the attacker mindset: "How would I get around this?"
Test Adjacent Functionality
- •Did the fix break legitimate use cases? (Overly aggressive validation rejecting valid input)
- •Test with international characters, long strings, and edge-case valid inputs.
Check for Variants
- •If you found SQLi in
/search, check/filter,/export,/report-- same pattern, different endpoint. - •Grep the codebase for the vulnerable pattern:
grep -r "query.*+.*request"or equivalent. - •Fix the pattern, not just the instance.
Regression Protection
- •Add the exploit test to CI. It must run on every build.
- •If the test suite does not cover this attack vector, the fix WILL regress.
Remediation Anti-Patterns
Patterns that feel like fixes but are not:
- •Blocklist over allowlist: blocking known-bad input (
DROP,<script>) instead of allowlisting known-good. Always bypassable with encoding, casing, or novel payloads. - •Client-side only validation: the server must validate independently. Client validation is UX, not security.
- •Security through obscurity: hiding the admin endpoint at
/x7f3admindoes not fix missing auth. Assume the attacker knows the URL. - •Partial fix: fixing the reported instance but not the pattern. If one endpoint was vulnerable, grep for the same pattern everywhere.
- •Over-patching: adding so many validation layers that legitimate usage breaks. Fix precisely -- one correct check at the right boundary.
- •Fix and forget: no regression test means the vulnerability returns in the next refactor. If there is no test, the fix is incomplete.