Your web application is under attack right now.
I have 3 decades of experience in cybersecurity, but I still feel anxious when I review security audits. The reason is not that the risks changed; they haven’t, but because the companies are making the dumb mistakes they used to make in the 1990s and before. The tools evolved. The foolishness didn’t.
Here’s what kills me: 94% of applications tested last year had at least one critical vulnerability in their first month of production. We’re not talking about obscure edge cases. We’re talking about SQL injection. Broken authentication. The stuff we figured out how to fix when I still had hair.
Authentication: Still the Weakest Link After All These Years
Key Insight: Sixty-two percent of breaches start with authentication failures, and most organizations still treat passwords like they’re a real security control.
Let me tell you about passwords. They’re garbage. Always have been. The average user recycles the same password across eleven services because remembering unique credentials is cognitive torture. So when Adobe gets breached or LinkedIn, or Yahoo, or whoever’s turn it is this month attackers don’t just get Adobe accounts. They get everything.
Credential stuffing works because humans are predictable [Link Source: Verizon Data Breach Investigations Report]. Attackers take leaked credentials from one breach and spray them across thousands of other sites. They succeed 0.1% of the time. Sounds tiny until you realize they’re testing billions of combinations monthly. Do the math.
Multi-Factor Authentication: Better, But Still Screwed Up
Companies finally started taking MFA seriously. Then they implemented it wrong.
Here’s the MFA hierarchy nobody tells you about:
- SMS codes: Worthless. SIM swapping is trivial now, and I’ve watched mobile carrier employees sell access for $500.
- Email codes: Slightly better than SMS, which is damning with faint praise.
- TOTP apps: Decent. Google Authenticator works until someone sophisticated really wants in.
- Push notifications: Convenient, but “MFA fatigue” attacks exploit user laziness.
- Hardware keys: Finally, something that actually works. FIDO2/WebAuthn require physical possession and cryptographic proof.
- Passkeys: Where we should’ve been a decade ago. No passwords to steal because there aren’t any passwords.
I watched a Fortune 500 company get compromised last year because their CEO’s assistant approved a fraudulent MFA push notification while distracted. One click. $4.7 million wire transfer. Gone.
Session Management: Where Even Smart Developers Fail
JWT tokens became popular because they’re stateless and scale beautifully. They also became popular attack vectors because developers don’t understand them.
Walk into any startup and check their JWT implementation. I guarantee you’ll find at least three of these mistakes:
- Access tokens that don’t expire for 30 days (why even bother?)
- Tokens stored in localStorage where any XSS attack can grab them
- No signature algorithm validation, letting attackers switch to alg: none
- Zero token revocation capability once issued, valid forever
Ask a simple question: “If someone reports their account is compromised, can you kill all their active sessions right now?” If the answer involves words like “eventually” or “next deployment,” you’ve failed. Sessions need millisecond-level revocation, not “maybe by Tuesday.”
Input Validation: The Bug That Refuses to Die
Key Insight: Every single web vulnerability traces back to trusting user input, yet frameworks keep adding convenience methods that let developers bypass validation entirely.
SQL injection should be extinct. Bobby Tables is old enough to vote. We have parameterized queries, ORMs, stored procedures every tool you need to prevent injection attacks. Yet here we are, still finding it in production code at Fortune 500 companies.
You know why? Developers get lazy. They concatenate strings because it’s “just one query” or they don’t understand how their ORM handles edge cases. Then someone sends ‘; DROP TABLE users; — and suddenly it’s an all-hands incident response.
Three Validation Layers You’re Probably Skipping
Most developers validate at one layer and call it done. Wrong.
Syntactic validation:
- Format, length, character set matching
- Rejecting <script> tags in name fields
- Ensuring email addresses look like email addresses
Semantic validation:
- Business logic sanity checks
- Catching negative quantities, impossible dates, absurd dollar amounts
- Questioning why someone’s ordering 50,000 units of toothpaste
Contextual encoding:
- Different escaping for HTML vs JavaScript vs SQL vs URLs
- Encoding based on where data lands, not where it came from
- Understanding that JSON contexts need different handling than XML
I reviewed code last month where developers validated email format but never checked if the domain existed. Attackers registered throwaway domains, created thousands of accounts, and used them for a credential stuffing campaign. Simple check. Nobody did it.
XSS: The Gift That Keeps on Giving
Cross-site scripting exploits output encoding failures [Link Source: OWASP XSS Prevention Cheat Sheet, Link Source: Mozilla CSP Documentation]. React and Vue auto-escape by default, which is great until some developer uses dangerouslySetInnerHTML because they need to “display rich content.”
I’ve seen this pattern a hundred times. Developer thinks they’re being clever. Marketing wants formatted text in comments. Developer enables HTML without sanitization. Attackers inject malicious scripts. Everyone acts surprised.
Content Security Policy headers could prevent this. Seventy-three percent of production apps either skip CSP entirely or configure it so loosely it’s worthless. It’s like installing a security camera pointed at the ceiling.
API Security: Where Traditional Security Models Collapse
Key Insight: APIs handle more traffic than traditional web interfaces now, but security practices are stuck in the server-side rendering era.
REST APIs, GraphQL, gRPC these are the nervous system of modern applications. Each endpoint is an attack vector. Unlike browser forms with some built-in constraints, APIs accept whatever JSON nightmare an attacker dreams up.
I tested an API last quarter that accepted 100MB JSON payloads with zero size validation. Sent nested objects 5,000 levels deep. Crashed the entire service. They’d been in production for two years.
Authorization: Beyond the OAuth Theater
OAuth 2.0 scopes sound great in theory. In practice, most implementations treat them like boolean flags instead of granular permissions.
What broken API authorization looks like:
- A user:read token accessing every user in the database
- No distinction between reading your own data vs someone else’s
- Scopes that never expire or get re-evaluated
- Zero consideration for organizational boundaries or data ownership
What it should look like:
- Scope + resource-level access control
- Context-aware permissions based on relationships
- Automatic scope narrowing based on usage patterns
- Aggressive token expiration with refresh mechanisms
Rate Limiting: Amateur Hour
Most rate limiting implementations are jokes. Per-IP limiting? Attackers have residential proxy networks with 50,000 IPs. Per-user limiting? They’ll create 10,000 accounts through automated registration.
Rate limiting that actually stops attacks:
- Sliding windows instead of fixed buckets
- Multi-tier limiting (IP, user, API key, organization)
- Cost-based limiting for expensive operations
- Adaptive limits that tighten during suspected attacks
- Behavioral analysis that distinguishes humans from bots
GraphQL: Great Idea, Terrible Security Posture
GraphQL lets clients request exactly the data they need [Link Source: GraphQL Security Best Practices, Link Source: API Security Project, Link Source: OWASP API Security Top 10, Link Source: Cloudflare API Shield Documentation]. It also lets attackers craft queries that murder your database.
Seen it happen. Attacker requested users { posts { comments { author { posts { comments }}}}} nested 20 levels deep. No query complexity limits. No timeout enforcement. Database fell over. Application crashed. Monitoring systems crashed trying to log the crash.
Query depth limiting, complexity scoring, and timeout enforcement aren’t optional anymore. They’re mandatory.
Zero Trust: Finally Admitting the Perimeter Never Existed
Key Insight: The network perimeter is dead, and pretending otherwise gets you breached.
Zero Trust means trusting nothing. Not your VPN. Not your corporate network. Not authenticated requests from five minutes ago. Verify everything, every time.
I watched companies cling to perimeter security like it meant something. Put up firewalls. Configured VPNs. Then COVID hit and everyone went remote. Suddenly the perimeter was every coffee shop and home network in America. Attackers had a field day.
Modern implementations combine device fingerprinting, geolocation, behavioral biometrics, and access patterns. They calculate real-time risk scores. A login from your usual device and location at 9 AM? Low risk. Same credentials from Romania at 3 AM? High risk. Prompt for additional verification.
Service-to-Service: The Security Hole You’re Ignoring
Microservices architectures create dozens of internal APIs. Companies secure the external perimeter then connect internal services with zero authentication because “they’re all internal.”
That’s how lateral movement works. Compromise one service, pivot to the next, escalate privileges, extract data. Mutual TLS should be mandatory for service-to-service communication. Service meshes like Istio automate mTLS deployment, but most teams skip it because “it’s complicated.”
Everything’s complicated until you’re explaining a breach to your CEO.
Container Security: Shipping Vulnerabilities at Scale
Key Insight: Containers package dependencies, which means they also package every vulnerability in those dependencies.
Docker revolutionized deployment. It also made it trivially easy to deploy vulnerable code. A FROM ubuntu:latest pulls whatever Ubuntu’s shipping this week, complete with CVEs. Image scanning should happen in CI/CD before deployment, not after breach.
Container security basics everyone skips:
- Scan images before deployment
- Use minimal base images (Alpine, distroless)
- Never run as root (still the default in too many places)
- Read-only filesystems wherever possible
- Secrets in Vault, not environment variables
Kubernetes: Security Complexity Turned Up to Eleven
Pod security standards, network policies, RBAC configs Kubernetes security is its own discipline. Most teams learn it the hard way.
I’ve audited Kubernetes clusters where service accounts had cluster-admin permissions “because it was easier.” Where network policies were nonexistent. Where secrets were base64-encoded environment variables that appeared in logs, error messages, and process listings.
HashiCorp Vault exists for a reason. Cloud provider secret managers exist for a reason. Use them.
Security Culture: Why Technology Alone Fails
Developers don’t wake up thinking about security. They wake up thinking about shipping features. Product managers measure velocity, not vulnerability counts. Executives care about revenue, not security posture.
This is why breaches happen.
Security champions embedded in dev teams help. Not security cops who reject pull requests enablers who help teammates write secure code. Threat modeling catches architectural flaws before code exists. Automated testing in CI/CD provides fast feedback.
Security tooling that matters:
- SAST scans source code for patterns
- DAST probes running apps like an attacker
- SCA identifies vulnerable dependencies
- IAST combines both for comprehensive coverage
None of these replace human expertise. They free security engineers to focus on novel threats instead of catching SQL injection for the thousandth time.
The 2026 Reality
Attackers professionalized while we argued about frameworks. Ransomware-as-a-Service lets script kiddies deploy sophisticated attacks. AI generates phishing emails that fool security-aware users. The barrier to entry dropped to zero.
The fundamentals haven’t changed in thirty years: validate input, enforce least privilege, assume breach. What changed is the scale, speed, and sophistication of attacks.
Your application is under attack right now. The question isn’t if they’ll get in. It’s whether you’ll detect it when they do.




