So you’ve got a great DevOps pipeline that builds, tests and deploys your application. You might still be running manual security scans for vulnerabilities or you could be passively scanning with OWASP ZAP as your functional tests run. Here are some ways you can automate OWASP ZAP to actively scan your entire application for vulnerabilities.

OWASP ZAP is a great open source security scanning tool, but with an extensive GUI, how does it fit into an automated pipeline? Luckily there are many options for interacting with ZAP without using the GUI. The easiest way to start exploring and interacting with the API would be the Web API. With ZAP running as a GUI or daemon and your browser configured to use the port where ZAP is running, simply navigate to http://zap/ and you will be presented with the different sections of the API. I’ve also found this useful when using the API with Python as a quick reference. More details on the API can be found here. All the API implementations can be found here, along with the ZAP’s source code. Another great resource is the test_zap.py script found in the zaproxy repo under zaproxy/python/scripts/generic-pytest/. You can use this script for standalone security testing or as a way to spider and scan after using ZAP as a proxy for your functional tests.

This tutorial will cover using the Python API to spider and scan an application. Spidering is where ZAP will visit a known webpage, scrape that page for links to other pages and visit them, repeating the same process on the newly found pages. Of course, if your application has a login portal, then you’ll need some way to authenticate in order to spider your entire application. You can find a great tutorial on how to do this here. The interesting part is the active scan. ZAP looks at all the urls you’ve found through spidering and actively tries to exploit vulnerabilities.

Start by grabbing the module with ‘pip install python-owasp-zap-v2.4’. Now let’s take a look at the script.

# A helpful reference: https://github.com/zaproxy/zaproxy/wiki/ApiPython
import time
import urllib2
from pprint import pprint
from zapv2 import ZAPv2

# The value of api must match api.key when running the daemon
api = "123456"

myApp = 'https://${private_ip}/'

#The following line must be the ip of where ZAP is, so for us it is localhost:8090
#Also if you are not running ZAP on port 8080 then you must include the line below 
#with the correct port numbers.

zap = ZAPv2(proxies={'http': 'http://localhost:${port}', 'https': 'http://localhost:${port}'})

# The script must be loaded prior to importing the context otherwise it will fail.

# Additionally, the APIKEY must be the last parameter on every method.

# Importing the context using the full file path.

print("IMPORTING CONTEXT")

zap.context.import_context('${workspace}/sbir-security/sbir.context', apikey = api)

# The URL must be opened before it can be tested on.

print('Accessing target %s' % myApp)

zap.urlopen(myApp)

time.sleep(2)

# Start the spider and wait until it's complete

print ('Spidering target ' + myApp)

scanid = zap.spider.scan_as_user(2, 2, myApp, subtreeonly = False, recurse = True, apikey = api)

time.sleep(2)

while (int(zap.spider.status(scanid)) < 100):
    print 'Spider progress %: ' + zap.spider.status(scanid)
    time.sleep(2)

print 'Spider completed'

# Wait for passive scanning to complete

while (int(zap.pscan.records_to_scan) > 0):
  print ('Records to passive scan : ' + zap.pscan.records_to_scan)
  time.sleep(2)

print ('Passive scanning complete')

# Start the active scan and wait till it's complete

print ('Scanning target ' + myApp)

ascan_id = zap.ascan.scan(myApp)

while (int(zap.ascan.status(ascan_id)) < 100):
    print ('Scan progress %: ' + zap.ascan.status(ascan_id))
    time.sleep(5)

print ('Scan completed')

# Report the results

print ('Hosts: ' + ', '.join(zap.core.hosts))
print ('Sites: ' + ', '.join(zap.core.sites))
print ('Urls: ' + ', '.join(zap.core.urls))
print ('Alerts: ')

pprint (zap.core.alerts())

# Writes the XML and HTML reports that will be exported to the workspace.
f = open('${workspace}/xmlreport.xml','w')
f2 = open('${workspace}/htmlreport.html','w')
f.write(zap.core.xmlreport(apikey = api))
f2.write(zap.core.htmlreport(apikey = api))

f.close()
f2.close()

Thankfully, Python is extremely readable so other than the comments, there’s no explaining to do. Before you can start using the API, you must start ZAP. Here’s a simple bash script to do just that.

/opt/zap/zap.sh daemon config api.key=“123456” port $port &
while $(! netstat anp | grep $port | grep LISTEN );
do
   if [[ $counter = 300 ]];
   then
       exit 1;
   fi;
   echo “sleeping $counter”;
   counter=$((counter+1));
   sleep 1s;
done
echo “done sleeping”;

Keep in mind that spidering and scanning, depending on the size of the application, can take quite a good amount of time, sometimes hours. For this reason, it’s best to save this for a later stage in your pipeline, or even a job that you configure to run overnight.

By automating active scans with OWASP ZAP, you free up other resources to be able to test or develop more, improve your application’s security, and ensure software quality. We all know security testing never stops, so why not put your pipeline to work in the never-ending effort and allow your resources to focus on creating more customer value.

Leave a comment

Your email address will not be published. Required fields are marked *

X