Scripting with ZAP: adding a new header to each scan request

Introduction

ZAP has scripting support that allows programmatical access to code and data structures but also to automatically modify requests and responses passing through ZAP's proxy or Active Scanner. Sometimes it can be useful to automatically add a (header) value to each request passing through the proxy or Active Scanner for monitoring purposes. This can be achieved with ZAP's scripting capabilities.

The previous blogs I wrote about security automation can be found here:

OWASP ZAP and Jython

I use Jython to access ZAP's data structures dynamically. To use Jython in your own environment, download it at http://www.jython.org/downloads.html and copy it to the lib directory of your ZAP installation.

A lot of scripts have been written by the ZAP community ready for you to use. Likely a script is available for the goal you want to achieve. You can have a look at the repository on the following location: https://github.com/zaproxy/community-scripts.

Typically ZAP is integrated within a customer's SDLC incorporating a Selenium/Jenkins set-up (see my previous automation blogs for more information about this). Functional automated tests pass through the proxy of ZAP and afterwards the Active Scanner replays these requests to the server. For a customer, I needed to add a custom header to each request sent by ZAP's Active Scanner so they could monitor and filter each request originating from ZAP's scanner and proxy. I found a script to add and modify the request headers and tailored it to my own needs. To use it yourself, save the script below as <filename>.py on the virtual machine running ZAP.

headers = dict({**"Log-Identifier": "zap-active-scanner"**});
def sendingRequest(msg, initiator, helper):
	for x in list(headers):
		msg.getRequestHeader().setHeader(x, headers[x]);
def responseReceived(msg, initiator, helper):
	pass;

Proof Of Concept

If you use ZAP in daemon mode, you can use the REST API to upload and activate your script. You can do this by browsing to the endpoint in your browser or by using a (Groovy) script like the one shown below:

url = **"http://localhost:8080"**
path = "/JSON/script/action/load/"
query = [apikey: **"<api-key>"**, scriptName: **"manipulate_header"**, scriptType: "httpsender", scriptEngine: "jython", fileName: **"<path-to-script-file>"**, scriptDescription: **"Add log-identifier header"**]
zapApiCall(url, path, query)
def zapApiCall(url, path, query) {
	def zaproxy = new HTTPBuilder(url)
	data = zaproxy.request(GET, JSON) { req ->
		uri.path = path
		requestContentType = JSON
		uri.query = query
		headers.Accept = 'application/json'
 
		// response handler for a successful response code:
		response.success = { resp, json ->
			assert resp.status == 200
			assert resp.contentType == JSON.toString()
			return json
		}
		response.failure = { resp, json ->
			throw new RuntimeException("${json}")
		}
	}
}

The screenshots below demonstrate the initial request passing through the proxy of ZAP and the adjusted request with newly added Log-Identifier header.

This is the initial request part of an automated functional test passing through the proxy of ZAP before the Jython script is enabled.

This is the same request replayed by the Active Scanner of ZAP containing our newly added header.

Vragen of feedback?