In This Guide
- 1. OWASP Top 10 — The Vulnerabilities That Actually Hit You
- 2. Injection Prevention — SQL, XSS, Command
- 3. Authentication and Authorization Done Right
- 4. Secrets Management — No More .env in Git
- 5. Dependency Security — Your Weakest Link
- 6. Security Headers and Transport
- 7. Building a Security-First Culture
- 8. Frequently Asked Questions
The average data breach costs $4.88 million (IBM, 2024). Most breaches exploit known vulnerabilities with known fixes — SQL injection, leaked credentials, unpatched dependencies. This isn't a guide about advanced threat modeling. It's a practical checklist of the things every dev team should already be doing — and most aren't.
1. OWASP Top 10 — The Vulnerabilities That Actually Hit You
| # | Vulnerability | What Goes Wrong | Primary Defense |
|---|---|---|---|
| A01 | Broken Access Control | User accesses other users' data | Server-side authorization on every request |
| A02 | Cryptographic Failures | Data in plaintext, weak algorithms | TLS everywhere, AES-256, bcrypt for passwords |
| A03 | Injection | SQL, XSS, command injection | Parameterized queries, output encoding |
| A04 | Insecure Design | Missing security requirements | Threat modeling, security in design phase |
| A05 | Security Misconfiguration | Default creds, verbose errors, open ports | Hardened defaults, infrastructure as code |
| A06 | Vulnerable Components | Unpatched dependencies | Dependabot, npm audit, SCA scanning |
| A07 | Auth Failures | Weak passwords, broken session management | MFA, rate limiting, secure session handling |
| A08 | Data Integrity Failures | Insecure deserialization, unsigned updates | Signed artifacts, integrity checks |
| A09 | Logging & Monitoring Failures | Attacks go undetected | Structured logging, SIEM, alerting |
| A10 | SSRF | Server fetches malicious internal URLs | URL allowlists, disable internal network access |
2. Injection Prevention — SQL, XSS, Command
SQL Injection — Always Use Parameterized Queries
// ❌ VULNERABLE — string concatenation
const query = `SELECT * FROM users WHERE id = ${req.params.id}`;
// Attack: id = "1; DROP TABLE users; --"
// ✅ SAFE — parameterized query
const query = 'SELECT * FROM users WHERE id = $1';
const result = await db.query(query, [req.params.id]);
// PHP — PDO prepared statements
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $userId]);
// Python — parameterized
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
// Java — PreparedStatement
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
stmt.setInt(1, userId);
XSS Prevention — Output Encoding
// ❌ VULNERABLE — inserting user input as HTML
element.innerHTML = userComment;
// Attack: userComment = ''
// ✅ SAFE — use textContent (auto-escapes)
element.textContent = userComment;
// ✅ SAFE — React auto-escapes JSX by default
return {userComment}
; // React escapes HTML entities
// ⚠️ DANGEROUS React — never use unless sanitized
return ;
// If you must: sanitize with DOMPurify first
import DOMPurify from 'dompurify';
return ;
// Content Security Policy header (blocks inline scripts)
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'
3. Authentication and Authorization Done Right
| Practice | Do This | Not This |
|---|---|---|
| Passwords | bcrypt/argon2 with salt | MD5, SHA-256 without salt |
| Sessions | Cryptographic random tokens, HttpOnly cookies | Sequential IDs, localStorage tokens |
| JWTs | Short expiry (15 min), RS256, validate issuer | alg: none, HS256 with weak secret, no expiry |
| MFA | TOTP (Google Auth), WebAuthn/passkeys | SMS-only (SIM swap vulnerable) |
| Authorization | Check on server for every request | Hide UI elements and hope nobody finds the URL |
| Rate limiting | 5 failed logins → lockout, CAPTCHA | Unlimited login attempts |
IDOR Prevention — The #1 Missed Vulnerability
// ❌ VULNERABLE — Insecure Direct Object Reference (IDOR)
// GET /api/orders/12345 — any authenticated user can access any order
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await db.query('SELECT * FROM orders WHERE id = $1', [req.params.id]);
res.json(order); // No ownership check!
});
// ✅ SAFE — verify ownership on every resource access
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await db.query(
'SELECT * FROM orders WHERE id = $1 AND user_id = $2',
[req.params.id, req.user.id] // Always filter by authenticated user
);
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order);
});
4. Secrets Management — No More .env in Git
| Tool | Best For | How It Works |
|---|---|---|
| GitHub Secrets | CI/CD pipelines | Injected as env vars in GitHub Actions |
| AWS Secrets Manager | Production apps on AWS | API call to retrieve, auto-rotation |
| HashiCorp Vault | Multi-cloud, dynamic secrets | Generates short-lived credentials on demand |
| 1Password / Doppler | Small teams, local dev | CLI injects secrets into env |
| git-secrets / gitleaks | Prevention | Pre-commit hook that blocks secret commits |
5. Dependency Security — Your Weakest Link
The average Node.js project has 1,000+ transitive dependencies. Each one is an attack surface. The supply chain security guide covers this in depth, but here are the essentials.
# Scan dependencies for known vulnerabilities
# Node.js
npm audit # Built-in
npx audit-ci --moderate # Fail CI on moderate+ vulnerabilities
# Python
pip-audit # Scan installed packages
safety check # Check requirements.txt
# Java
mvn dependency-check:check # OWASP Dependency-Check
# Go
govulncheck ./... # Official Go vulnerability scanner
# Universal (any language)
trivy fs . # Aqua Trivy — scans code, deps, IaC
snyk test # Snyk — commercial, good integration
# Automate in CI (GitHub Actions example)
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'HIGH,CRITICAL'
exit-code: '1' # Fail the build on high/critical vulns
6. Security Headers and Transport
# Essential security headers (Nginx example)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()";
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.example.com";
# Cookie security
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=86400
# CORS — restrict to your domains
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
7. Building a Security-First Culture
| Practice | Implementation | Frequency |
|---|---|---|
| Security in code reviews | Security checklist in PR template | Every PR |
| Dependency updates | Dependabot + auto-merge for patches | Automated daily |
| Penetration testing | External pentest firm | Annually + after major changes |
| SAST/DAST in CI | Semgrep (SAST), ZAP (DAST) | Every build |
| Incident response plan | Documented runbook, practiced | Drill quarterly |
| Security training | OWASP Juice Shop, Hack The Box | Quarterly |
Frequently Asked Questions
What's the single most impactful security practice?
Parameterized queries for all database access. SQL injection is still the most exploited web vulnerability, and it's 100% preventable with prepared statements. If your codebase has even one string-concatenated SQL query, fix it today.
Do I need to encrypt data at rest?
Yes, for sensitive data (PII, financial, health). Cloud providers offer transparent disk encryption (AWS EBS, GCP disk encryption) — enable it. For application-level encryption of specific fields (credit cards, SSNs), use AES-256-GCM. Don't roll your own crypto — use established libraries (libsodium, AWS Encryption SDK).
JWT or session cookies?
Session cookies for web apps (simpler, more secure — HttpOnly prevents XSS theft, server can revoke instantly). JWTs for API authentication between services (stateless, no session store needed). Don't store JWTs in localStorage — use HttpOnly cookies with SameSite=Strict.
How do I start securing a legacy codebase?
Priority order: (1) Run a dependency audit and fix critical vulns — takes hours, huge ROI. (2) Scan for hardcoded secrets with gitleaks. (3) Add SAST scanning (Semgrep) to CI. (4) Audit authentication and authorization. (5) Add security headers. Don't try to fix everything at once — attackers target the easiest vulnerabilities first, so should you.
Is HTTPS enough for API security?
HTTPS protects data in transit but doesn't protect against injection, broken auth, or access control issues. You also need: authentication on every endpoint, authorization checks, input validation, rate limiting, and request logging. See our API security guide for the full picture.
Pillai Infotech LLP
We build security into every application we develop — from secure coding practices to DevSecOps pipelines. Let's secure your application.