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 SecurityApp Security
Checking if windows lock properlyTesting if login can be bypassed
Making sure only family has keysVerifying only authorized users access data
Testing if alarm worksChecking if suspicious activity is logged
Hiding valuables in a safeEncrypting sensitive data
Not leaving spare key under matNot 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

TermSimple ExplanationExample
VulnerabilityA weakness that can be exploitedUnlocked door
ExploitTaking advantage of a vulnerabilityWalking through the unlocked door
Attack VectorThe path hackers use to attackFront door, window, chimney
AuthenticationProving WHO you areShowing your ID
AuthorizationChecking WHAT you can doVIP pass to backstage
InjectionSneaking malicious code into inputsHiding a bomb in a gift box
XSSMaking a website run your evil scriptPutting a fake sign in someone’s store
CSRFTricking users to do actions unknowinglyMaking 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

  1. Open any website you have permission to test
  2. Open Developer Tools (F12)
  3. Go to “Network” tab
  4. Click around the site
  5. 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!

#VulnerabilitySimple ExplanationHouse Analogy
1Broken Access ControlAccessing things you shouldn’tNeighbor walking into your bedroom
2Cryptographic FailuresPoor protection of secretsWriting passwords on sticky notes
3InjectionSneaking commands into inputsFake delivery person with bad intentions
4Insecure DesignFundamentally flawed architectureHouse with no locks by design
5Security MisconfigurationWrong settingsLeaving all windows open
6Vulnerable ComponentsUsing broken partsUsing a rusty, broken lock
7Authentication FailuresWeak identity verificationAccepting any ID card without checking
8Integrity FailuresTrusting unverified dataInstalling software from random USB
9Logging FailuresNot recording what happensNo security cameras
10SSRFServer makes requests to bad placesYour 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:

AreaWhat to Check
HTTPSAll pages use HTTPS, no mixed content
PasswordsHashed with bcrypt/Argon2, never logged
API KeysNot exposed in client-side code
CookiesSecure & HttpOnly flags set
HeadersHSTS 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:

EventPriority
Login failuresHigh
Access control failuresHigh
Input validation failuresMedium
Admin actionsHigh
Password changesMedium

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:

  1. Proxy - Intercept and modify requests
  2. Repeater - Replay and modify requests
  3. Intruder - Automated testing with payloads
  4. 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
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

ResourceTypeLink
OWASP WebGoatPracticehttps://owasp.org/www-project-webgoat/
PortSwigger AcademyLearninghttps://portswigger.net/web-security
HackTheBoxCTFhttps://www.hackthebox.com/
TryHackMeLearninghttps://tryhackme.com/
OWASP Testing GuideGuidehttps://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:

  1. Learn the OWASP Top 10 by heart
  2. Practice with vulnerable applications
  3. Add security checks to your test plans
  4. Use tools like Burp Suite and ZAP
  5. 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! 🔐