Skip to main content
Security Testing·Lesson 3 of 5

Vulnerability Scanning

Vulnerability scanning automates the discovery of known security weaknesses in applications and infrastructure. While manual testing catches logic flaws, scanners excel at finding known patterns across large surfaces quickly.

Manual vs Automated Scanning

AspectManual TestingAutomated Scanning
SpeedSlow, thoroughFast, broad coverage
Logic FlawsCan detectUsually misses
Known VulnsTime-consumingExcellent
False PositivesLowModerate to high
ScalabilityLimitedHighly scalable
CostHigh (skilled testers)Lower per scan

The best approach combines both: automated scans for breadth, manual testing for depth.

OWASP ZAP: Your Primary Scanner

OWASP ZAP (Zed Attack Proxy) is a free, open-source security scanner maintained by the OWASP Foundation.

Setting Up ZAP

# Install ZAP
# macOS
brew install --cask owasp-zap

# Docker (recommended for CI)
docker pull ghcr.io/zaproxy/zaproxy:stable

# Start ZAP in daemon mode for scripting
docker run -u zap -p 8080:8080 \
  ghcr.io/zaproxy/zaproxy:stable \
  zap.sh -daemon -host 0.0.0.0 -port 8080

Running a Baseline Scan

The baseline scan is a quick, passive scan that checks for common issues without actively attacking the target.

# ZAP baseline scan via Docker
docker run -t ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py -t https://target-app.com \
  -r report.html

# The scan will:
# 1. Spider the application to discover pages
# 2. Passively analyze all requests and responses
# 3. Check for missing security headers
# 4. Identify information disclosure
# 5. Generate an HTML report

Running a Full Scan

The full scan includes active attacks and takes significantly longer.

# ZAP full scan
docker run -t ghcr.io/zaproxy/zaproxy:stable \
  zap-full-scan.py -t https://target-app.com \
  -r full-report.html \
  -z "-config scanner.maxScanDurationInMins=60"

ZAP API Scripting

import requests
import time

ZAP_API = "http://localhost:8080"
API_KEY = "your-api-key"
TARGET = "https://target-app.com"

# Start a spider scan
spider_response = requests.get(f"{ZAP_API}/JSON/spider/action/scan/", params={
    "apikey": API_KEY,
    "url": TARGET,
    "maxChildren": 10
})
scan_id = spider_response.json()["scan"]

# Wait for spider to complete
while True:
    progress = requests.get(f"{ZAP_API}/JSON/spider/view/status/", params={
        "apikey": API_KEY,
        "scanId": scan_id
    })
    status = int(progress.json()["status"])
    print(f"Spider progress: {status}%")
    if status >= 100:
        break
    time.sleep(5)

# Start active scan
active_response = requests.get(f"{ZAP_API}/JSON/ascan/action/scan/", params={
    "apikey": API_KEY,
    "url": TARGET
})
active_scan_id = active_response.json()["scan"]

# Wait for active scan to complete
while True:
    progress = requests.get(f"{ZAP_API}/JSON/ascan/view/status/", params={
        "apikey": API_KEY,
        "scanId": active_scan_id
    })
    status = int(progress.json()["status"])
    print(f"Active scan progress: {status}%")
    if status >= 100:
        break
    time.sleep(10)

# Get alerts
alerts = requests.get(f"{ZAP_API}/JSON/core/view/alerts/", params={
    "apikey": API_KEY,
    "baseurl": TARGET
})

for alert in alerts.json()["alerts"]:
    print(f"[{alert['risk']}] {alert['alert']}")
    print(f"  URL: {alert['url']}")
    print(f"  Description: {alert['description'][:100]}")
    print()

Nikto: Web Server Scanner

Nikto specializes in web server misconfigurations and known vulnerable files.

# Install Nikto
sudo apt install nikto  # Debian/Ubuntu
brew install nikto      # macOS

# Basic scan
nikto -h https://target-app.com -o report.html -Format html

# Scan specific ports
nikto -h target-app.com -p 80,443,8080

# Tune the scan for specific test categories
# 1 = Interesting File / Seen in logs
# 2 = Misconfiguration / Default File
# 3 = Information Disclosure
# 4 = Injection (XSS/Script/HTML)
nikto -h https://target-app.com -Tuning 1234

Nuclei: Template-Based Scanner

Nuclei uses YAML templates to detect vulnerabilities, making it highly extensible.

# Install Nuclei
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# Or via Docker
docker pull projectdiscovery/nuclei:latest

# Run with default templates
nuclei -u https://target-app.com

# Run specific template categories
nuclei -u https://target-app.com -tags cve,misconfig

# Run with severity filter
nuclei -u https://target-app.com -severity critical,high

Writing a Custom Nuclei Template

id: custom-api-key-exposure

info:
  name: API Key Exposure Check
  author: your-name
  severity: high
  description: Checks for exposed API keys in JavaScript files
  tags: exposure,api,keys

requests:
  - method: GET
    path:
      - "{{BaseURL}}/js/app.js"
      - "{{BaseURL}}/js/config.js"
      - "{{BaseURL}}/static/js/main.js"

    matchers-condition: or
    matchers:
      - type: regex
        regex:
          - "(?i)(api[_-]?key|apikey)\\s*[:=]\\s*['\"][a-zA-Z0-9]{20,}['\"]"
          - "(?i)(secret[_-]?key)\\s*[:=]\\s*['\"][a-zA-Z0-9]{20,}['\"]"
          - "(?i)(access[_-]?token)\\s*[:=]\\s*['\"][a-zA-Z0-9]{20,}['\"]"

Interpreting Scan Results

Not every finding is a real vulnerability. You must triage results carefully.

Severity Classification

Critical (CVSS 9.0-10.0)
  Action: Fix immediately
  Example: Remote code execution, SQL injection

High (CVSS 7.0-8.9)
  Action: Fix within 24-48 hours
  Example: Authentication bypass, stored XSS

Medium (CVSS 4.0-6.9)
  Action: Fix within 1-2 weeks
  Example: Missing security headers, CSRF

Low (CVSS 0.1-3.9)
  Action: Fix in next release cycle
  Example: Information disclosure, verbose errors

Informational (CVSS 0.0)
  Action: Note for future reference
  Example: Server version disclosure

Handling False Positives

# Document false positives in a suppression file
# false-positives.json
{
    "suppressions": [
        {
            "tool": "zap",
            "alert_id": "10038",
            "reason": "CSP header intentionally allows inline scripts for analytics",
            "reviewed_by": "security-team",
            "date": "2026-03-24"
        },
        {
            "tool": "nikto",
            "finding": "OSVDB-3092: /docs/: Directory listing",
            "reason": "Public documentation directory, intentionally open",
            "reviewed_by": "security-team",
            "date": "2026-03-24"
        }
    ]
}

Building a Scan Report

# Vulnerability Scan Report

## Executive Summary
- Target: https://target-app.com
- Scan Date: 2026-03-24
- Tools Used: OWASP ZAP, Nikto, Nuclei
- Critical: 1 | High: 3 | Medium: 5 | Low: 8

## Critical Findings

### 1. SQL Injection in Product Search
- **Severity**: Critical
- **URL**: /api/products?search=
- **Evidence**: Response includes database error when injecting single quote
- **Remediation**: Use parameterized queries
- **CVSS**: 9.8

## Recommendations
1. Implement parameterized queries across all database interactions
2. Add Content-Security-Policy header
3. Enable HTTP Strict Transport Security
4. Remove server version headers

Key Takeaways

  • Automated scanners find known vulnerabilities quickly but miss logic flaws
  • OWASP ZAP is the best free option for web application scanning
  • Nuclei templates let you create custom checks for your specific stack
  • Always triage results -- false positives are common in automated scanning
  • Combine multiple scanners for better coverage

Next, you will move from scanning to actively exploiting vulnerabilities through penetration testing.