Testing
Security Testing
Security testing identifies vulnerabilities in your application. It's not optional. A functional, fast app that's vulnerable to hacking is worse than useless—it can harm users and destroy your reputation. Security testing combines automated scanning, manual testing, and code review to find and fix vulnerabilities before attackers do.
Why Security Testing Matters
Vulnerabilities are like rust on a building: you don't see them until they cause structural failure. Common vulnerabilities:
- SQL injection: Attacker inputs malicious SQL to access or modify database data.
- Cross-site scripting (XSS): Attacker injects JavaScript that runs in users' browsers, stealing sessions or data.
- Cross-site request forgery (CSRF): Attacker tricks a user into making unintended requests (transferring money, changing password).
- Weak authentication: Passwords stored insecurely, easily guessed, or not validated properly.
- Sensitive data exposure: API keys, credentials, personal data exposed in code, logs, or repositories.
- Insecure deserialization: Untrusted data is deserialized, allowing code execution.
- Broken access control: Users can access data they shouldn't (seeing other users' accounts, admin features).
The OWASP Top 10 is a list of the most critical web security vulnerabilities. Familiarize yourself with it.
SAST vs DAST
Two approaches to security testing:
| Approach | HowItWorks | Examples | Pros | Cons |
|---|---|---|---|---|
| SAST (Static Application Security Testing) | Analyzes code without running it. Looks for patterns and vulnerabilities in source code. | SonarQube, Snyk (code scanning), Semgrep | Fast (milliseconds), finds issues early, integrates into development | High false positives, can't catch runtime issues, requires source code access |
| DAST (Dynamic Application Security Testing) | Tests running application. Sends payloads to find vulnerabilities, like a hacker would. | OWASP ZAP, Burp Suite, Acunetix | Tests real app behavior, finds runtime issues, no source code needed | Slower, can't find every vulnerability, requires running app |
Best approach: combine both. SAST catches issues during development; DAST tests the running app. Together, they catch more than either alone.
Dependency Vulnerability Scanning
Your dependencies (npm packages, Python libraries, etc.) can have known vulnerabilities. Tools like npm audit and Dependabot scan your dependencies and alert you to issues.
Workflow:
- Tool scans your package.json (or equivalent) against a database of known vulnerabilities.
- If a dependency has a known vulnerability, the tool alerts you.
- You update the dependency to a patched version.
- If no patch exists, you either accept the risk or find an alternative dependency.
This should run in CI. Don't merge code with high-severity vulnerabilities. Tools like Snyk and Dependabot automatically create pull requests with updates, making it easy to stay patched.
Secret Scanning
Developers accidentally commit secrets (API keys, passwords, database credentials) to repositories. Once in Git, they're nearly impossible to remove. Prevent this:
- Pre-commit hooks: Tools like git-secrets or Husky scan commits before they're committed. Blocks committing obvious secrets (AWS keys, JWT patterns, etc.).
- Repository scanning: Gitleaks and similar tools scan the entire repository history for exposed secrets.
- CI scanning: Run secret scanning in CI. Fail the build if secrets are detected.
- Environment variables: Never commit secrets. Use environment variables or secret management tools (Vault, AWS Secrets Manager).
If you discover a secret in a repository, rotate it immediately (change the password, regenerate the key). Assume it's compromised.
Container Image Scanning
If you use Docker, scan images for vulnerabilities. Tools like Trivy scan Docker images for known vulnerabilities in OS packages and dependencies.
Scan images during CI, before pushing to a registry. Don't deploy images with high-severity vulnerabilities. Keep base images updated (pull the latest version of ubuntu:latest, python:3.11, etc.) to get security patches.
Security Testing in CI Pipelines
Integrate security scanning into your CI pipeline:
- On every commit, run SAST (SonarQube, Snyk code scan).
- Scan dependencies (npm audit, Snyk dependencies, Dependabot).
- Scan for secrets (Gitleaks, git-secrets).
- If building containers, scan the image (Trivy).
- Fail the build if high-severity issues are found.
This automated scanning catches most common issues. Combined with manual security review for critical code, it provides solid coverage.
Manual Security Testing
Automated tools catch known vulnerabilities, but human testers find creative exploits tools miss. Manual security testing includes:
- Input validation testing: Try to submit invalid input (SQL, scripts, very long strings, special characters). Does the app handle it correctly?
- Authentication testing: Try to bypass login. Can you access data without credentials? Can you access other users' data?
- Access control testing: Try to perform actions you shouldn't be able to. Admin functions as a user? Delete other users' data?
- Business logic flaws: Can you exploit the application logic? Transfer money twice? Get discounts improperly?
Manual testing is slower but finds deeper issues. For critical applications (financial, healthcare), invest in manual security review.
OWASP Testing Guide (WSTG)
The OWASP Web Security Testing Guide is a comprehensive manual for testing web applications. It covers:
- Information gathering (what can you learn about the app?)
- Configuration testing (are defaults secure?)
- Authentication testing (is login secure?)
- Authorization testing (are access controls enforced?)
- Business logic testing (can you abuse the app's intended flow?)
If you're doing manual security testing, the WSTG is your bible. It guides you through comprehensive testing without missing categories.
Penetration Testing vs Security Testing
These terms are sometimes confused:
- Security testing: Your team tests the app for vulnerabilities. Ongoing, part of development.
- Penetration testing: External experts try to break into your app (like a hacker would). Usually a one-time engagement.
Pen testing is valuable but expensive. It's typically done before major launches or on a yearly basis. Security testing (automated + manual) should be continuous.
CVSS Scores and Severity
CVSS (Common Vulnerability Scoring System) scores vulnerabilities from 0-10:
- Critical (9-10): Drop everything, fix immediately. The app is compromised.
- High (7-8.9): Fix within days. Major vulnerability.
- Medium (4-6.9): Fix within weeks. Should be addressed but not emergency.
- Low (0.1-3.9): Fix when convenient. Theoretical risk or requires specific conditions.
Don't fix every vulnerability. Prioritize by severity. A critical vulnerability requires immediate action; a low one can wait for the next release cycle.
Reporting and Prioritizing Security Findings
When you find a vulnerability, report it clearly to developers:
- Severity: CVSS score and category (critical, high, medium, low).
- Vulnerability type: SQL injection, XSS, weak authentication, etc.
- Location: Which file, function, endpoint is vulnerable?
- Impact: What can an attacker do? Access data? Crash the app? Steal money?
- How to reproduce: Steps to exploit the vulnerability, without actually exploiting it.
- Recommendation: How to fix it? Use parameterized queries, sanitize input, require authentication, etc.
Clear reporting makes it easier to fix vulnerabilities quickly. Vague reports get deprioritized.
Security Champions
Many teams designate a security champion: a developer who owns security practices. They:
- Stay updated on new vulnerabilities and best practices
- Review code for security issues
- Lead security training for the team
- Manage security tools and scanning
- Prioritize security vulnerabilities
This role elevates security culture. It prevents security from being an afterthought.
Key Takeaways
Security testing identifies vulnerabilities before attackers do. Use SAST tools (SonarQube, Snyk) to scan code during development. Scan dependencies (npm audit, Dependabot) for known vulnerabilities. Prevent secrets from being committed (pre-commit hooks, secret scanning). Scan container images (Trivy). Combine automated scanning with manual testing and code review. Use OWASP WSTG for comprehensive manual testing. Prioritize vulnerabilities by CVSS severity. Security is ongoing; scan regularly and keep dependencies updated. A secure app is as important as a fast, functional one.