Automated security tests with OWASP ZAP

Primarily, if we can integrate Selenium Webdriver tests with ZAP then we can have the automated security tests ready through ZAP APIs. In spite of good documentation around this topic, I have seen a lot of people face issues in integrating tests with ZAP. This blog is showing the practical steps to have this integration in place using ZAP APIs.

Note – The following content will not cover the OWASP ZAP features, types of ZAP security scans, ZAP internal usage and reading the scan reports. Fortunately, there is very good documentation around all the features of ZAP here. Please go through it.

Let’s begin with the actual integration.

The foremost step is to initiate the ZAP executable. The selenium test will be communicating to the ZAP executable so this has to be initiated on the configured machine. ZAP executable supports various command line parameters, but here we will be using the bare minimum.

zap.sh -daemon -host some-host -port some-port -config api.addrs.addr.regex=true -config api.disablekey=true


zap.sh — a startup script provided by ZAP

-daemon – Start in a headless configuration

-host, -port – The ZAP host and port where selenium tests will eventually listen

-config api.addrs.addr.regex=true – Allow any source IP to connect

-config api.disablekey=true – by default it is false.

Once ZAP executable is started, the next action is to configure the selenium driver and add the ZAP APIs library to your selenium framework project and if framework project uses build tool like maven then add the dependencies for 1. OWASP ZAP API client and 2. OWASP Zed Attack Proxy.

Idea is to initiate the selenium driver using the ZAP proxy address which we initiated above. The example code snippet is as follows – (for reference using chrome driver)


//setting up chrome options
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--ignore- certificate-errors");
//set the proxy to use ZAP host and port
String proxyAddress = ”ZAP_machine_IP:ZAP_machine_PORT";
Proxy zap_proxy = new Proxy(); zap_proxy.setHttpProxy(proxyAddress).setSslProxy(proxyAddress);

//set the desired capabilities to use zap_proxy object.
DesiredCapabilities capabilities = DesiredCapabilities.chrome();   capabilities.setCapability(CapabilityType.PROXY, zap_proxy);   capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);   capabilities.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS,true);   capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);
ChromeDriverService service = new ChromeDriverService.Builder() .usingAnyFreePort() .usingDriverExecutable(new File(<chromedriver executable path)) .build(); service.start();
// initiate the driver with required chrome options and capabilities.
Webdriver driver = new  ChromeDriver(service, options);

If tests need to be executed over the remote machine, the use RemoteWebDriver with appropriate capabilities. Just make sure you are referring to the correct proxy address of ZAP. Having this been done, when the selenium tests execute, in the background ZAP will create a scan tree of navigated application pages through selenium test which is called a passive scan. ZAP uses this scan tree to perform other scans like active scan, spider attack and so on. ZAP generally takes some time to complete the passive scan, so in the code, you must wait for the passive scan to complete once your all tests execution complete. The example code snippet is as follows –

{
// create an object of org.zaproxy.clientapi.core.ClientApi using ZAP host and port.   
private static ClientApi api = new ClientApi(ZAP_HOST, ZAP_PORT);
// function to wait for passive scan to complete
private static void waitForPassiveScanToComplete() {
System.out.println("--- Waiting for passive scan to complete --- ");
     try{
       api.pscan.enableAllScanners();  // enable passive scanner.
       ApiResponse response = api.pscan.recordsToScan();
       // getting a response //iterating till we get response as "0".
       while(!response.toString().equals("0")) { response = api.pscan.recordsToScan(); }
       }
     catch (ClientApiException e1){e1.printStackTrace();
       }
System.out.println("--- Passive scan completed! ---");   }}
}


So, the passive scan is done. Through ZAP APIs you can start the active scans and spider scan. An example code snippet is as follows –

{
// create an object of org.zaproxy.clientapi.core.ClientApi using ZAP host and port. 
private static ClientApi api = new ClientApi(ZAP_ADDRESS, ZAP_PORT);
private String application_base_url = "https://<application_url>/";
// Example function to start and synchronize with active scan.
private static void startActiveScan() {
  System.out.println("Active scan : " + application_base_url);
  try {
       // initiate the active scan - refer doc to underatand the constructor parameters. 
    ApiResponse resp = api.ascan.scan(application_base_url, "True", "False", null, null, null);
         int progress;
       // scan response will return the scan id to support concurrent scanning.
    String scanid = ((ApiResponseElement) resp).getValue();
       // Polling the status of scan until it completes
      while (true)
      {
      Thread.sleep(5000);
      progress =Integer.parseInt(((ApiResponseElement) api.ascan.status(scanid)).getValue());
      System.out.println("Active Scan progress : " + progress + "%");
          if (progress >= 100) { break; }
}
  System.out.println("Active Scan complete");
  }
    catch(Exception e) {
    e.printStackTrace();   }}
}

Similarly, the example code snippet to start a spider scan is as follows –


{
// create an object of org.zaproxy.clientapi.core.ClientApi using ZAP host and port. 
private static ClientApi api = new ClientApi(ZAP_ADDRESS, ZAP_PORT);
private String application_base_url = "https://<application_url>/";
// Example code to start and synchronize the spider scan.
private static void startSpiderScan() {
  System.out.println("Spider : " + application_base_url);
  try {
  // Start the spider scan - refer the documentation of ZAP APIs to understand the spider scan
  ApiResponse resp = api.spider.scan(TARGET, null, null, null, null);
  int progress;
  // scan response will return the scan id to support concurrent scanning.
  String scanid = ((ApiResponseElement) resp).getValue();
  // Polling the status until it completes
  while (true) {
    Thread.sleep(1000);
    progress =Integer.parseInt(((ApiResponseElement) api.spider.status(scanid)).getValue());           System.out.println("Spider progress : " + progress + "%");
    if (progress >= 100) { break;}
  }
  System.out.println("Spider complete");
  }
catch(Exception e) {
e.printStackTrace();
}
}

So we saw how different scan starts, the next step is to access and consume the vulnerability reports of scan performed. However, the scan reports can be accessed using a curl request

Alerts: ZAP_HOST:ZAP:PORT/JSON/core/view/alerts

Report: ZAP_HOST:ZAP:PORT/OTHER/core/other/htmlreport

As we are discussing here the automation of tests so report should also be automatically fetched, an example code snippet using ZAP APIs is as follows –


// example fucntion to store an html report.
private static void getReports()
{
   private String application_base_url = "https://<application_url>/";
   try {
       // calling core apis to get html report in bytes.
      byte[] bytes = api.core.htmlreport();
        // getting the alert messages and just printing those.
      ApiResponse messages =  api.core.messages(application_base_url,"0","99999999");
      System.out.println(messages);
      // storing the bytes in to html report.
      String str = new String(bytes, StandardCharsets.UTF_8);
      File newTextFile = new File("report.html");
      FileWriter fw = new FileWriter(newTextFile);
      fw.write(str);
      fw.close();
   }
   catch (Exception e) {
      e.printStackTrace();
   }

I hope you find the content helpful and could solve the issues people are facing with automated security tests.

Comments