> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loremstock.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Testing for Node.js Backend Applications

> Identify API vulnerabilities using OWASP API Security Top 10, prevent SQL and NoSQL injection, configure rate limiting and helmet, and automate scanning with npm audit and OWASP ZAP.

Security testing verifies that your API cannot be exploited by attackers. A backend that passes all functional tests can still have critical vulnerabilities. This page covers the OWASP API Security Top 10, common attack types and their prevention, and both manual and automated security testing techniques.

## Why Security Testing

* A single SQL injection can leak an entire user database
* A missing rate limit enables brute-force password attacks
* Improper authorization lets users access each other's data
* These bugs are not caught by functional tests

## OWASP API Security Top 10 (2023)

<CardGroup cols={2}>
  <Card title="API1: Broken Object Level Authorization" icon="lock">
    Users access other users' resources by changing IDs in URLs. `/invoices/123` -> `/invoices/124`. Fix: check ownership on every request.
  </Card>

  <Card title="API2: Broken Authentication" icon="key">
    Weak passwords, missing MFA, flawed token handling. Fix: JWT with strong secrets, short expiry, algorithm enforcement.
  </Card>

  <Card title="API3: Broken Object Property Level Authorization" icon="eye">
    API returns fields the user shouldn't see (passwordHash, isAdmin, balance). Fix: whitelist fields in response.
  </Card>

  <Card title="API4: Unrestricted Resource Consumption" icon="gauge">
    Missing rate limits. Attackers can make unlimited requests to drain resources. Fix: express-rate-limit.
  </Card>

  <Card title="API5: Broken Function Level Authorization" icon="shield">
    Regular users access admin endpoints by guessing URLs. Fix: checkRole middleware on all admin routes.
  </Card>

  <Card title="API6: Unrestricted Sensitive Business Flows" icon="briefcase">
    Bots abuse flows like mass account creation or ticket purchasing. Fix: CAPTCHA, rate limits on sensitive flows.
  </Card>

  <Card title="API7: Server-Side Request Forgery (SSRF)" icon="server">
    API fetches a URL from user input, allowing probing of internal services. Fix: whitelist allowed URLs.
  </Card>

  <Card title="API8: Security Misconfiguration" icon="wrench">
    Default credentials, verbose error messages, unnecessary services, missing HTTPS. Fix: helmet, hide error details in production.
  </Card>

  <Card title="API9: Improper Inventory Management" icon="list">
    Old API versions and shadow endpoints remain exposed. Fix: version your API, deprecate and remove old versions.
  </Card>

  <Card title="API10: Unsafe API Consumption" icon="plug">
    Your API trusts data from third-party APIs without validation. Fix: validate and sanitize all external data.
  </Card>
</CardGroup>

## SQL Injection

### Vulnerable Code

```javascript theme={null}
// NEVER DO THIS
app.get('/users', async (req, res) => {
  const name = req.query.name;
  // Attack input: ' OR '1'='1
  const query = `SELECT * FROM users WHERE name = '${name}'`;
  const users = await db.query(query);
  res.json(users);
});
```

Attack: `GET /users?name=' OR '1'='1` returns ALL users.

### Prevention: Parameterized Queries

```javascript theme={null}
// SAFE: parameterized query (mysql2)
app.get('/users', async (req, res) => {
  const [users] = await pool.execute(
    'SELECT * FROM users WHERE name = ?',
    [req.query.name]  // Driver escapes this value
  );
  res.json(users);
});
```

The mysql2 driver escapes all `?` placeholder values automatically, preventing SQL injection.

### Extra: Input Sanitization with express-validator

```bash theme={null}
npm install express-validator
```

```javascript theme={null}
const { body, validationResult } = require('express-validator');

router.post('/login', [
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 1 }).trim()
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() });
  }
  // Safe to use req.body now
});
```

## Rate Limiting

```bash theme={null}
npm install express-rate-limit
```

```javascript theme={null}
const rateLimit = require('express-rate-limit');

// General API rate limit
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,                  // 100 requests per window per IP
  message: { message: 'Too many requests. Please try again later.' },
  standardHeaders: true,
  legacyHeaders: false
});

// Strict limit for login (prevent brute force)
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,                    // 5 login attempts per 15 minutes
  skipSuccessfulRequests: true
});

app.use('/api/', apiLimiter);
app.use('/api/v1/auth/login', authLimiter);
```

## Helmet: Secure HTTP Headers

```bash theme={null}
npm install helmet
```

```javascript theme={null}
const helmet = require('helmet');
app.use(helmet());
```

Helmet sets these headers automatically:

| Header                      | Protection                    |
| --------------------------- | ----------------------------- |
| `Content-Security-Policy`   | Prevents XSS attacks          |
| `X-Frame-Options`           | Prevents clickjacking         |
| `Strict-Transport-Security` | Enforces HTTPS                |
| `X-Content-Type-Options`    | Prevents MIME sniffing        |
| `Referrer-Policy`           | Controls referrer information |

## CORS Misconfiguration

```javascript theme={null}
// UNSAFE: allows all origins
app.use(cors());
app.use(cors({ origin: '*' }));

// SAFE: whitelist specific origins
app.use(cors({
  origin: (origin, callback) => {
    const allowed = ['https://myapp.com', 'https://admin.myapp.com'];
    if (!origin || allowed.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('CORS: origin not allowed'));
    }
  },
  credentials: true
}));
```

<Warning>
  Never use `origin: '*'` with `credentials: true`. This allows any website to make authenticated requests to your API using the user's cookies.
</Warning>

## JWT Security

### Algorithm Confusion Attack

If you don't specify allowed algorithms, some library versions accept `alg: "none"`:

```javascript theme={null}
// UNSAFE: no algorithm restriction
jwt.verify(token, secret); // Could accept "none" algorithm

// SAFE: restrict to HS256 only
jwt.verify(token, secret, { algorithms: ['HS256'] });
```

### Weak JWT Secret Check

```javascript theme={null}
// On startup, validate secret strength
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
  throw new Error('JWT_SECRET must be at least 32 characters');
}
```

Generate a strong secret:

```bash theme={null}
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```

## Security Testing Tools

### npm audit

```bash theme={null}
npm audit                  # Show all vulnerabilities
npm audit --audit-level=high  # Only show high/critical
npm audit fix              # Auto-fix where possible
```

### Snyk

```bash theme={null}
npm install -g snyk
snyk auth
snyk test                  # Test current project
snyk monitor               # Continuous monitoring
```

### OWASP ZAP (Automated Scanner)

```bash theme={null}
# Quick scan with Docker
docker run -t owasp/zap2docker-stable zap-baseline.py \
  -t http://localhost:3000 \
  -r zap-report.html
```

## Jest Security Tests

Write security-specific tests to verify protections are active:

```javascript theme={null}
// tests/security.test.js
const request = require('supertest');
const app = require('../app');

describe('Authentication security', () => {
  it('returns 401 with no token', async () => {
    const res = await request(app).get('/api/v1/users');
    expect(res.status).toBe(401);
  });

  it('returns 401 with invalid token', async () => {
    const res = await request(app)
      .get('/api/v1/users')
      .set('Authorization', 'Bearer invalid.token.here');
    expect(res.status).toBe(401);
  });

  it('returns 401 with expired token', async () => {
    const expiredToken = jwt.sign(
      { sub: 'user1' },
      process.env.JWT_SECRET,
      { expiresIn: '-1s' } // Expired 1 second ago
    );
    const res = await request(app)
      .get('/api/v1/users')
      .set('Authorization', `Bearer ${expiredToken}`);
    expect(res.status).toBe(401);
  });
});

describe('Rate limiting', () => {
  it('blocks after 5 failed login attempts', async () => {
    for (let i = 0; i < 5; i++) {
      await request(app).post('/api/v1/auth/login')
        .send({ email: 'test@test.com', password: 'wrong' });
    }
    const res = await request(app).post('/api/v1/auth/login')
      .send({ email: 'test@test.com', password: 'wrong' });
    expect(res.status).toBe(429);
  });
});

describe('SQL injection prevention', () => {
  it('rejects login with SQL injection attempt', async () => {
    const res = await request(app)
      .post('/api/v1/auth/login')
      .send({ email: "' OR '1'='1", password: 'anything' });

    // Should NOT successfully authenticate
    expect(res.status).toBe(401);
  });
});
```

## Key Terms

| Term                    | Definition                                                                                        |
| ----------------------- | ------------------------------------------------------------------------------------------------- |
| **OWASP**               | Open Web Application Security Project. Publishes top security risks for APIs and web apps.        |
| **SQL injection**       | Attack inserting malicious SQL into queries via user input. Prevented with parameterized queries. |
| **Parameterized query** | SQL query using `?` placeholders, preventing SQL injection by escaping user input.                |
| **XSS**                 | Cross-Site Scripting. Attacker scripts execute in victim's browser.                               |
| **Rate limiting**       | Restricts requests per IP per time window. Prevents brute force and DoS.                          |
| **CORS**                | Cross-Origin Resource Sharing. Restricts which origins can call your API.                         |
| **Penetration testing** | Simulated attacks to find vulnerabilities before real attackers do.                               |
| **Vulnerability**       | Known security weakness that can be exploited.                                                    |

## Common Mistakes

<CardGroup cols={2}>
  <Card title="Trusting client-side validation only" icon="triangle-exclamation">
    Browser validation can be bypassed with Postman or curl. Always validate on the server.
  </Card>

  <Card title="Returning stack traces in production" icon="triangle-exclamation">
    Stack traces reveal internal structure to attackers. Return generic messages; log details server-side.
  </Card>

  <Card title="Hardcoded secrets in code" icon="triangle-exclamation">
    API keys in source code are visible to anyone with repo access. Always use environment variables.
  </Card>

  <Card title="Not running npm audit in CI" icon="triangle-exclamation">
    Vulnerable dependencies go unnoticed. Add npm audit --audit-level=high to your CI pipeline and fail on critical findings.
  </Card>
</CardGroup>
