Skip to content
Automated Security Testing on a Budget: A Practical Guide to OWASP ZAP for ASP.NET Core

Automated Security Testing on a Budget: A Practical Guide to OWASP ZAP for ASP.NET Core

1 Introduction: The Case for Proactive and Automated Security

Security breaches are headline news. Software architects and senior developers know that a single vulnerability can expose an organization to legal, financial, and reputational damage. Yet in many teams, security remains an afterthought—addressed only after an application is built, or worse, after it’s already running in production.

Why is this? Too often, security is viewed as someone else’s job. Perhaps the security team’s responsibility, or an external consultant’s concern. But in today’s world of rapid development, cloud-native architectures, and CI/CD pipelines, this view is outdated and dangerous.

1.1 Why Security is an Architect’s Responsibility, Not an Afterthought

As a software architect, you define the blueprint of systems. Performance, scalability, maintainability, and user experience are often at the forefront of your mind. Security deserves an equal seat at that table. You are best positioned to ensure that security is embedded from the outset, not layered on afterwards.

Why? Because architectural decisions directly impact the attack surface of an application. Choices about authentication mechanisms, data flow, API exposure, and third-party integrations all create potential openings for threats. By taking a proactive stance, architects can design systems that are resilient against both common and emerging attacks.

1.2 The Shift-Left Movement: Integrating Security into the SDLC

“Shift-left” security means moving security practices as early as possible into the software development lifecycle (SDLC). This involves integrating security testing, reviews, and controls into design, development, and deployment—not just at the end.

Think of shift-left as an investment: catching issues early is cheaper and less disruptive than fixing them after release. For teams working with ASP.NET Core and deploying on Azure, integrating automated security checks in your CI/CD pipeline is both feasible and cost-effective.

1.3 Introducing Dynamic Application Security Testing (DAST): What it is and What it is Not

DAST stands for Dynamic Application Security Testing. Unlike Static Application Security Testing (SAST), which inspects code for vulnerabilities, DAST analyzes your running application from the outside—much like an attacker would.

DAST is black-box testing. It doesn’t know about your codebase; instead, it interacts with your app through HTTP requests, looking for weaknesses such as injection flaws, broken authentication, or cross-site scripting (XSS). It is complementary to SAST, not a replacement.

DAST helps you answer a critical question: “If someone tries to attack my live application, what will they find?”

1.4 Why OWASP ZAP?: The Power of a Free, Open-Source, and Community-Driven Tool

OWASP ZAP (Zed Attack Proxy) is the world’s most popular free DAST tool. Backed by the Open Web Application Security Project (OWASP), ZAP is continuously updated by a global community of security professionals. Unlike many commercial scanners, it costs nothing to get started and has no licensing restrictions for integration into pipelines or cloud builds.

ZAP is designed for automation and extensibility. It works with REST APIs, modern SPAs, and traditional web apps. With features like a programmable API, Docker support, and integration plugins for Azure DevOps, GitHub Actions, and others, it fits naturally into .NET and Azure environments.

1.5 What You Will Achieve with This Guide: From Manual Scans to a Fully Automated DevSecOps Pipeline

This article walks you through:

  • Understanding DAST and the core features of ZAP.
  • Setting up a secure, reproducible test environment for ASP.NET Core.
  • Using ZAP manually to explore your app’s attack surface.
  • Automating security tests in local and CI/CD pipelines.
  • Interpreting ZAP’s reports and remediating common findings.
  • Tips for scaling up and integrating into an Azure-based DevSecOps workflow.

By the end, you’ll have the knowledge to automate dynamic security testing for your .NET Core applications—without blowing the budget.


2 Understanding the Core Concepts: DAST and OWASP ZAP

To use ZAP effectively, it’s essential to understand where DAST fits in the broader security testing landscape and how ZAP works behind the scenes.

2.1 DAST vs. SAST vs. IAST: Clearing up the Alphabet Soup

  • SAST (Static Application Security Testing): Analyzes source code or binaries without executing the program. Great for catching certain bugs and insecure coding practices but can’t see issues in runtime configuration or third-party libraries.
  • DAST (Dynamic Application Security Testing): Interacts with the running application, sending crafted HTTP requests and analyzing responses. Finds issues that occur only during execution, such as injection flaws or misconfigurations.
  • IAST (Interactive Application Security Testing): Runs inside the application (usually via an agent), combining runtime analysis with code knowledge. More advanced but often requires proprietary tooling and has limited open-source options.

ZAP is a DAST tool. It doesn’t access your codebase directly. Instead, it crawls your live application, simulates attacks, and reports what it finds.

2.2 A High-Level Overview of How ZAP Works

2.2.1 The Proxy at its Heart

At its core, ZAP is a man-in-the-middle proxy. It sits between your browser or test client and your application. Every request and response flows through ZAP, allowing it to inspect, modify, and replay traffic.

2.2.2 Spiders (Crawlers) for Discovery

ZAP uses spiders (web crawlers) to explore your application’s pages and APIs. It finds URLs, parameters, forms, and endpoints to build a map of your app.

  • Traditional Spider: Works well for classic websites with clear navigation.
  • Ajax Spider: Handles modern single-page apps by running a headless browser.

2.2.3 Active and Passive Scanning Engines

  • Passive Scanning: As ZAP observes traffic, it looks for signs of weak configurations or missing headers. This is low-risk and safe for production.
  • Active Scanning: ZAP sends crafted requests designed to trigger vulnerabilities, such as SQL injection, XSS, or path traversal. Best for staging or test environments, as it may modify or stress your application.

2.2.4 The Role of Alerts and Reporting

After scanning, ZAP presents findings as alerts—each describing a specific issue, its risk level, and remediation guidance. You can export these as HTML, JSON, or JUnit XML for integration with CI/CD systems.

2.3 Key ZAP Terminology for Architects

2.3.1 Contexts: Defining Your Application’s Boundaries

A context tells ZAP where your application begins and ends. This is crucial for authentication, access control tests, and avoiding accidental attacks on unrelated systems.

2.3.2 Session Management & Authentication

ZAP supports complex login flows. You can record login scripts, configure session cookies, and test with specific users. This allows scanning of authenticated areas, not just public endpoints.

2.3.3 Scan Policies: Tailoring Your Attack

ZAP’s scan policies let you choose which attacks to run, their aggressiveness, and thresholds for reporting. This helps balance thoroughness with scan speed.

2.3.4 The ZAP API: The Key to Automation

Everything you can do in the ZAP desktop app can be done programmatically via its REST API. This is the foundation for scripting, integrating with CI/CD, or running headless in Docker containers.


3 Setting Up Your Local Testing Environment

Before diving into automation, you need a local testbed where you control both the application and ZAP. This section guides you through setting up all prerequisites, running ZAP, and spinning up a sample ASP.NET Core app with intentional vulnerabilities.

3.1 Prerequisites: What You’ll Need

To follow along, make sure you have the following:

  • .NET 8 SDK (latest stable recommended)
  • Visual Studio 2022 or VS Code
  • Docker Desktop (for running ZAP and optionally your app)
  • Git (for source control and sample repo)
  • Basic knowledge of ASP.NET Core, Entity Framework Core, and JWT authentication

3.2 Installing OWASP ZAP

3.2.1 Windows, macOS, and Linux Installation

ZAP offers a cross-platform GUI client and headless CLI options. For local manual testing, the desktop client is convenient.

  • Windows: Download the installer from ZAP’s official site. Run the executable to install.
  • macOS: Use Homebrew (brew install --cask owasp-zap) or download the DMG.
  • Linux: Snap (sudo snap install zaproxy --classic), or use the tarball.

For automation and CI/CD, Docker is preferred. It guarantees a consistent, isolated environment and simplifies updates.

docker pull owasp/zap2docker-stable

To start ZAP in headless mode:

docker run -u zap -p 8080:8080 -i owasp/zap2docker-stable zap.sh -daemon -host 0.0.0.0 -port 8080

ZAP’s API is now accessible at http://localhost:8080.

3.3 Creating a Sample ASP.NET Core Application to Attack

A practical guide needs a practical example. We’ll build a basic ASP.NET Core 8.0 Web API with Entity Framework Core and JWT authentication. We’ll also introduce a few common vulnerabilities for ZAP to discover.

3.3.1 Building a Simple Web API with EF Core and a SQL Database

Let’s scaffold a basic API for a simple “Task Manager” app.

1. Create the Project

dotnet new webapi -n VulnerableApi
cd VulnerableApi

2. Add EF Core and SQL Server

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

3. Define a Simple Model

public class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string? Description { get; set; }
    public bool IsComplete { get; set; }
}

4. Create the DbContext

public class TasksDbContext : DbContext
{
    public TasksDbContext(DbContextOptions<TasksDbContext> options) : base(options) { }

    public DbSet<TaskItem> Tasks => Set<TaskItem>();
}

5. Register DbContext in Program.cs

builder.Services.AddDbContext<TasksDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

6. Add a Simple TasksController

[ApiController]
[Route("api/[controller]")]
public class TasksController : ControllerBase
{
    private readonly TasksDbContext _context;

    public TasksController(TasksDbContext context)
    {
        _context = context;
    }

    [HttpGet]
    public async Task<IActionResult> GetTasks(string? search)
    {
        var query = _context.Tasks.AsQueryable();

        // Intentionally vulnerable to SQL Injection for demonstration
        if (!string.IsNullOrEmpty(search))
        {
            query = _context.Tasks.FromSqlRaw($"SELECT * FROM Tasks WHERE Title LIKE '%{search}%'");
        }

        return Ok(await query.ToListAsync());
    }
}

Note: The FromSqlRaw with direct string interpolation is a deliberate SQL injection vulnerability for demonstration.

3.3.2 Implementing Basic Token-Based Authentication (JWT)

Add JWT support:

1. Add NuGet package

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

2. Configure JWT in Program.cs

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            // For demo: simple validation (not for production)
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("VerySecretSigningKey!123")) // keep this in configuration!
        };
    });

3. Create a simple AuthController

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginModel model)
    {
        // Demo: Accept any username/password, never do this in production
        var token = GenerateJwtToken(model.Username);
        return Ok(new { token });
    }

    private string GenerateJwtToken(string username)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("VerySecretSigningKey!123"));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            claims: new[] { new Claim(ClaimTypes.Name, username) },
            expires: DateTime.Now.AddHours(1),
            signingCredentials: creds);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

public class LoginModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

4. Secure Endpoints

Decorate sensitive endpoints in TasksController with [Authorize] as appropriate.

3.3.3 Intentionally Introducing a Few Common Vulnerabilities for Demonstration

For ZAP to find something, we’ll make our sample app intentionally vulnerable:

  • SQL Injection (as shown above).
  • XSS Placeholder: In a real-world app, this might be in a Razor view. For API, you can imagine an endpoint that echoes user input unsanitized.
[HttpPost("echo")]
public IActionResult Echo([FromBody] string input)
{
    // Vulnerable to XSS if consumed in a frontend or log
    return Ok(new { input });
}
  • Missing Security Headers: By default, ASP.NET Core may not set all recommended headers.

3.3.4 Running the ASP.NET Core App Locally (and via Docker)

Locally:

  1. Add your connection string for SQL Server to appsettings.json. For a quick start, you can use the free SQL Server 2022 Express LocalDB.

  2. Run migrations to create the database:

dotnet ef migrations add InitialCreate
dotnet ef database update
  1. Start the app:
dotnet run

Via Docker:

Create a simple Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "VulnerableApi.dll"]

Build and run:

docker build -t vulnerable-api .
docker run -p 5000:80 vulnerable-api

Now, your sample app is running and ready to be scanned by ZAP.


4 Your First Scan: A Manual Exploration with the ZAP Desktop UI

Automated security testing is most effective when you understand the tool’s capabilities and its view of your application. Starting with manual exploration provides insight into how ZAP discovers vulnerabilities and prepares you to get the most out of automated runs.

4.1 Launching the ZAP UI and Understanding the Layout

Once you’ve installed ZAP (or are running it locally from Docker with X11 forwarding or a Windows GUI), open the application. The interface is logically structured and approachable for both security professionals and newcomers.

Key areas in the ZAP Desktop UI:

  • Menu Bar: Access to file operations, tools, reports, and help.
  • Toolbar: Quick access to frequent tasks such as starting/stopping scans.
  • Sites Tree (Left Panel): As ZAP explores your application, it constructs a hierarchical tree of all discovered URLs, parameters, and endpoints.
  • Request/Response Tabs (Bottom Panel): Detailed views of HTTP requests sent and responses received.
  • Alerts Tab: Where ZAP logs detected vulnerabilities.
  • History Tab: Shows all traffic that passed through ZAP.

Familiarize yourself with this layout. The Sites Tree and Alerts Tab will become your most frequently referenced panels during analysis.

4.2 The “Quick Start” Automated Scan: A First Look

To get started rapidly, use ZAP’s “Quick Start” feature:

  1. Enter your application’s URL (e.g., http://localhost:5000).
  2. Click Attack.

ZAP immediately begins crawling and passively scanning the site. As it discovers pages, it launches active attacks against inputs and parameters, searching for common vulnerabilities.

While this is convenient, it’s important to understand its limitations:

  • The scan is unauthenticated by default (will not see behind login screens).
  • It will only explore what it can reach from the initial entry point.
  • Certain complex flows or SPA navigation may be missed.

Still, this “smoke test” offers a quick gut-check of how exposed your application might be to a casual attacker.

4.3 Manual Exploration: Using Your Browser Through the ZAP Proxy

Automated crawling is powerful, but it cannot match human intuition. Manually browsing your app through ZAP helps ensure all business-critical flows are exercised and logged for scanning.

4.3.1 Configuring Your Browser’s Proxy Settings

To channel browser traffic through ZAP:

  • Set your browser’s HTTP and HTTPS proxy to localhost on port 8080 (ZAP’s default).
  • In Firefox: Preferences > Network Settings > Manual Proxy.
  • In Chrome: Use command-line flags or a browser extension like SwitchyOmega.
  • Accept ZAP’s generated root certificate in your browser to avoid HTTPS warnings (ZAP: Tools > Options > Dynamic SSL Certificates > Export).

Now, all your browsing traffic will be intercepted by ZAP.

4.3.2 Exploring Your Application Manually to Build the Site Tree

Navigate through your application as a typical user might:

  • Login, if authentication exists.
  • Visit every page, submit forms, and use the application’s features.
  • For APIs, use Swagger UI or Postman with ZAP as a proxy.

Watch as the Sites Tree populates with discovered endpoints, routes, and parameters. The more you explore, the more ZAP can analyze.

4.4 Understanding the Spiders

While manual exploration is valuable, ZAP’s built-in spiders (crawlers) allow systematic discovery of URLs and resources.

ZAP’s traditional spider parses HTML for anchor tags, forms, and other navigable elements. It works well for:

  • Classic server-rendered web apps.
  • Sites with clear navigation hierarchies.

Launch it via Spider > Spider Site from the context menu on your root URL in the Sites Tree. You can configure depth limits and constraints as needed.

4.4.2 The AJAX Spider: Handling Modern, JavaScript-Heavy Applications (Crucial for SPAs)

Modern single-page applications (SPAs) load content dynamically via JavaScript. The traditional spider cannot see what isn’t in the initial HTML.

The AJAX spider launches a headless browser (based on Selenium) and interacts with your app as a user would:

  • It clicks buttons, follows navigation, and triggers client-side routing.
  • Essential for React, Angular, or Blazor WASM apps.

To use it, right-click your site in the Sites Tree and choose Attack > AJAX Spider. Monitor its progress in the output panel.

4.5 Running a Manual Active Scan on the Discovered URLs

Once you have a populated Sites Tree, you’re ready for deeper analysis:

  • Select the root node or a specific endpoint.
  • Right-click and choose Attack > Active Scan.

Configure scan policies (we’ll discuss customization later) and launch the scan. ZAP will systematically probe each discovered input point using its suite of attack plugins, including SQL injection, XSS, and more.

Active scans are best run in non-production environments, as they can generate significant load and may alter data.

4.6 Interpreting the Initial Results

4.6.1 Analyzing the “Alerts” Tab: Understanding Risk and Confidence Levels

As ZAP completes its scan, findings appear in the Alerts Tab:

  • Risk Level: Ranges from Informational to High. High-risk alerts demand immediate attention (e.g., successful SQL injection).
  • Confidence Level: Indicates how certain ZAP is that an issue exists (Low, Medium, High, or Confirmed).

Prioritize triage based on risk and confidence. Don’t panic at a long list; not all findings are equally critical.

4.6.2 Diving into a Specific Alert: Request, Response, and Remediation Advice

Click any alert for full details:

  • Description: What the vulnerability is and why it matters.
  • Request/Response: The exact HTTP traffic that triggered the alert.
  • Evidence: The string or pattern found.
  • Remediation: Practical steps to fix or mitigate.

For instance, a SQL Injection finding will show the malicious payload sent and the server’s error response, making it easier for developers to reproduce and fix.

4.6.3 Generating Your First HTML Report

ZAP’s reports are clear and professional—ideal for sharing with your team.

To generate a report:

  • Go to Report > Generate HTML Report.
  • Specify location and filename.

The report summarizes all findings, complete with risk ratings, evidence, and recommendations.


5 Advanced Manual Scanning: Simulating Real-World Scenarios

Manual and “quick” scans provide a solid foundation, but real-world security is rarely that simple. Sensitive endpoints may be behind authentication, and certain routes should be excluded from tests to avoid side effects (for example, logout links). ZAP’s advanced manual features allow you to simulate complex attack scenarios and get results that closely mirror those a skilled attacker might achieve.

5.1 Defining a Context: Why This is The Most Critical Step

In ZAP, a context is a logical grouping that defines the boundaries of your application under test. Think of it as a “scan policy” for a specific application, which includes rules about which URLs are in scope, how authentication works, and what constitutes a user session.

5.1.1 Setting the Scope of the Scan

Set your application’s base URL as the root of the context. ZAP will only scan URLs that match this pattern. This prevents accidental testing of external systems or unintended domains—crucial when working in environments where multiple apps share infrastructure.

How to create a context:

  • Right-click your app’s root node in the Sites Tree.
  • Choose Include in Context > [New Context].
  • Name your context appropriately.

Certain endpoints, such as logout URLs or destructive actions, should be excluded from scans to avoid triggering unwanted side effects.

To exclude:

  • Right-click the endpoint.
  • Choose Exclude from Context > [Your Context].

This fine-grained control is essential for testing safely in shared or pre-production environments.

5.2 Handling Authentication: The Biggest Hurdle in DAST

Most business applications are gated behind authentication. By default, ZAP can only see public endpoints unless you configure it to handle login. Authenticated scans are non-negotiable if you want real coverage.

5.2.1 Why Authenticated Scans are Non-Negotiable

  • Critical vulnerabilities often lurk behind login screens.
  • Authenticated flows test authorization, role segregation, and privilege escalation.
  • Without authentication, your scan results are a partial picture at best.

5.2.2 Configuring Form-Based Authentication for your ASP.NET Core App

Most ASP.NET Core APIs use JWT or cookie-based authentication. ZAP supports both, but requires configuration.

For JWT-based APIs:

  • You can set up a script in ZAP to obtain a token via the login endpoint (/api/auth/login), then inject that token as an Authorization: Bearer ... header for all subsequent requests.

For cookie-based login:

  • Configure ZAP’s context with form-based authentication by specifying the login page, field names, and credentials.

Example Scripted Authentication with JWT:

  1. In ZAP, go to Contexts > Authentication.

  2. Choose “Script-based Authentication.”

  3. Write a ZAP authentication script in JavaScript that:

    • Sends a POST to /api/auth/login with username and password.
    • Extracts the JWT from the response.
    • Adds the Authorization header to future requests.
// Pseudocode for a ZAP authentication script
function authenticate(helper, paramsValues, credentials) {
    var loginMessage = helper.prepareMessage();
    loginMessage.setURI(new org.apache.commons.httpclient.URI("http://localhost:5000/api/auth/login", false));
    loginMessage.setRequestBody(JSON.stringify({
        username: credentials.getParam("Username"),
        password: credentials.getParam("Password")
    }));
    loginMessage.setRequestHeader("Content-Type", "application/json");

    helper.sendAndReceive(loginMessage);

    var jsonResponse = JSON.parse(loginMessage.getResponseBody().toString());
    var jwt = jsonResponse.token;
    // Store JWT for use in subsequent requests
    // Implementation varies by ZAP version
}

Note: See ZAP’s scripting documentation for detailed examples and adapt to your environment.

5.2.3 Creating Users and Roles within ZAP for Authorization Testing

ZAP contexts support multiple users. This allows you to test role-based access:

  • Go to Contexts > Users.
  • Create users with different credentials and (if needed) roles.
  • Assign each user to the context.
  • ZAP can switch between users during scans, helping you test privilege escalation and improper access controls.

5.2.4 Verifying Authentication is Working Correctly

To ensure ZAP is logged in:

  • Use the “Manual Request Editor” to send a request as the configured user.
  • Verify the response is as expected (e.g., 200 OK, not 401 Unauthorized).
  • Check that protected endpoints are visible and tested in the Sites Tree.

If authentication fails, revisit your login script, credential setup, or session management configuration.

5.3 Managing Session Handling: Keeping Your Scan Alive

Session timeouts, token expirations, or CSRF tokens can derail an automated scan. ZAP supports session management to keep the scan authenticated:

  • Cookie-based sessions: ZAP tracks cookies and reuses them for each user.
  • Token refresh: You can script token refresh logic for JWTs and similar schemes.
  • Session expiry detection: ZAP can be configured to detect when it has been logged out and re-authenticate automatically.

These mechanisms ensure your scan doesn’t grind to a halt midway through testing.

5.4 Customizing Scan Policies: From a Quick Scan to a Deep Dive

Not all scans are created equal. Sometimes you need a fast check for critical issues; other times, a thorough pre-release audit is warranted. ZAP’s scan policies let you tune every aspect.

5.4.1 Understanding Thresholds (Low, Medium, High) and Strength (Low, Medium, High, Insane)

  • Threshold controls how sensitive ZAP is to potential issues. At “Low,” ZAP reports anything remotely suspicious; at “High,” only well-evidenced vulnerabilities are flagged.
  • Strength dictates how aggressive the attack is. “Insane” will brute-force and fuzz exhaustively, taking more time.

Tailor these settings to match the environment and the business impact of the scan.

5.4.2 Disabling or Enabling Specific Scan Rules

Every organization has its own risk tolerance. You may wish to disable certain scan rules (for example, those that always produce false positives on your stack) or enable additional plugins relevant to your technology (e.g., for API-specific tests).

  • Go to Scan Policy > Policy Manager.
  • Enable/disable rules as appropriate.
  • Save your custom policy for reuse.

5.4.3 Creating Your Own Scan Policies for Different Environments

  • Quick Smoke Test: Only high-risk, high-confidence plugins with conservative thresholds. Fast, low-noise, ideal for every commit.
  • Full Pre-Release Audit: All plugins enabled, high strength, and lower thresholds for maximum coverage. Run nightly or before production releases.

Switching policies is a matter of seconds in ZAP, and you can specify the policy file for automated scans as well.

5.5 Exploring with the “Heads-Up Display” (HUD): A Developer-Friendly Approach

ZAP’s HUD is a browser overlay that provides instant feedback as you interact with your app. It’s especially useful for developers and QA who want to test security in a hands-on, visual way.

  • Launch your app through ZAP’s HUD (Toolbar > Tools > HUD).
  • As you browse, the HUD injects an overlay with real-time alerts, scan results, and quick attack options.
  • This helps bridge the gap between security and development, fostering a security-first mindset without needing to switch contexts or analyze raw HTTP logs.

The HUD is currently most stable in Firefox, with Chrome support improving. It works seamlessly with both classic web apps and modern SPAs.


6 The Automation Endgame: Scripting ZAP for Headless Operation

Manual testing and exploration have their place, especially when learning or troubleshooting. However, true security maturity arrives when these practices are repeatable, reliable, and invisible. Automation is the cornerstone of modern security engineering—making ZAP not just a tool, but a continuous, programmable guardian.

This section will show you how to run ZAP headlessly (without a UI), trigger it via scripts or CI/CD, interact with its API, and optimize scans for different environments.

6.1 Interacting with the ZAP API: Your Gateway to Automation

The real power of ZAP lies in its fully exposed REST API. Every action available in the UI is accessible through HTTP requests—this means ZAP is as scriptable as you need it to be.

6.1.1 Finding and Using Your ZAP API Key

By default, ZAP protects its API with a key. When running in Docker or headless mode, you can specify the key via the -config parameter, or let ZAP generate one.

To find or set your API key:

  • In the ZAP Desktop UI: Go to Tools > Options > API.
  • When running via Docker: Use -config api.key=YOURKEY as a command argument.

Once ZAP is running, any HTTP call to the API (e.g., http://localhost:8080/JSON/core/view/version/?apikey=YOURKEY) is authenticated and will return JSON results.

6.1.2 Exploring the API via Your Browser

ZAP offers an interactive API UI. With ZAP running, visit:

http://localhost:8080/UI/core/

Here, you can browse every API endpoint, try out commands, and discover parameters for scanning, spidering, authentication, context configuration, and reporting.

This is an excellent place to experiment and prototype scripts before wiring them into your CI/CD.

6.2 Headless ZAP Scans using the Docker Container

Most modern ZAP automation uses the official Docker image. This keeps your environment clean, consistent, and easy to update. You don’t need a desktop or Java installed—just Docker.

To run ZAP headless:

docker run -v $(pwd)/zap-reports:/zap/reports \
  -t owasp/zap2docker-stable zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.key=changeme

This starts ZAP in daemon mode, listening for API commands. You can then control it from scripts, CI pipelines, or other tools—either from your build agent, or a dedicated security test runner.

Alternatively, you can use pre-built scripts (zap-baseline.py, zap-full-scan.py, etc.) for common use cases, which we’ll cover next.

6.3 The Baseline Scan (-T): A Lightweight Scan for CI

6.3.1 What it Checks for and Why It’s Fast

The Baseline Scan is designed for frequent runs—such as on every pull request. It is passive-only: ZAP crawls the target, but does not send active attack payloads.

This means:

  • No risk of corrupting your database or modifying state.
  • No “noisy” or destructive requests—safe for integration environments.
  • Rapid execution (often under 5 minutes for small to mid-sized apps).

It checks for issues like:

  • Missing security headers.
  • Weak cookies.
  • Exposed error messages.
  • Poor TLS configurations.
  • Open directories.

6.3.2 Running a Baseline Scan Against Your Local App from the Command Line

Suppose your app is running at http://host.docker.internal:5000 (the typical way to address host services from Docker on Windows/Mac):

docker run -v $(pwd)/zap-reports:/zap/reports \
  -t owasp/zap2docker-stable zap-baseline.py -t http://host.docker.internal:5000 -r zap-report.html

Parameters:

  • -t – target URL.
  • -r – name of the HTML report file (saved in /zap/reports inside the container).
  • Additional flags let you set alert thresholds, context, authentication, or API definitions.

When finished, examine zap-report.html for findings.

6.4 The Full Scan (-z): A Deeper Dive for Staging or Nightly Builds

6.4.1 Combining Spiders and Active Scans via Command-Line Options

The Full Scan script (zap-full-scan.py) includes everything from the baseline, but also runs active attack plugins—testing for injection, authentication bypasses, XSS, and more.

It’s best for:

  • Staging environments with test data.
  • Nightly or pre-release security audits.
  • Times when you can tolerate aggressive traffic.

Run it similarly:

docker run -v $(pwd)/zap-reports:/zap/reports \
  -t owasp/zap2docker-stable zap-full-scan.py -t http://host.docker.internal:5000 -r full-zap-report.html

You can further tune:

  • Which plugins to enable or disable.
  • Attack strength and thresholds.
  • Timeouts and exclusion patterns.

6.4.2 Passing Context and Authentication Details via Configuration Files

To scan authenticated areas, you need to tell ZAP how to log in. You can do this using context files and environment variables.

Creating a Context File:

  1. Configure your context (authentication, users, exclusions) in the Desktop UI.
  2. Export the context to an XML file (context.context).
  3. Mount the file into your Docker container and pass it with -c /zap/context.context.

Example:

docker run -v $(pwd)/zap-reports:/zap/reports \
  -v $(pwd)/context.context:/zap/context.context \
  -t owasp/zap2docker-stable zap-full-scan.py \
  -t http://host.docker.internal:5000 -c /zap/context.context -r auth-report.html

You can inject user credentials as environment variables, or reference stored users in your context for fully automated authenticated scans.

6.5 The API Scan: Testing Your Headless Services

Web APIs are a core part of ASP.NET Core development. ZAP is equally effective against RESTful endpoints.

6.5.1 Importing an OpenAPI/Swagger Definition

Most modern APIs publish an OpenAPI (Swagger) document (e.g., /swagger/v1/swagger.json). ZAP can import this directly and build its site tree without needing to crawl UI flows.

  • Run ZAP with the zap-api-scan.py script.
  • Pass the URL or path to your OpenAPI definition.

Example:

docker run -v $(pwd)/zap-reports:/zap/reports \
  -t owasp/zap2docker-stable zap-api-scan.py \
  -t http://host.docker.internal:5000 -f openapi \
  -r api-report.html \
  -u http://host.docker.internal:5000/swagger/v1/swagger.json

6.5.2 Running an API-Specific Attack

By importing the definition, ZAP is aware of all endpoints, expected parameters, verbs, and response codes. It can then:

  • Test for typical API issues (e.g., excessive data exposure, insecure direct object references).
  • Probe each route for injection vulnerabilities.
  • Generate an HTML or JSON report focused on your API’s attack surface.

Combined with authentication scripting, this approach enables full-coverage security regression tests for your services.


7 Integrating ZAP into Your Azure DevOps Pipeline

The final—and most impactful—stage of modern security automation is integration with your existing CI/CD platform. For .NET teams, Azure DevOps offers the flexibility, security, and transparency you need for continuous DevSecOps.

7.1 The Architect’s Vision: A Fully Automated DevSecOps Workflow

Imagine this scenario:

  • Every code change triggers a build and deploys your ASP.NET Core app to a temporary test slot.
  • ZAP automatically scans the running app, checking both public and authenticated endpoints.
  • High-risk findings fail the pipeline, ensuring no critical vulnerabilities reach production.
  • Developers receive actionable reports, and work items are created for issues needing remediation.

This is no longer aspirational—it’s best practice.

7.2 Setting Up the Pipeline

7.2.1 Creating a New YAML Pipeline in Azure DevOps

Azure DevOps pipelines are defined as YAML files in your source repository. Here’s a basic scaffold for an ASP.NET Core project:

trigger:
  branches:
    include:
      - main
      - feature/*

pool:
  vmImage: 'ubuntu-latest'

variables:
  buildConfiguration: 'Release'

steps:
- task: UseDotNet@2
  inputs:
    packageType: 'sdk'
    version: '8.x'

- script: dotnet build --configuration $(buildConfiguration)
  displayName: 'Build project'

- script: dotnet publish -c $(buildConfiguration) -o $(Build.ArtifactStagingDirectory)
  displayName: 'Publish project'

- publish: $(Build.ArtifactStagingDirectory)
  artifact: drop

7.2.2 Building and Publishing the ASP.NET Core Application as an Artifact

Artifacts are how you share build outputs between pipeline stages. Once published, your web app can be deployed to a test environment (for example, Azure Web App or a container in Azure Container Instances) where ZAP can scan it.

Add a deployment stage if desired:

- stage: Deploy
  jobs:
    - deployment: DeployWeb
      environment: 'Test'
      strategy:
        runOnce:
          deploy:
            steps:
            - download: current
              artifact: drop
            # Add deployment logic here (e.g., AzureWebApp@1 task)

7.3 Running ZAP as a Pipeline Stage

7.3.1 Using the Official OWASP ZAP Scan Task from the Marketplace

Azure DevOps Marketplace offers an official ZAP scan extension. Install it to your organization.

Once available, add a new pipeline stage with the ZAP scan task:

- task: OWASPZAP@1
  inputs:
    zapDockerImage: 'owasp/zap2docker-stable'
    targetUrl: 'http://$(deployUrl)' # Set this to your test app’s endpoint
    scanType: 'baseline'
    reportType: 'html'
    reportName: 'zap-report.html'
    reportDir: '$(Build.ArtifactStagingDirectory)/zap-reports'

7.3.2 Configuring the Task: Pointing to the Docker Image, Target URL, and Scan Type

Set the zapDockerImage to the latest stable, and targetUrl to your deployed test environment. Choose scanType as baseline for quick checks, or full for deeper scanning on dedicated branches or schedules.

7.3.3 Running a Baseline Scan on Every Pull Request

Integrate the baseline scan into your PR workflow:

  • Fast, passive scanning prevents regressions without slowing delivery.
  • Developers are notified of new or recurring issues before merge.

This pattern “shifts left” security responsibility and fosters a culture of security ownership.

7.4 Implementing a Full Authenticated Scan in a Staging Environment

7.4.1 Creating a Dedicated Stage in Your Pipeline

Full scans are best run in a clean, production-like environment. Add a pipeline stage (e.g., SecurityScan) that only triggers on merges to main or before production deploys.

7.4.2 Securely Storing Credentials using Azure Key Vault and Variable Groups

Never hardcode secrets in YAML. Use Azure Key Vault or pipeline variable groups to securely store and retrieve authentication details:

- task: AzureKeyVault@2
  inputs:
    azureSubscription: 'MyAzureConnection'
    KeyVaultName: 'my-keyvault'
    SecretsFilter: 'zap-user,zap-password'
    RunAsPreJob: true

Reference these secrets in your ZAP context or authentication scripts, ensuring automated, credentialed scans without exposing sensitive information.

7.4.3 Passing the Full Scan Configuration to the ZAP Task

Mount your custom context, user scripts, or environment variables into the ZAP Docker container as part of the task configuration:

- task: OWASPZAP@1
  inputs:
    scanType: 'full'
    contextFile: '$(Pipeline.Workspace)/context.context'
    authenticationScript: '$(Pipeline.Workspace)/login.js'
    zapUser: '$(zap-user)'
    zapPassword: '$(zap-password)'
    reportType: 'html'
    reportName: 'full-auth-zap-report.html'
    reportDir: '$(Build.ArtifactStagingDirectory)/zap-reports'

This orchestrates a robust, fully authenticated scan of your application as deployed to staging.

7.5 Managing Results in Azure DevOps

7.5.1 Publishing the ZAP HTML Report as a Build Artifact

After the scan, publish the resulting HTML (and optionally XML/JSON) reports so that developers, leads, and auditors can access detailed findings:

- publish: $(Build.ArtifactStagingDirectory)/zap-reports
  artifact: zap-reports

7.5.2 Failing the Build Based on Alert Thresholds

ZAP’s scripts and pipeline task can be configured to fail the build if high-risk findings are detected. This keeps your main branch secure by default.

  • In the baseline or full scan command, add --fail-on WARN or --fail-on HIGH to set thresholds.

Example:

- task: OWASPZAP@1
  inputs:
    failOnError: true
    failOnAlertLevel: 'High'

This ensures that critical vulnerabilities are never shipped unnoticed.

7.5.3 (Advanced) Parsing the XML/JSON report to create Work Items for High-Priority Vulnerabilities

Take automation a step further by parsing the ZAP output with custom scripts or Azure DevOps extensions, and automatically creating work items or bugs for each new critical alert:

  • Parse the zap-report.json or zap-report.xml in a script step.
  • Use Azure DevOps REST API to create work items with detailed vulnerability context.

This closes the loop—ensuring every security issue enters your standard backlog and tracking process.


8 Advanced Topics and Best Practices

Once ZAP is running smoothly in your pipelines and workflows, you’ll naturally want to refine your security posture—reducing noise, customizing behavior, and integrating findings into your broader operational processes. This is where ZAP’s extensibility, scripting, and ecosystem integrations pay dividends.

8.1 Fine-Tuning Your Scans: Dealing with False Positives

No automated security scanner is immune to false positives. While ZAP aims to minimize these, you may occasionally see alerts for issues that don’t actually threaten your application (for example, custom error pages mistaken for information leaks, or benign input flagged as potential XSS).

Here’s how to keep noise manageable and results actionable:

  • Review findings critically. Use ZAP’s request and response views to understand the context of each alert. Cross-reference with your application’s expected behavior.
  • Collaborate with developers. Discuss findings with engineers who know the application logic best; sometimes what looks dangerous is handled safely in code.
  • Prioritize by risk and confidence. Start with high-risk, high-confidence issues and work your way down. Not all medium or low alerts need immediate fixes.
  • Document legitimate exceptions. If an alert is truly a false positive, note the reason in your ticketing or tracking system to avoid duplicate work.

Regular tuning over the first few cycles will drastically reduce “alert fatigue” and build trust in automated testing.

8.2 Using Alert Filters and Global Exclusions

ZAP provides flexible mechanisms to automatically suppress or manage alerts that are known to be non-issues for your application.

Alert Filters allow you to:

  • Suppress certain alert types (e.g., “Missing X-Frame-Options Header”) for specific URLs or contexts.
  • Change the risk level of certain findings (e.g., downgrade an alert from “High” to “Medium” for a non-critical test endpoint).

To configure:

  • In the ZAP UI: Go to Tools > Options > Alert Filters.
  • In automation/pipeline: Use the -config option or reference an alert filters file.

Global Exclusions let you skip entire URLs, patterns, or file types (such as static assets, documentation routes, or known test hooks) from scans. This speeds up scans and reduces irrelevant noise.

A well-maintained set of filters and exclusions ensures your reports stay relevant and actionable.

8.3 The Power of ZAP Scripts: Customizing ZAP’s Behavior with JavaScript

One of ZAP’s distinguishing features is its powerful scripting capability. Using JavaScript (or other supported languages like Python or Groovy), you can:

  • Automate authentication flows: Create login sequences that handle multi-factor, OAuth2, or custom token exchange patterns.
  • Customize requests: Inject or modify headers, cookies, or payloads based on your application’s logic or test scenarios.
  • Define passive or active scan rules: Write scripts to look for vulnerabilities specific to your tech stack or business logic.
  • Handle complex session management: Automatically refresh tokens, rotate credentials, or respond to anti-CSRF challenges.

Example: Automatically injecting a bearer token

Suppose your API requires a dynamically generated bearer token per user. A ZAP “HTTP Sender” script can intercept all outgoing requests and insert the correct token, pulled from your login logic:

// Simple ZAP HTTP Sender script
function sendingRequest(msg, initiator, helper) {
    var token = 'your-token-fetching-logic-here';
    msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + token);
}

You can manage scripts via the ZAP UI (Scripts tab), or reference them in automation through mounted files and configuration.

Scripting empowers you to bridge the gap between generic DAST and your unique security context.

8.4 Integrating with Other Systems (e.g., sending alerts to Slack or Teams)

ZAP is not just a scanner; it can become a first-class citizen in your broader incident and alerting ecosystem.

Practical integrations include:

  • Slack or Microsoft Teams: Use a post-processing script or pipeline task to parse the ZAP report and send notifications for new high-risk vulnerabilities.
  • SIEM/SOC platforms: Export reports in JSON or XML for ingestion into tools like Splunk, Sentinel, or ELK stacks, enabling centralized monitoring.
  • Bug/issue trackers: Automatically create issues in Azure DevOps, Jira, or GitHub for every verified critical finding.
  • Dashboards: Visualize trends in security posture over time, helping leadership track improvements and justify investments.

Example: Sending alerts to Slack

After your scan runs, a simple shell script or PowerShell task can parse the report and send a message via Slack’s webhook API. For advanced needs, you could trigger a Logic App, Power Automate flow, or custom webhook endpoint.

This approach ensures that security findings are not only captured but acted upon—closing the feedback loop between testing and remediation.

8.5 Keeping ZAP and Scan Rules Up-to-Date

Security is a moving target. New vulnerabilities emerge regularly, and the effectiveness of your automated testing depends on staying current.

  • Update ZAP Docker images regularly. The official ZAP container is updated frequently. Reference owasp/zap2docker-stable:latest or a specific version tag in your pipelines and update on a regular schedule.
  • Enable auto-updates for add-ons. ZAP add-ons (scan rules, parsers, scripts) are versioned and upgradable from the ZAP Marketplace.
  • Follow OWASP and ZAP project channels. Subscribe to release notes, security advisories, and mailing lists.
  • Review new scan rules and plugins. Emerging threats (such as new API abuse techniques or SSRF vectors) are often addressed quickly in new or updated scan rules.

Setting a quarterly review cadence for your security tooling (including ZAP and its rules) is a practical way to ensure your scans remain relevant, comprehensive, and reliable.


9 Conclusion: A New Baseline for Secure Development

The road to automated security testing can seem daunting at first, especially for teams accustomed to ad-hoc or periodic manual reviews. Yet, as this guide has shown, bringing DAST into the heart of your ASP.NET Core and Azure DevOps lifecycle is both achievable and transformational.

9.1 Recap: From Manual Chaos to Automated Confidence

Let’s reflect on the journey:

  • You learned to set up and explore ZAP manually, gaining insights into your app’s real attack surface.
  • You mastered authenticated, context-aware scanning, ensuring that critical business logic—not just public endpoints—gets tested.
  • You scripted ZAP for headless operation and embedded it into your build pipelines, making security a consistent, automated gate in your SDLC.
  • You adopted advanced practices: filtering false positives, customizing behavior with scripts, integrating with your collaboration tools, and staying current as threats evolve.

Each of these steps replaces ad-hoc, fire-fighting chaos with calm, automated confidence.

9.2 The True ROI: Catching Vulnerabilities Early and Reducing Risk on a Budget

The tangible benefits of integrating ZAP into your pipeline aren’t just technical—they’re business-critical:

  • Reduced risk exposure. Vulnerabilities are caught early, before they become costly incidents.
  • Developer empowerment. Security is democratized, shifting responsibility left and engaging every engineer in risk reduction.
  • Cost savings. Free and open source, ZAP delivers value without the overhead of commercial DAST solutions.
  • Auditability and compliance. Automated, reproducible scans support security audits and regulatory needs.

For many organizations, this shift marks a new baseline for software assurance—one that’s sustainable and defensible.

9.3 Your Next Steps: Expanding Security Testing Across Your Organization

Ready to go further?

  • Broaden coverage: Extend scanning to all web properties, public APIs, and critical business systems.
  • Upskill your teams: Train developers, QA, and ops staff to read and act on ZAP findings.
  • Pair DAST with SAST and other security layers: Combine ZAP with static code analysis, container security, and infrastructure scanning for end-to-end coverage.
  • Measure and improve: Use ZAP reports and integrations to track trends, celebrate risk reductions, and target investment where needed.

Security is not a product or a tool—it’s a journey of continuous improvement. By embedding OWASP ZAP into your DevSecOps toolchain, you’re laying a foundation for secure, agile, and resilient software—at a price every organization can afford.

Advertisement