/zap-maven-plugin

OWASP Zed Attack Proxy (ZAP) Maven plugin

Primary LanguageJavaApache License 2.0Apache-2.0

ZAP Maven Plugin

Build Status Maven Central

Check out the ZAP SonarQube Plugin

This plugin makes it easier to integrate OWASP Zed Attack Proxy (ZAP) security tests with the application development and build process for Maven users. With this plugin, you can:

  • Run ZAP analysis during the build of your application;
  • Run authenticated analysis on CAS and on many other applications with complex authentication strategies;
  • Use your Selenium integration tests navigation to feed ZAP;
  • Easily run ZAP analysis during development.

Contents

Usage

Generally, the plugin configuration will follow the format below:

<plugin>
    <groupId>br.com.softplan.security.zap</groupId>
    <artifactId>zap-maven-plugin</artifactId>
    <version>${zap-maven-plugin.version}</version>
    <configuration>
        <!-- Configuration parameters -->
    </configuration>
    <executions>
        <execution>
            <phase>verify</phase>
            <goals><goal>analyze</goal></goals>
        </execution>
    </executions>
</plugin>

If you want to bind the plugin execution to the build lifecycle, it is necessary to define the phase where the plugin will be executed, as well as the goal that will be executed. Optionally, the plugin can be executed by directly calling the desired goal:

mvn br.com.softplan.security.zap:zap-maven-plugin:analyze

The main goal provided is analyze, responsible to execute a ZAP analysis according to the configuration parameters. However, the plugin also provides other goals for more specific situations. The list of available goals is presented bellow:

  • analyze: performs a complete analysis running (by default) the Spider before the Active Scan and starting ZAP automatically if necessary (and closing it after the analysis).
  • startZap: simply starts ZAP (via local installation or Docker).
  • seleniumAnalyze: assumes ZAP is already executing and simply runs the Active Scan, closing ZAP after the analysis. This goal is useful when there are Selenium integration tests that are executed with a proxy to ZAP and the navigation done by the tests should be used instead of the Spider. More on that at Selenium Integration.

The goals that run analysis save the generated reports in the end of the plugin execution. By default, the reports are saved in the directory target/zap-reports within the project. The parameter reportPath can be used to specify another directory (absolute or relative).

Configuration Parameters

Common parameters:

Parameter Description Required? Default
skip Skip the plugin execution (equivalent CLI property: zap.skip) No false

Analysis parameters:

Parameter Description Required? Default
targetUrl URL of the application that will be scanned Yes -
spiderStartingPointUrl Starting point URL for the Spider (and AJAX Spider, in case it runs) No targetUrl
activeScanStartingPointUrl Starting point URL for the Active Scan No targetUrl
context The URLs to be set on ZAP's context (absolute or relative) No targetUrl
analysisTimeoutInMinutes Analysis timeout in minutes No 480
shouldRunAjaxSpider Indicates whether ZAP should execute the AJAX Spider after the default Spider (it can improve the scan on applications that rely on AJAX) No false
shouldRunPassiveScanOnly In case it's true, the Active Scan will not be executed No false
shouldStartNewSession Indicates whether a new session should be started on ZAP before the analysis No true

If the options spiderStartingPoint, activeScanStartingPoint and context are all provided, targetUrl will be ignored. These options are useful when you want to spider through the whole application, but want to run the Active Scan for only a portion of it, for instance. These parameters are very sensitive and should be set accordingly.

ZAP related parameters:

Parameter Description Required? Default
zapPort Port where ZAP is running or will run Yes -
zapHost Host where ZAP is running No localhost
zapApiKey API key needed to access ZAP's API, in case it's enabled No -
zapPath Absolute path where ZAP is installed, used to automatically start ZAP No -
zapJvmOptions JVM options used to launch ZAP No -Xmx512m
shouldRunWithDocker Indicates whether ZAP should be automatically started with Docker No false
zapOptions Options that will be used to automatically start ZAP No See bellow
initializationTimeoutInMillis ZAP's automatic initialization timeout in milliseconds No 120000
reportPath Absolute or relative path where the generated reports will be saved No ${project.build.directory}/zap-reports

To automatically start ZAP, it must be installed locally and the option zapPath must be provided. If the installation folder contains more than one Jar, zapPath should point to the core Jar file. To start ZAP with Docker, Docker must be locally installed and the option shouldRunWithDocker must be passed as true. In both cases, by default, ZAP is initialized with the following options:

-daemon -config api.disablekey=true -config api.incerrordetails=true -config proxy.ip=0.0.0.0 -port ${zapPort}

These options make ZAP start without a GUI, with its API key disabled, able to report errors details via API, and able to be accessed remotely. Besides that, it makes sure ZAP will run on the port specified by the port option. These options can be overridden by the zapOptions parameter.

Authentication parameters:

Parameter Description Required?
authenticationType Define the authentication type: 'http', 'form', 'cas' or 'selenium' Yes, for authenticated analysis
loginUrl Login page URL Yes, for authenticated analysis
username Username used in the authentication Yes, for authenticated analysis
password Password used in the authentication Yes, for authenticated analysis
extraPostData Used to define any extra parameters that must be passed in the authentication request (e.g. domain=someDomain&param=value) No
loggedInRegex Regex that identifies a pattern in authenticated responses (needed to allow re-authentication) No
loggedOutRegex Regex that identifies a pattern in non-authenticated responses (needed to allow re-authentication) No
excludeFromScan Define the URLs regexs that will be excluded from the scan No

HTTP authentication parameters:

Parameter Description Required? Default
hostname The host name of the server where the authentication is done Yes -
realm The realm the credentials apply to Yes -
port The port of the server where the authentication is done No 80

CAS authentication parameter:

Parameter Description Required?
protectedPages Define the URL of a protected page of the application that will be scanned Yes, if authenticationType is CAS

As it was stated, the option protectedPage should have as value the URL of a protected page of the application that will be scanned. For CAS authentication, the login is done directly at the CAS server. Thus, in the first access to the application there will be a redirect to the server, that ends up redirecting the user back to the protected page, since the user is already authenticated. ZAP doesn't support this circular redirect, and because of that the application needs to be accessed at least once before the scan is started. This option defines the URL of a protected page that will be accessed after the authentication and before the scan to make sure the circular redirect won't happen during ZAP's analysis.

Form and Selenium authentication parameters:

Parameter Description Required? Default
usernameParameter Name of the request parameter that holds the username No username
passwordParameter Name of the request parameter that holds the password No password

Selenium authentication parameters:

Parameter Description Required? Default
httpSessionTokens Any additional session tokens that should be added to ZAP prior authentication No -
seleniumDriver The web driver that will be used to perform authentication: 'htmlunit', 'firefox', or 'phantomjs' No firefox
  • It's important to realize that the HtmlUnitDriver lacks complete support for JavaScript. Therefore, it might not always work properly. If a GUI is not available, prefer to use PhantomJS.
  • When using PhantomJS, make sure its executable is in the system's path.

Notice that the parameters context, excludeFromScan, protectedPages and httpSessionTokens accept multiple values, like in the example below:

<excludeFromScan>
    <!-- It doesn't matter how you name the inner tag -->
    <exclude1>http://myapp/logout</exclude1>
    <exclude2>http://myapp/forbidden</exclude2>
</excludeFromScan>
<httpSessionTokens>
    <!-- And even for only one item, the inner tag is necessary -->
    <token>LtpaToken2</token>
</httpSessionTokens>

Authentication Strategies

There are three ways to perform authenticated scans with the ZAP Maven Plugin. The first and most simple one is the HTTP authentication. According to ZAP, three authentication schemes are supported: Basic, Digest, and NTLM, and reauthentication is possible. The hostname, realm and (optional) port parameters are passed to ZAP, which handles the authentication process.

The second is the form based authentication. This should be used for very simple form authentications (like the one found in the bodgeit application), where all you need to authenticate is a simple POST request. This strategy uses ZAP's form authentication mechanism, thus reauthentication is possible (through loggedIn and loggedOutRegex parameters).

It's also possible to run authenticated scans on applications that use CAS. This strategy uses ZAP's script authentication mechanism with a script to perform the CAS authentication. It might not work for all possible CAS configurations (there are many), and, as with the form authentication, reauthentication is possible.

The last strategy uses Selenium. The idea is to perform the authentication via Selenium, and pass to ZAP the session created (i.e. the session cookie). This should work in most situations, including more complex form based authentications. However, reauthentication is not possible, so make sure you use the excludeFromScan parameter to exclude the logout URL and avoid logging out during the scan.

Examples

Using a running instance of ZAP

For this to work, ZAP must already be running.

<plugin>
    <groupId>br.com.softplan.security.zap</groupId>
    <artifactId>zap-maven-plugin</artifactId>
    <version>${zap-maven-plugin.version}</version>
    <configuration>
        <!-- If ZAP is running on localhost, there is no need to define zapHost -->
        <zapHost>10.10.3.4</zapHost>
        <zapPort>8080</zapPort>
        <targetUrl>http://localhost:8090/testwebapp</targetUrl>
    </configuration>
</plugin>

Starting ZAP automatically

For ZAP to be automatically started, the option zapPath must be provided with the directory where ZAP is installed.

<plugin>
	<groupId>br.com.softplan.security.zap</groupId>
	<artifactId>zap-maven-plugin</artifactId>
	<version>${zap-maven-plugin.version}</version>
	<configuration>
		<zapPort>8080</zapPort>
		<targetUrl>http://localhost:8090/testwebapp</targetUrl>
		<zapPath>C:\Program Files (x86)\OWASP\Zed Attack Proxy</zapPath>
	</configuration>
</plugin>

There is no need to inform the host in this case, since it will obviously be localhost.

Starting ZAP with Docker

If ZAP is not installed, you can still start ZAP with Docker. For this, Docker must be installed and the option shouldRunWithDocker must be provided with value true.

<plugin>
	<groupId>br.com.softplan.security.zap</groupId>
	<artifactId>zap-maven-plugin</artifactId>
	<version>${zap-maven-plugin.version}</version>
	<configuration>
		<zapPort>8080</zapPort>
		<targetUrl>http://localhost:8090/testwebapp</targetUrl>
		<shouldRunWithDocker>true</shouldRunWithDocker>
	</configuration>
</plugin>

Form authentication example

<plugin>
	<groupId>br.com.softplan.security.zap</groupId>
	<artifactId>zap-maven-plugin</artifactId>
	<version>${zap-maven-plugin.version}</version>
	<configuration>
    	<zapPort>8080</zapPort>
		<targetUrl>http://localhost:8180/bodgeit</targetUrl>

		<authenticationType>form</authenticationType>
		<username>user</username>
		<password>pass</password>
		<loginUrl>http://localhost:8180/bodgeit/login.jsp</loginUrl>
		<loggedInRegex><![CDATA[\\Q<a href=\"logout.jsp\">Logout</a>\\E]]></loggedInRegex>
	</configuration>
</plugin>

Notice that it might be necessary to use the tag <![CDATA[]]> so the characters used within the parameter value are not parsed as part of the XML.

CAS authentication example

<plugin>
	<groupId>br.com.softplan.security.zap</groupId>
	<artifactId>zap-maven-plugin</artifactId>
	<version>${zap-maven-plugin.version}</version>
	<configuration>
		<zapPort>8080</zapPort>
		<targetUrl>https://localhost:8443/myapp</targetUrl>

        <authenticationType>cas</authenticationType>
        <username>user</username>
        <password>pass</password>
        <loginUrl>https://localhost:8443/cas-server/login</loginUrl>
        <protectedPages>
            <protectedPage>https://localhost:8443/myapp/protected/index</protectedPage>
        </protectedPages>
        <loggedOutRegex><![CDATA[\\QLocation: https://localhost:8443/cas-server/\\E.*]]></loggedOutRegex>
	</configuration>
</plugin>

A good way to achieve re-authentication with CAS is defining the loggedOutRegex with a value like \QLocation: https://your.domain/your-cas-server\E.*. Unauthenticated responses will be redirects to the CAS server, so this is the easiest way to identify that there was a redirection to the CAS server and thus the user is not logged in.

Selenium authentication example

<plugin>
	<groupId>br.com.softplan.security.zap</groupId>
	<artifactId>zap-maven-plugin</artifactId>
	<version>${zap-maven-plugin.version}</version>
	<configuration>
		<zapPort>8080</zapPort>
		<targetUrl>http://localhost:8090/testwebapp</targetUrl>

        <authenticationType>selenium</authenticationType>
        <username>user</username>
        <password>pass</password>
        <loginUrl>http://localhost:8090/testwebapp/login</loginUrl>
        <httpSessionTokens>
            <token>LtpaToken2</token>
        </httpSessionTokens>
        <seleniumDriver>phantomjs</seleniumDriver>
	</configuration>
</plugin>

Selenium Integration

If your application has Selenium integration tests that navigate through the application, it might be interesting to feed ZAP with the visited pages instead of relying on ZAP's Spider. The Spider can't ensure a complete navigation through the application. Besides that, by feeding ZAP with the visited pages, it's possible to easily define the analysis scope, since ZAP's tests will only be executed on the pages that were visited during the tests.

With that in mind, the goals startZap and seleniumAnalyze were developed. With them, it's possible to start ZAP before the integration tests and execute the analysis after the tests, using the navigation done by the tests.

The first step to feed ZAP with your integration tests navigation is to ensure the tests use ZAP as a proxy. This way, all requests made during the tests execution go through ZAP. The configuration needed for the most common drivers are presented bellow:

    WebDriver driver;

    // HtmlUnit
    driver = new HtmlUnitDriver();
	((HtmlUnitDriver) driver).setProxy(ZAP_HOST, ZAP_PORT);
	
	// Firefox
	FirefoxProfile profile = new FirefoxProfile();
	profile.setPreference("network.proxy.type", 1); // 1 = 'Manual proxy configuration'
	profile.setPreference("network.proxy.share_proxy_settings", true);
	profile.setPreference("network.proxy.no_proxies_on", "");
    profile.setPreference("network.proxy.http", ZAP_HOST);
    profile.setPreference("network.proxy.http_port", ZAP_PORT);
    driver = new FirefoxDriver(profile);
	
	// Chrome
	Proxy proxy = new Proxy(); // org.openqa.selenium.Proxy
	proxy.setHttpProxy(ZAP_HOST + ":" + ZAP_PORT);
	DesiredCapabilities capabilities = DesiredCapabilities.chrome();
	capabilities.setCapability("proxy", proxy);
	driver = new ChromeDriver(capabilities);

With that done, all that remains are the plugin goals configuration:

<plugin>
    <groupId>br.com.softplan.security.zap</groupId>
    <artifactId>zap-maven-plugin</artifactId>
    <version>${zap-maven-plugin.version}</version>
    <configuration>
		<!-- whatever configuration -->
	</configuration>
	<executions>
		<execution>
			<id>start-zap</id>
			<phase>pre-integration-test</phase>
			<goals><goal>startZap</goal></goals>
		</execution>
		<execution>
			<id>selenium-analyze</id>
			<phase>post-integration-test</phase>
			<goals><goal>seleniumAnalyze</goal></goals>
		</execution>
	</executions>
</plugin>

This will ensure that ZAP is started before the integration tests, so the tests can be proxied through it. Also, with that configuration ZAP will only execute the analysis after the tests execution.