Article 9: Security Testing for QA Engineers - Beginner to Pro Guide
Security Testing for QA Engineers: Beginner to Pro Guide
Learn to find security bugs step-by-step — no prior security experience needed!
🎯 Who Is This Article For?
This article is designed for:
- ✅ Complete beginners who never did security testing before
- ✅ QA Engineers who want to add security skills
- ✅ Developers curious about security testing
- ✅ Students learning about web security
No prerequisites required! We’ll explain everything from scratch with simple analogies.
📍 You Are Here:
[✓] Article 1: QA Fundamentals
[✓] Article 2: QA Practice
[✓] Article 3: DSA for QA
[✓] Article 4: Automation Frameworks
[✓] Article 5: CI/CD
[✓] Article 6: Test Design Techniques
[✓] Article 7: Performance Testing
[✓] Article 8: Landing Your Job at Apple
[→] Article 9: Security Testing for QA
Progress: 100% + Bonus ✨
🏠 What is Security Testing? (Simple Explanation)
Think of Your App as a House
Imagine your web application is a house:
🏠 YOUR APPLICATION = A HOUSE
Front Door = Login Page (authentication)
Rooms = Different pages/features
Valuables = User data, money, private info
Locks = Passwords, permissions
Burglar = Hacker trying to break in
Security Guard = Security testing (that's you!)
Security testing is like being a friendly burglar who tries to break into the house BEFORE real burglars do. You find the weak spots and fix them!
Real-Life Analogy
| House Security | App Security |
|---|---|
| Checking if windows lock properly | Testing if login can be bypassed |
| Making sure only family has keys | Verifying only authorized users access data |
| Testing if alarm works | Checking if suspicious activity is logged |
| Hiding valuables in a safe | Encrypting sensitive data |
| Not leaving spare key under mat | Not hardcoding passwords in code |
🔰 Security Testing for Absolute Beginners
Step 1: Understand the Hacker Mindset
Hackers think differently. They ask:
- “What if I do something unexpected?”
- “What if I send weird data?”
- “What if I pretend to be someone else?”
- “What if I access things I shouldn’t?”
Your job: Think like a hacker, but help fix the problems instead of exploiting them.
Step 2: Learn the Basic Terminology
| Term | Simple Explanation | Example |
|---|---|---|
| Vulnerability | A weakness that can be exploited | Unlocked door |
| Exploit | Taking advantage of a vulnerability | Walking through the unlocked door |
| Attack Vector | The path hackers use to attack | Front door, window, chimney |
| Authentication | Proving WHO you are | Showing your ID |
| Authorization | Checking WHAT you can do | VIP pass to backstage |
| Injection | Sneaking malicious code into inputs | Hiding a bomb in a gift box |
| XSS | Making a website run your evil script | Putting a fake sign in someone’s store |
| CSRF | Tricking users to do actions unknowingly | Making someone sign a document while distracted |
Step 3: Your First Security Test (Try It Now!)
Let’s do your first security test. It takes 2 minutes!
Test: Check for Information Disclosure
- Open any website you have permission to test
- Open Developer Tools (F12)
- Go to “Network” tab
- Click around the site
- Look at the API responses
What to look for:
❌ BAD: API returns password field (even if empty)
❌ BAD: Error messages show database details
❌ BAD: Response includes internal IDs or emails of other users
✅ GOOD: Only necessary data is returned
Congratulations! 🎉 You just did your first security test!
Why QA Engineers Should Know Security Testing
In today’s world, security is everyone’s responsibility. As a QA engineer, you’re already skilled at finding bugs — security vulnerabilities are just a special category of bugs with severe consequences.
Why it matters:
- 🔓 Data breaches cost companies millions
- 💼 Security skills increase your market value by 30-50%
- 🎯 Many companies expect QA to perform basic security checks
- 🏆 Finding security bugs = high-impact contributions
Part 1: OWASP Top 10 - The Foundation
The OWASP Top 10 is the standard awareness document for web application security. Every QA engineer should understand these vulnerabilities.
📚 Want to go deeper? Check out Article 10: OWASP Top 10 Deep Dive for comprehensive coverage with labs, real-world examples, and complete test suites for each vulnerability.
🎓 OWASP Top 10 Explained Simply
Think of OWASP Top 10 as the “Top 10 Most Common Ways Hackers Break Into Websites”. It’s like a “Most Wanted” list for security bugs!
| # | Vulnerability | Simple Explanation | House Analogy |
|---|---|---|---|
| 1 | Broken Access Control | Accessing things you shouldn’t | Neighbor walking into your bedroom |
| 2 | Cryptographic Failures | Poor protection of secrets | Writing passwords on sticky notes |
| 3 | Injection | Sneaking commands into inputs | Fake delivery person with bad intentions |
| 4 | Insecure Design | Fundamentally flawed architecture | House with no locks by design |
| 5 | Security Misconfiguration | Wrong settings | Leaving all windows open |
| 6 | Vulnerable Components | Using broken parts | Using a rusty, broken lock |
| 7 | Authentication Failures | Weak identity verification | Accepting any ID card without checking |
| 8 | Integrity Failures | Trusting unverified data | Installing software from random USB |
| 9 | Logging Failures | Not recording what happens | No security cameras |
| 10 | SSRF | Server makes requests to bad places | Your assistant calling scam numbers |
1. Broken Access Control (A01:2021)
What it is: Users can access resources or perform actions they shouldn’t be able to.
How to test:
✓ Try accessing other users' data by changing IDs in URLs
✓ Test horizontal privilege escalation (user A → user B's data)
✓ Test vertical privilege escalation (user → admin actions)
✓ Check if you can bypass access controls by modifying requests
Example test cases:
# Test: Can user access another user's profile?
# Original URL: /api/users/123/profile
# Modify to: /api/users/456/profile
# Test: Can regular user access admin endpoints?
# Try: /api/admin/users (as regular user)
# Test: IDOR (Insecure Direct Object Reference)
def test_idor_vulnerability():
# Login as user A
response = client.get("/api/orders/1001") # User A's order
assert response.status_code == 200
# Try to access user B's order
response = client.get("/api/orders/1002") # User B's order
assert response.status_code == 403 # Should be forbidden!
2. Cryptographic Failures (A02:2021)
What it is: Failure to protect sensitive data with proper encryption.
How to test:
✓ Check if sensitive data is transmitted over HTTPS
✓ Verify passwords are hashed (not stored in plain text)
✓ Check for sensitive data in URLs, logs, or error messages
✓ Test if old/weak encryption algorithms are used
Checklist:
| Area | What to Check |
|---|---|
| HTTPS | All pages use HTTPS, no mixed content |
| Passwords | Hashed with bcrypt/Argon2, never logged |
| API Keys | Not exposed in client-side code |
| Cookies | Secure & HttpOnly flags set |
| Headers | HSTS enabled |
3. Injection (A03:2021)
What it is: Untrusted data is sent to an interpreter as part of a command or query.
Types of injection:
- SQL Injection - Database queries
- XSS - Browser scripts
- Command Injection - OS commands
- LDAP Injection - Directory services
SQL Injection Test Cases:
-- Basic SQL injection payloads to test:
' OR '1'='1
' OR '1'='1' --
'; DROP TABLE users; --
' UNION SELECT username, password FROM users --
-- In search fields, login forms, URL parameters
Testing in practice:
# Test login form for SQL injection
def test_sql_injection_login():
payloads = [
"' OR '1'='1",
"admin'--",
"' OR 1=1--",
"1; DROP TABLE users",
]
for payload in payloads:
response = client.post("/login", data={
"username": payload,
"password": "anything"
})
# Should NOT log in with injection
assert "Welcome" not in response.text
assert response.status_code != 200
4. Insecure Design (A04:2021)
What it is: Missing or ineffective security controls in the design phase.
How to test:
✓ Review business logic for security flaws
✓ Test rate limiting on sensitive operations
✓ Check for missing security requirements
✓ Verify threat modeling was done
Example scenarios:
Scenario: Password reset without verification
Given I request a password reset for any email
When I receive the reset link
Then I should NOT be able to reset password without email access
Scenario: Unlimited login attempts
Given the login form exists
When I try 100 wrong passwords
Then the account should be locked or rate-limited
5. Security Misconfiguration (A05:2021)
What it is: Insecure default configurations, incomplete setups, or verbose error messages.
How to test:
✓ Check for default credentials
✓ Look for unnecessary features enabled
✓ Verify error messages don't leak info
✓ Check security headers
Security Headers Checklist:
# Required security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Test with curl:
# Check security headers
curl -I https://yoursite.com | grep -E "(Strict|X-Content|X-Frame|Content-Security|X-XSS)"
6. Vulnerable and Outdated Components (A06:2021)
What it is: Using libraries, frameworks, or components with known vulnerabilities.
How to test:
# NPM projects
npm audit
# Python projects
pip-audit
safety check
# Check specific CVEs
# Use Snyk, OWASP Dependency-Check, or GitHub Dependabot
7. Identification and Authentication Failures (A07:2021)
What it is: Weak authentication mechanisms that allow attackers to compromise user accounts.
How to test:
✓ Test for weak password policies
✓ Check session management (timeout, invalidation)
✓ Verify MFA implementation
✓ Test "remember me" functionality
✓ Check password reset flow
Test cases:
def test_weak_password_allowed():
# Should NOT allow weak passwords
weak_passwords = ["123456", "password", "qwerty", "abc123"]
for pwd in weak_passwords:
response = client.post("/register", data={
"username": "testuser",
"password": pwd
})
assert "Password too weak" in response.text
def test_session_fixation():
# Get session before login
session_before = client.cookies.get('session_id')
# Login
client.post("/login", data={"username": "user", "password": "pass"})
# Session should change after login
session_after = client.cookies.get('session_id')
assert session_before != session_after
8. Software and Data Integrity Failures (A08:2021)
What it is: Code and infrastructure that doesn’t protect against integrity violations.
How to test:
✓ Verify software updates come from trusted sources
✓ Check CI/CD pipeline security
✓ Test for insecure deserialization
✓ Verify digital signatures on updates
9. Security Logging and Monitoring Failures (A09:2021)
What it is: Insufficient logging and monitoring that allows attacks to go undetected.
How to test:
✓ Verify login failures are logged
✓ Check if security events are alerted
✓ Test log integrity (no tampering)
✓ Verify PII is not logged
What should be logged:
| Event | Priority |
|---|---|
| Login failures | High |
| Access control failures | High |
| Input validation failures | Medium |
| Admin actions | High |
| Password changes | Medium |
10. Server-Side Request Forgery (SSRF) (A10:2021)
What it is: Application fetches a remote resource without validating the user-supplied URL.
How to test:
# Test SSRF in URL input fields
ssrf_payloads = [
"http://localhost/admin",
"http://127.0.0.1:22",
"http://169.254.169.254/latest/meta-data/", # AWS metadata
"file:///etc/passwd",
"http://internal-server.local/",
]
def test_ssrf_prevention():
for payload in ssrf_payloads:
response = client.post("/fetch-url", data={"url": payload})
assert response.status_code == 400
assert "Invalid URL" in response.text
Part 2: Essential Security Testing Techniques
2.1 Input Validation Testing
Golden Rule: Never trust user input!
# Comprehensive input validation test suite
class TestInputValidation:
# XSS Payloads
xss_payloads = [
"<script>alert('XSS')</script>",
"<img src=x onerror=alert('XSS')>",
"javascript:alert('XSS')",
"<svg onload=alert('XSS')>",
"'-alert('XSS')-'",
"<body onload=alert('XSS')>",
]
# SQL Injection Payloads
sqli_payloads = [
"' OR '1'='1",
"1; DROP TABLE users--",
"' UNION SELECT * FROM users--",
"1' AND '1'='1",
]
# Path Traversal Payloads
path_payloads = [
"../../../etc/passwd",
"....//....//....//etc/passwd",
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd",
]
def test_xss_in_all_inputs(self, all_input_fields):
for field in all_input_fields:
for payload in self.xss_payloads:
response = submit_form(field, payload)
assert payload not in response.text
assert html.escape(payload) in response.text or \
"Invalid input" in response.text
2.2 Authentication Testing
class TestAuthentication:
def test_brute_force_protection(self):
"""Test that account lockout works"""
for i in range(10):
response = login("admin", f"wrong_password_{i}")
# Account should be locked
response = login("admin", "correct_password")
assert "Account locked" in response.text
def test_password_complexity(self):
"""Test password requirements"""
weak_passwords = [
("short", "Too short"),
("alllowercase", "Needs uppercase"),
("ALLUPPERCASE", "Needs lowercase"),
("NoNumbers!", "Needs number"),
("NoSpecial123", "Needs special character"),
]
for password, expected_error in weak_passwords:
response = register(password=password)
assert expected_error in response.text
def test_session_expiration(self):
"""Test that sessions expire"""
token = login_and_get_token()
# Wait for session timeout (or mock time)
time.sleep(SESSION_TIMEOUT + 1)
response = access_protected_resource(token)
assert response.status_code == 401
2.3 Authorization Testing (Access Control)
class TestAuthorization:
def test_horizontal_privilege_escalation(self):
"""User A should not access User B's data"""
# Login as User A
token_a = login("user_a", "password_a")
# Get User A's order
response = get_order(token_a, order_id=100) # User A's order
assert response.status_code == 200
# Try to access User B's order
response = get_order(token_a, order_id=200) # User B's order
assert response.status_code == 403
def test_vertical_privilege_escalation(self):
"""Regular user should not access admin functions"""
user_token = login("regular_user", "password")
admin_endpoints = [
"/api/admin/users",
"/api/admin/settings",
"/api/admin/logs",
]
for endpoint in admin_endpoints:
response = client.get(endpoint, headers={"Authorization": user_token})
assert response.status_code == 403
def test_idor_in_api(self):
"""Test Insecure Direct Object References"""
user_token = login("user1", "password")
# Should access own profile
response = client.get("/api/users/1/profile",
headers={"Authorization": user_token})
assert response.status_code == 200
# Should NOT access other's profile
response = client.get("/api/users/2/profile",
headers={"Authorization": user_token})
assert response.status_code == 403
2.4 API Security Testing
class TestAPISecurity:
def test_rate_limiting(self):
"""API should have rate limiting"""
responses = []
for i in range(100):
response = client.get("/api/search?q=test")
responses.append(response.status_code)
# Should see 429 (Too Many Requests) after limit
assert 429 in responses
def test_api_versioning(self):
"""Old API versions should be deprecated"""
response = client.get("/api/v1/users") # Old version
assert response.status_code == 410 # Gone
def test_sensitive_data_exposure(self):
"""API should not expose sensitive fields"""
response = client.get("/api/users/1")
user_data = response.json()
sensitive_fields = ["password", "ssn", "credit_card", "api_key"]
for field in sensitive_fields:
assert field not in user_data
def test_mass_assignment(self):
"""Users should not be able to set admin fields"""
response = client.put("/api/users/1", json={
"name": "John",
"role": "admin", # Should be ignored
"is_verified": True # Should be ignored
})
user = get_user(1)
assert user["role"] != "admin"
assert user["is_verified"] != True
Part 3: Security Testing Tools
3.1 Browser Developer Tools
What you can find:
- Sensitive data in localStorage/sessionStorage
- API keys in JavaScript files
- Insecure cookies (missing Secure/HttpOnly flags)
- Mixed content warnings
- CSP violations
// Check for sensitive data in browser storage
console.log("LocalStorage:", localStorage);
console.log("SessionStorage:", sessionStorage);
console.log("Cookies:", document.cookie);
// Check for exposed API keys in window object
Object.keys(window).filter(k =>
k.toLowerCase().includes('key') ||
k.toLowerCase().includes('token') ||
k.toLowerCase().includes('secret')
);
3.2 Burp Suite (Essential Tool)
Key features for QA:
- Proxy - Intercept and modify requests
- Repeater - Replay and modify requests
- Intruder - Automated testing with payloads
- Scanner - Automatic vulnerability detection
Basic workflow:
1. Configure browser to use Burp proxy (127.0.0.1:8080)
2. Browse the application normally
3. Review requests in Proxy > HTTP history
4. Send interesting requests to Repeater
5. Modify and replay to test vulnerabilities
3.3 OWASP ZAP (Free Alternative)
# Run ZAP in headless mode for CI/CD
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://yoursite.com \
-r report.html
3.4 Command Line Tools
# Nikto - Web server scanner
nikto -h https://yoursite.com
# SQLMap - SQL injection testing
sqlmap -u "https://yoursite.com/search?q=test" --batch
# Nmap - Network scanning
nmap -sV -sC yoursite.com
# SSLyze - SSL/TLS testing
sslyze yoursite.com
# Check security headers
curl -I https://yoursite.com | grep -iE "strict|x-frame|x-content|csp|x-xss"
3.5 Automated Security Testing in CI/CD
# GitHub Actions example
name: Security Tests
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Dependency scanning
- name: Run npm audit
run: npm audit --audit-level=high
# SAST (Static Analysis)
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
# DAST (Dynamic Analysis)
- name: Run OWASP ZAP
uses: zaproxy/action-baseline@v0.7.0
with:
target: 'https://staging.yoursite.com'
# Secret scanning
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
Part 4: Security Testing Checklist
Pre-Release Security Checklist
## Authentication & Session Management
- [ ] Strong password policy enforced
- [ ] Account lockout after failed attempts
- [ ] Session timeout implemented
- [ ] Session invalidation on logout
- [ ] Secure session token generation
- [ ] MFA available for sensitive accounts
## Authorization
- [ ] Role-based access control working
- [ ] No horizontal privilege escalation
- [ ] No vertical privilege escalation
- [ ] API endpoints properly protected
- [ ] Admin functions restricted
## Input Validation
- [ ] All inputs validated server-side
- [ ] SQL injection tested and blocked
- [ ] XSS tested and blocked
- [ ] File upload restrictions in place
- [ ] Path traversal blocked
## Data Protection
- [ ] HTTPS everywhere (no mixed content)
- [ ] Sensitive data encrypted at rest
- [ ] No sensitive data in URLs
- [ ] Passwords properly hashed
- [ ] PII not logged
## Security Headers
- [ ] HSTS enabled
- [ ] X-Content-Type-Options: nosniff
- [ ] X-Frame-Options: DENY
- [ ] Content-Security-Policy set
- [ ] Referrer-Policy configured
## Error Handling
- [ ] Generic error messages to users
- [ ] No stack traces in production
- [ ] No sensitive info in errors
## Dependencies
- [ ] No known vulnerabilities
- [ ] Dependencies up to date
- [ ] License compliance checked
Part 5: Real-World Security Bug Examples
Example 1: IDOR in E-commerce
Bug: Order details exposed via predictable URLs
Severity: High
URL: /api/orders/12345
Steps to reproduce:
1. Login as User A
2. View your order: /api/orders/100
3. Change URL to: /api/orders/101 (another user's order)
4. Observe: Other user's order details visible
Impact: Any authenticated user can view any order,
exposing personal data, addresses, payment info
Fix: Verify order belongs to authenticated user
Example 2: SQL Injection in Search
Bug: SQL Injection in product search
Severity: Critical
URL: /search?q=
Steps to reproduce:
1. Go to search page
2. Enter: ' OR '1'='1' --
3. Observe: All products returned (SQL executed)
4. Enter: ' UNION SELECT username, password FROM users --
5. Observe: User credentials exposed
Impact: Full database access, data breach
Fix: Use parameterized queries
Example 3: XSS in User Profile
Bug: Stored XSS in profile bio field
Severity: High
Location: User profile bio
Steps to reproduce:
1. Go to profile settings
2. Enter bio: <script>alert(document.cookie)</script>
3. Save profile
4. View profile (as any user)
5. Observe: JavaScript executes
Impact: Session hijacking, account takeover
Fix: Sanitize output, implement CSP
Part 6: Security Testing Interview Questions
Common Questions & Answers
Q: What is the difference between authentication and authorization?
Authentication: Verifying WHO you are (identity)
- Username/password, MFA, biometrics
Authorization: Verifying WHAT you can do (permissions)
- Role checks, access control lists
Q: Explain SQL Injection and how to prevent it.
SQL Injection: Inserting malicious SQL through user input
Prevention:
1. Use parameterized queries (prepared statements)
2. Use ORM frameworks
3. Input validation and sanitization
4. Least privilege database permissions
Example (Python):
# Bad (vulnerable)
query = f"SELECT * FROM users WHERE id = {user_id}"
# Good (parameterized)
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))
Q: What is XSS and what are the types?
XSS (Cross-Site Scripting): Injecting malicious scripts
Types:
1. Stored XSS - Script saved in database
2. Reflected XSS - Script in URL, reflected back
3. DOM XSS - Script manipulates DOM directly
Prevention:
- Output encoding
- Content Security Policy (CSP)
- Input validation
- HttpOnly cookies
Q: How do you test for broken access control?
1. Test IDOR - Change IDs in URLs/APIs
2. Test horizontal escalation - Access other users' data
3. Test vertical escalation - Access admin functions
4. Test missing function level access control
5. Test JWT/token manipulation
6. Test path traversal
Part 7: Building Your Security Testing Skills
Learning Path
Level 1: Foundations (Week 1-2)
├── OWASP Top 10
├── Basic web security concepts
├── Browser DevTools for security
└── HTTP/HTTPS fundamentals
Level 2: Tools (Week 3-4)
├── Burp Suite basics
├── OWASP ZAP
├── Command line tools
└── Browser extensions
Level 3: Practice (Week 5-8)
├── OWASP WebGoat
├── Damn Vulnerable Web Application (DVWA)
├── HackTheBox
├── PortSwigger Web Security Academy
└── Bug bounty programs (read-only first)
Level 4: Integration (Week 9-12)
├── Security in CI/CD
├── Threat modeling
├── Security requirements
└── Security automation
Free Resources
| Resource | Type | Link |
|---|---|---|
| OWASP WebGoat | Practice | https://owasp.org/www-project-webgoat/ |
| PortSwigger Academy | Learning | https://portswigger.net/web-security |
| HackTheBox | CTF | https://www.hackthebox.com/ |
| TryHackMe | Learning | https://tryhackme.com/ |
| OWASP Testing Guide | Guide | https://owasp.org/www-project-testing/ |
Summary
As a QA engineer, adding security testing to your skillset:
✅ Increases your value - Security skills are in high demand
✅ Improves product quality - Find bugs before hackers do
✅ Protects users - Prevent data breaches and identity theft
✅ Advances your career - Opens doors to security roles
Key takeaways:
- Learn the OWASP Top 10 by heart
- Practice with vulnerable applications
- Add security checks to your test plans
- Use tools like Burp Suite and ZAP
- Automate security tests in CI/CD
What’s Next?
Continue your learning journey:
- 🔒 Get certified: CompTIA Security+, CEH, OSCP
- 🐛 Join bug bounty programs: HackerOne, Bugcrowd
- 📚 Read: “The Web Application Hacker’s Handbook”
- 🛠️ Practice daily with CTF challenges
Remember: Security is not a one-time activity. It’s a mindset that should be part of every testing activity!
Happy (ethical) hacking! 🔐