Deployment guide

OWASP ZAP

Videos

Overview: OWASP ZAP on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: OWASP ZAP on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: OWASP ZAP on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

OWASP ZAP (Zed Attack Proxy) is the world's most widely used open-source web application security scanner. It performs Dynamic Application Security Testing (DAST): it spiders a target web application, proxies the traffic, and runs passive and active scan rules to find vulnerabilities such as missing security headers, injection points and information leaks.

The SecureAnyCloud image runs ZAP 2.17.0 in headless daemon mode as an appliance. A long-running ZAP daemon exposes its REST API and a local scanning proxy, and you drive it through the API or the ZAP Automation Framework. The daemon runs as a dedicated zap system user, stores its sessions and scan results on a dedicated Azure data disk, and rotates a unique API key into the image on first boot.

What you get:

  • OWASP ZAP 2.17.0 (the official Linux distribution from the ZAP project) in -daemon mode
  • A Temurin 17 JRE, the Java runtime that ZAP 2.17 requires
  • A unique per-VM API key generated on first boot, with no shared default credential
  • ZAP home, sessions, scan results and add-ons on a dedicated 20 GiB Azure data disk at /var/lib/zap
  • A loopback-only ZAP API and proxy on 127.0.0.1:8090, fronted by nginx on :80
  • A static, unauthenticated /health endpoint for load balancers and probes
  • 24/7 SecureAnyCloud support

This image is a scanner appliance. The ZAP REST API and scanning proxy listen on 127.0.0.1:8090 only, and the per-VM API key gates them. Port 8090 is not opened to the internet. Call the API locally on the VM or over an SSH tunnel.

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2ms (2 vCPU / 8 GiB RAM) is a good starting size. ZAP is a Java application and benefits from the extra memory.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network. You do not need to open any inbound application ports, because you reach the ZAP API over the SSH tunnel.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OWASP ZAP by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) only.

Select Review + create, then Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:owasp-zap-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name owasp-zap \
  --image sac1647283583153:owasp-zap-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

First boot takes a few minutes. On first boot, the VM finishes its per-VM setup in the background by generating its own credentials and starting the services. If you connect as soon as the VM shows as running, wait for that setup to complete before you continue. Run sudo cloud-init status --wait in bash.

The command prints status: done when the VM is ready. The credentials and services described below exist only after that.

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm ZAP is installed and running

The ZAP daemon (zap.service) and the nginx front end (nginx.service) start automatically on boot. Confirm that both services are active:

sudo systemctl is-active zap.service nginx.service

Both services should report active. The ZAP daemon reports version 2.17.0 over its REST API, as Step 6 shows.

Step 5 - Retrieve your per-VM API key

On first boot, the image generates a unique ZAP API key for this VM. It writes the key, with the API URL and a security note, to /root/owasp-zap-credentials.txt (mode 0600, root only). Read the file:

sudo cat /root/owasp-zap-credentials.txt

The API key on the zap.api.key= line is the credential that gates every ZAP API call. Store it somewhere safe. For the commands that follow, export it into your shell:

ZAP_API_KEY=$(sudo grep '^zap.api.key=' /root/owasp-zap-credentials.txt | cut -d= -f2-)

Step 6 - Call the ZAP API with your API key

The ZAP REST API requires the API key, passed either as the apikey query parameter or in the X-ZAP-API-Key header. A correct key returns the data, and a wrong or missing key is rejected. Call the API with the query parameter:

curl -s "http://127.0.0.1:8090/JSON/core/view/version/?apikey=$ZAP_API_KEY"

The response is JSON containing "version":"2.17.0". The correct key returns HTTP 200 plus the version, and a wrong key is rejected.

You can also pass the key in the header:

curl -s -H "X-ZAP-API-Key: $ZAP_API_KEY" "http://127.0.0.1:8090/JSON/core/view/version/"

Step 7 - Run an automated scan via the API

ZAP scans a target web application in two stages. A spider crawls the application and discovers URLs, and the passive scanner analyses every request and response that the spider made.

The example below starts a small local page and scans it end to end so that you can see the workflow. In practice, you set url= to your own application.

Start the local target, then spider it through ZAP:

TARGET="http://127.0.0.1:8099"
D=$(mktemp -d); printf '%s' '<!doctype html><html><head><title>scan target</title></head><body><h1>target</h1><a href="/page2.html">page 2</a></body></html>' > "$D/index.html"; printf '%s' '<!doctype html><html><body><h1>page two</h1></body></html>' > "$D/page2.html" setsid bash -c "cd '$D' && exec python3 -m http.server 8099 --bind 127.0.0.1 >/tmp/zap-target.log 2>&1" </dev/null >/dev/null 2>&1 & disown 2>/dev/null || true sleep 2 curl -s "http://127.0.0.1:8090/JSON/spider/action/scan/?apikey=$ZAP_API_KEY&url=${TARGET}/&recurse=true"

The spider returns a scan id ({"scan":"0"}). Wait for the scan to reach 100%, then read the URLs that it discovered:

SID=$(curl -s "http://127.0.0.1:8090/JSON/spider/view/scans/?apikey=$ZAP_API_KEY" | jq -r '.scans[-1].id') for i in $(seq 1 40); do
P=$(curl -s "http://127.0.0.1:8090/JSON/spider/view/status/?apikey=$ZAP_API_KEY&scanId=${SID}" | jq -r '.status'); [ "$P" = 100 ] && break; sleep 2; done curl -s "http://127.0.0.1:8090/JSON/spider/view/results/?apikey=$ZAP_API_KEY&scanId=${SID}" | jq '.results'

The results list the discovered URLs. ZAP also adds the site to its scan tree and records the HTTP messages.

Step 8 - Read the scan findings (alerts)

The passive scanner raises alerts for the issues that it finds. Wait for the passive scan queue to drain, then read the alert summary and the individual findings for your target:

for i in $(seq 1 30); do
Q=$(curl -s "http://127.0.0.1:8090/JSON/pscan/view/recordsToScan/?apikey=$ZAP_API_KEY" | jq -r '.recordsToScan'); [ "$Q" = 0 ] && break; sleep 2; done curl -s "http://127.0.0.1:8090/JSON/alert/view/alertsSummary/?apikey=$ZAP_API_KEY&baseurl=http://127.0.0.1:8099" | jq . curl -s "http://127.0.0.1:8090/JSON/alert/view/alerts/?apikey=$ZAP_API_KEY&baseurl=http://127.0.0.1:8099&start=0&count=6" | jq '[.alerts[] | {alert:.alert, risk:.risk, url:.url}]'

The output shows the alert summary by risk and a sample of the findings, for example a missing Content Security Policy header and a server version leak.

When you finish, stop the local target:

pkill -f 'http.server 8099' 2>/dev/null || true

Step 9 - Reach the API from your workstation (SSH tunnel)

The ZAP API and proxy listen on loopback only and are not exposed publicly. To drive ZAP from your workstation, or to point a browser at the ZAP API UI at http://127.0.0.1:8090/, open an SSH tunnel and call the API locally:

ssh -L 8090:127.0.0.1:8090 azureuser@<vm-public-ip>
# then on your workstation:
curl -s "http://127.0.0.1:8090/JSON/core/view/version/?apikey=<your-api-key>"

To use ZAP as an HTTP proxy for a browser or a CI job, tunnel the same port and set your client's HTTP proxy to 127.0.0.1:8090. If you expose 8090 (or the nginx :80) to other hosts, put your own authentication and TLS in front of it. The ZAP API key alone does not replace transport security.

Through the nginx :80 front end, a request with a missing or wrong API key returns 502 Bad Gateway, not 401, because ZAP closes the connection instead of answering. Send the key in the X-ZAP-API-Key header and the same request returns 200.

Step 10 - Health check

nginx serves a static, unauthenticated health endpoint on port 80. It does not depend on the ZAP daemon being fully warmed up, so you can use it for Azure Load Balancer or Application Gateway probes:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/health

The endpoint returns 200.

Data persistence

The ZAP home directory (-dir /var/lib/zap) holds the session databases, scan results, contexts and add-ons. It lives on a dedicated 20 GiB Azure data disk mounted at /var/lib/zap, which is captured into the image and re-provisioned on every VM. Your sessions and results survive restarts, and you can resize the volume independently.

The image ships with a clean ZAP home and a provisional key. First boot generates the unique API key for your VM and an empty home.

Security model

  • The ZAP REST API and scanning proxy bind to 127.0.0.1:8090 only, and the Azure NSG never exposes them.
  • The per-VM API key in /root/owasp-zap-credentials.txt gates every API call.
  • nginx on :80 reverse-proxies the API and serves the static /health endpoint.
  • Reach the API remotely over an SSH tunnel. Add your own authentication and TLS before you expose any port publicly.
  • The OS receives unattended security updates.

Support

SecureAnyCloud maintains this image and provides 24/7 support. OWASP, ZAP and Zed Attack Proxy are trademarks of the OWASP Foundation. This image repackages the upstream Apache-2.0 software and is not affiliated with or endorsed by the OWASP Foundation.

Deploy on Azure

Launch OWASP ZAP on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure