Dynamic Application Security Testing (DAST) also called Black Box Testing is a testing practice that will test the application by executing your web application. As we know, In SAST, a Web application will be tested inside the application which doesn’t even require that the application should be running. So, when we do Dynamic Analysis, we need to attack our application bypassing the entry point into the DAST tool which will attack and identify some security issues. Then which can be captured only by Black Box testing. In this tutorial, we will see the OWASP Zed Attack Proxy (ZAP) and we will discuss How to perform Dynamic Analysis DAST with OWASP ZAP and Jenkins.
Table of Contents
So Far!
In our previous article, we have discussed How to perform Static Analysis SAST with Jenkins pipeline. And How to Integrate Jenkins SAST to SonarQube.
In this tutorial, I am using a simple python flask application to perform Dynamic Analysis DAST with OWASP ZAP and Jenkins.
Approach to DAST
A Typical DAST process is really a complex and lengthy process that will require a lot of manual interference. Those lengthy manual processes are not completely feasible to integrate with Jenkins to see the automated Security scan. So, we are going to perform only ZAP Baseline scan in this tutorial.
We will see how OWASP ZAP can be utilized to perform DAST test process in our upcoming articles.
Since this tutorial is about the ZAP Baseline scan, I am using the Docker image for the OWASP ZAP proxy and perform the Dynamic Analysis on our python application.
Setting up Jenkinsfile
OWASP ZAP proxy is available in the Docker Image as owasp/zap2docker-stable
. In which we can run it as docker container as follows.
$ docker run -t owasp/zap2docker-stable zap-baseline.py -t http://192.168.18.23:5000/
As we are building this entire Security Testing process in the Jenkins pipeline, we will proceed with the Jenkinsfile. So, we will update out Jenkinsfile with a new stage called Dynamic Analysis – “DAST with OWASP ZAP” and add a step with a shell script. Inside the shell, run the docker image for OWASP ZAP proxy by invoking the zap-baseline.py. Then pass the entry point URL of your application.
pipeline { agent any stages { stage ("Git checkout"){ steps { git branch: "master", url: "https://github.com/PrabhuVignesh/movie-crud-flask.git" sh "ls" } } stage ("Python Flask Prepare"){ steps { sh "pip3 install -r requirements.txt" } } stage ("Unit Test"){ steps{ sh "python3 test_basic.py" } } stage ("Python Bandit Security Scan"){ steps{ sh "cat report/banditResult.json" sh "sh run_bandit.sh || true" sh "ls" } } stage ("Dependency Check with Python Safety"){ steps{ sh "docker run --rm --volume \$(pwd) pyupio/safety:latest safety check" sh "docker run --rm --volume \$(pwd) pyupio/safety:latest safety check --json > report.json" } } stage ("Static Analysis with python-taint"){ steps{ sh "docker run --rm --volume \$(pwd) vickyrajagopal/python-taint-docker pyt ." } } stage ("sonar-publish"){ steps { echo "===========Performing Sonar Scan============" sh "${tool("sonarqube")}/bin/sonar-scanner" } } stage ("Dynamic Analysis - DAST with OWASP ZAP") { steps { sh "docker run -t owasp/zap2docker-stable zap-baseline.py -t http://192.168.18.23:5000/ || true" } } } }
So, the fundamental Baseline ZAP scan will perform and identify the following test cases and analyze whether those are passing or failing in the ZAP proxy.
+ docker run -t owasp/zap2docker-stable zap-baseline.py -t http://192.168.18.23:5000/ 2020-11-07 14:38:57,297 Params: ['zap-x.sh', '-daemon', '-port', '52548', '-host', '0.0.0.0', '-config', 'api.disablekey=true', '-config', 'api.addrs.addr.name=.*', '-config', 'api.addrs.addr.regex=true', '-config', 'spider.maxDuration=1', '-addonupdate', '-addoninstall', 'pscanrulesBeta'] _XSERVTransmkdir: ERROR: euid != 0,directory /tmp/.X11-unix will not be created. Nov 07, 2020 2:39:07 PM java.util.prefs.FileSystemPreferences$1 run INFO: Created user preferences directory. Total of 10 URLs PASS: Cookie No HttpOnly Flag [10010] PASS: Cookie Without Secure Flag [10011] PASS: Incomplete or No Cache-control and Pragma HTTP Header Set [10015] PASS: Cross-Domain JavaScript Source File Inclusion [10017] PASS: Content-Type Header Missing [10019] PASS: Information Disclosure - Debug Error Messages [10023] PASS: Information Disclosure - Sensitive Information in URL [10024] PASS: Information Disclosure - Sensitive Information in HTTP Referrer Header [10025] PASS: HTTP Parameter Override [10026] PASS: Information Disclosure - Suspicious Comments [10027] PASS: Open Redirect [10028] PASS: Cookie Poisoning [10029] PASS: User Controllable Charset [10030] PASS: User Controllable HTML Element Attribute (Potential XSS) [10031] PASS: Viewstate Scanner [10032] PASS: Directory Browsing [10033] PASS: Heartbleed OpenSSL Vulnerability (Indicative) [10034] PASS: Strict-Transport-Security Header Scanner [10035] PASS: Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s) [10037] PASS: X-Backend-Server Header Information Leak [10039] PASS: Secure Pages Include Mixed Content [10040] PASS: HTTP to HTTPS Insecure Transition in Form Post [10041] PASS: HTTPS to HTTP Insecure Transition in Form Post [10042] PASS: User Controllable JavaScript Event (XSS) [10043] PASS: Big Redirect Detected (Potential Sensitive Information Leak) [10044] PASS: Retrieved from Cache [10050] PASS: X-ChromeLogger-Data (XCOLD) Header Information Leak [10052] PASS: Cookie Without SameSite Attribute [10054] PASS: CSP Scanner [10055] PASS: X-Debug-Token Information Leak [10056] PASS: Username Hash Found [10057] PASS: X-AspNet-Version Response Header Scanner [10061] PASS: PII Disclosure [10062] PASS: Timestamp Disclosure [10096] PASS: Hash Disclosure [10097] PASS: Cross-Domain Misconfiguration [10098] PASS: Weak Authentication Method [10105] PASS: Reverse Tabnabbing [10108] PASS: Modern Web Application [10109] PASS: Private IP Disclosure [2] PASS: Session ID in URL Rewrite [3] PASS: Script Passive Scan Rules [50001] PASS: Insecure JSF ViewState [90001] PASS: Charset Mismatch [90011] PASS: Loosely Scoped Cookie [90033] WARN-NEW: X-Frame-Options Header Not Set [10020] x 3 http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/?__debugger__=yes&btn=Confirm+Pin&cmd=paste&pin=ZAP (200 OK) WARN-NEW: X-Content-Type-Options Header Missing [10021] x 12 http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/static/css/style.css (200 OK) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/update?__debugger__=yes&cmd=resource&f=style.css (200 OK) http://192.168.18.23:5000/update?__debugger__=yes&cmd=resource&f=console.png (200 OK) WARN-NEW: Server Leaks Version Information via "Server" HTTP Response Header Field [10036] x 18 http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/robots.txt (404 NOT FOUND) http://192.168.18.23:5000/sitemap.xml (404 NOT FOUND) http://192.168.18.23:5000/static/css/style.css (200 OK) http://192.168.18.23:5000/ (200 OK) WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 8 http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/robots.txt (404 NOT FOUND) http://192.168.18.23:5000/sitemap.xml (404 NOT FOUND) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/update (500 INTERNAL SERVER ERROR) WARN-NEW: Absence of Anti-CSRF Tokens [10202] x 9 http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/ (200 OK) http://192.168.18.23:5000/update (500 INTERNAL SERVER ERROR) WARN-NEW: Application Error Disclosure [90022] x 2 http://192.168.18.23:5000/update (500 INTERNAL SERVER ERROR) http://192.168.18.23:5000/?__debugger__=yes&cmd=paste (500 INTERNAL SERVER ERROR) FAIL-NEW: 0 FAIL-INPROG: 0 WARN-NEW: 6 WARN-INPROG: 0 INFO: 0 IGNORE: 0 PASS: 45 + true
So, When you execute the Jenkins pipeline Job, It will perform the usual other process and along with that, it will execute the ZAP security scanning which we have integrated now. If you see the log output in the BlueOcean, it will look like the below screenshot.
This ZAP baseline scan will show some warning and that will lead to an exit code that will make the Jenkins job fail. So, always pass the || true at the end of the shell script if the shell script output gives a false exit code.
Also, If you want the output of the ZAP proxy in the form of HTML, markdown, XML, or JSON, you can mention it as a parameter along with many more options of the zap-baseline.py
. So, the usage of the baseline scan is:
Usage: zap-baseline.py -t <target> [options] -t target target URL including the protocol, eg https://www.example.com Options: -h print this help message -c config_file config file to use to INFO, IGNORE or FAIL warnings -u config_url URL of config file to use to INFO, IGNORE or FAIL warnings -g gen_file generate default config file (all rules set to WARN) -m mins the number of minutes to spider for (default 1) -r report_html file to write the full ZAP HTML report -w report_md file to write the full ZAP Wiki (Markdown) report -x report_xml file to write the full ZAP XML report -J report_json file to write the full ZAP JSON document -a include the alpha passive scan rules as well -d show debug messages -P specify listen port -D delay in seconds to wait for passive scanning -i default rules not in the config file to INFO -I do not return failure on warning -j use the Ajax spider in addition to the traditional one -l level minimum level to show: PASS, IGNORE, INFO, WARN or FAIL, use with -s to hide example URLs -n context_file context file which will be loaded prior to spidering the target -p progress_file progress file which specifies issues that are being addressed -s short output format - dont show PASSes or example URLs -T max time in minutes to wait for ZAP to start and the passive scan to run -z zap_options ZAP command line options e.g. -z "-config aaa=bbb -config ccc=ddd" --hook path to python file that define your custom hooks
Configuration and Progress file
As part of the ZAP Security scan, we can ignore some warning and false findings by having the configuration. Then mention the same in the docker command as a parameter. For the same, we can have to create the configuration file first bypassing the parameter -g with the zap.baseline.py command and edit the file wherever WARN is appearing to IGNORE. We can also mention a particular rule ID and Make it ‘outscore’ with some regex pattern too. Example:
# Ignore the specified URL for Autocomplete in browser 90001 OUTOFSCOPE https://192.168.18.23:5000/index.html # Ignore all URLS containing ‘.js’ for all scan rules * OUTOFSCOPE .*\.js
Also, we can explicitly scan the known test case by addressing the same in the progress file. Example
{ "site" : "192.168.18.23:5000", "issues" : [ { "id" : "90020", "name" : "Web Browser XSS Protection Not Enabled", "state" : "inprogress", "link": "https://192.168.18.23:5000/bugtracker/issue=2312" }, { "id" : "90020", "name" : "X-Frame-Options Header Not Set", "state" : "inprogress", "link": "https://192.168.18.23:5000/bugtracker/issue=2312" } ] }
As I already mentioned, we can do more than the Baseline scan. We will discuss more in detail of OWASP ZAP proxy and DAST with it in our upcoming article.
Conclusion
In this article, we have discussed How to perform Dynamic Analysis DAST with OWASP ZAP and Jenkins. As I already mentioned, this article is about only the Baseline DAST scan. we will discuss more how to perform a full-scale DAST process with OWASP ZAP proxy and the ways to capture the results. Stay tuned and subscribe DigitalVarys for more articles and study materials on DevOps, Agile, DevSecOps, and App Development.
Certified Cloud Automation Architect and DevSecOps expert, skilled in optimizing IT workflows with Six Sigma and Value Stream Management. Proficient in both technical and leadership roles, I deliver robust solutions and lead teams to success.