Security TestingAdvanced

Security Testing Essentials

Learn how to identify and prevent common security vulnerabilities in web applications

7 min read
...
securityvulnerabilitiesowasptesting

Introduction

Security testing is critical for protecting applications from attacks. This guide covers essential security testing practices and common vulnerabilities from the OWASP Top 10.

Common Vulnerabilities

1. SQL Injection

What is it? Attackers inject malicious SQL code through input fields.

// VULNERABLE CODE ❌
const query = `SELECT * FROM users WHERE email = '${userInput}'`;
db.query(query);
 
// If userInput = "' OR '1'='1"
// Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1'
// Returns ALL users!
 
// SECURE CODE ✅
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [userInput]); // Parameterized query

How to test:

# Test with SQL injection payloads
curl -X POST https://api.example.com/login \
  -d "email=' OR '1'='1&password=anything"
 
# Should return error, not data

2. Cross-Site Scripting (XSS)

What is it? Attackers inject malicious scripts that execute in users' browsers.

// VULNERABLE CODE ❌
// Directly rendering user input
function displayComment(comment) {
  document.getElementById('comments').innerHTML = comment;
}
// If comment = "<script>alert('XSS')</script>"
// Script will execute!
 
// SECURE CODE ✅
// Escape user input
function displayComment(comment) {
  const div = document.createElement('div');
  div.textContent = comment; // Automatically escapes HTML
  document.getElementById('comments').appendChild(div);
}
 
// Or use a framework like React that auto-escapes
function Comment({ text }) {
  return <div>{text}</div>; // React escapes by default
}

How to test:

// Test XSS payloads
const xssPayloads = [
  '<script>alert("XSS")</script>',
  '<img src=x onerror="alert(\'XSS\')">',
  '<svg onload="alert(\'XSS\')">',
];
 
test('should prevent XSS in comments', async () => {
  for (const payload of xssPayloads) {
    await postComment(payload);
    const comments = await getComments();
    expect(comments[0].content).not.toContain('<script>');
  }
});

3. Authentication Issues

Broken Authentication:

// VULNERABLE ❌
// Storing password in plain text
await db.users.create({
  email: 'user@example.com',
  password: 'password123', // Plain text!
});
 
// SECURE ✅
// Hash passwords
import bcrypt from 'bcrypt';
 
const hashedPassword = await bcrypt.hash('password123', 10);
await db.users.create({
  email: 'user@example.com',
  passwordHash: hashedPassword,
});
 
// Verify password
const isValid = await bcrypt.compare(inputPassword, user.passwordHash);

Session Management:

// VULNERABLE ❌
// Predictable session IDs
const sessionId = userId + '_' + Date.now();
 
// SECURE ✅
// Use crypto for random session IDs
import crypto from 'crypto';
 
const sessionId = crypto.randomBytes(32).toString('hex');
 
// Set secure session cookie
res.cookie('session', sessionId, {
  httpOnly: true,  // Not accessible via JavaScript
  secure: true,    // Only sent over HTTPS
  sameSite: 'lax', // CSRF protection
  maxAge: 3600000, // 1 hour expiry
});

4. Cross-Site Request Forgery (CSRF)

What is it? Attackers trick users into performing unwanted actions.

// VULNERABLE ❌
// No CSRF protection
app.post('/api/transfer', (req, res) => {
  const { amount, to } = req.body;
  transferMoney(req.user.id, to, amount);
  res.json({ success: true });
});
 
// SECURE ✅
// Implement CSRF tokens
import csurf from 'csurf';
 
const csrfProtection = csurf({ cookie: true });
 
app.post('/api/transfer', csrfProtection, (req, res) => {
  // CSRF token is automatically validated
  const { amount, to } = req.body;
  transferMoney(req.user.id, to, amount);
  res.json({ success: true });
});
 
// Frontend sends token
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken,
  },
  body: JSON.stringify({ amount, to }),
});

5. Insecure Direct Object References

What is it? Exposing internal object IDs allows unauthorized access.

// VULNERABLE ❌
// Anyone can access any user's data
app.get('/api/users/:id', (req, res) => {
  const user = await db.users.findById(req.params.id);
  res.json(user);
});
 
// SECURE ✅
// Check authorization
app.get('/api/users/:id', async (req, res) => {
  // Verify user owns the resource or has permission
  if (req.user.id !== req.params.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Forbidden' });
  }
 
  const user = await db.users.findById(req.params.id);
  res.json(user);
});

Security Testing Strategies

1. Input Validation Testing

test('should validate email format', () => {
  const invalidEmails = [
    'notanemail',
    '@example.com',
    'user@',
    'user @example.com',
  ];
 
  for (const email of invalidEmails) {
    expect(validateEmail(email)).toBe(false);
  }
});
 
test('should reject long inputs', () => {
  const longString = 'a'.repeat(10001);
  expect(() => validateInput(longString)).toThrow();
});

2. Rate Limiting Testing

test('should enforce rate limits', async () => {
  const requests = Array.from({ length: 101 }, () =>
    fetch('/api/endpoint')
  );
 
  const responses = await Promise.all(requests);
  const rateLimited = responses.filter(r => r.status === 429);
 
  expect(rateLimited.length).toBeGreaterThan(0);
});

3. Access Control Testing

describe('Access Control', () => {
  test('admin can access admin panel', async () => {
    const response = await fetch('/admin', {
      headers: { Authorization: `Bearer ${adminToken}` },
    });
    expect(response.status).toBe(200);
  });
 
  test('regular user cannot access admin panel', async () => {
    const response = await fetch('/admin', {
      headers: { Authorization: `Bearer ${userToken}` },
    });
    expect(response.status).toBe(403);
  });
 
  test('unauthenticated user redirected to login', async () => {
    const response = await fetch('/admin');
    expect(response.status).toBe(401);
  });
});

Security Headers

Implement security headers to protect against common attacks:

import helmet from 'helmet';
 
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
}));

Dependency Security

1. Audit Dependencies

# Check for known vulnerabilities
npm audit
 
# Fix automatically (if possible)
npm audit fix
 
# Check for outdated packages
npm outdated

2. Use Dependency Scanning

# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
 
jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

API Security Testing

Test API Authentication

describe('API Authentication', () => {
  test('requires valid token', async () => {
    const res = await fetch('/api/protected');
    expect(res.status).toBe(401);
  });
 
  test('rejects expired tokens', async () => {
    const expiredToken = generateExpiredToken();
    const res = await fetch('/api/protected', {
      headers: { Authorization: `Bearer ${expiredToken}` },
    });
    expect(res.status).toBe(401);
  });
 
  test('rejects tampered tokens', async () => {
    const tamperedToken = validToken.slice(0, -5) + 'xxxxx';
    const res = await fetch('/api/protected', {
      headers: { Authorization: `Bearer ${tamperedToken}` },
    });
    expect(res.status).toBe(401);
  });
});

Security Testing Checklist

  • Input validation on all user inputs
  • SQL injection prevention (parameterized queries)
  • XSS prevention (output encoding)
  • CSRF protection on state-changing operations
  • Secure session management
  • Password hashing (bcrypt/argon2)
  • Access control enforcement
  • Security headers implemented
  • HTTPS enforced in production
  • Dependency vulnerabilities checked
  • Rate limiting on APIs
  • Error messages don't leak information
  • Logging and monitoring in place

Tools for Security Testing

  1. OWASP ZAP: Automated security scanner
  2. Burp Suite: Comprehensive security testing
  3. Snyk: Dependency vulnerability scanning
  4. npm audit: Node.js dependency checking
  5. SonarQube: Static code analysis
  6. Nmap: Network scanning
  7. SQLMap: SQL injection testing

Best Practices

  1. Defense in Depth: Multiple layers of security
  2. Principle of Least Privilege: Minimal access required
  3. Fail Securely: Errors shouldn't expose information
  4. Keep Software Updated: Patch regularly
  5. Security by Design: Build security in from start
  6. Regular Security Audits: Test continuously
  7. Educate Team: Security is everyone's responsibility

Conclusion

Security testing is not optional. Key takeaways:

  • Understand common vulnerabilities (OWASP Top 10)
  • Implement secure coding practices
  • Test security throughout development
  • Use automated tools for continuous scanning
  • Stay updated on latest threats

Remember: Security is a continuous process, not a one-time task.

Further Reading

Comments (0)

Loading comments...