The earlier you find application bugs, the cheaper it is to fix them. That’s one of the reasons so many organizations have adopted Test Driven Development (TDD). TDD enables Developers to more accurately identify if the code you are about to commit is going to break and not pass the tests you’ve instrumented in CI/CD. Analogous to the TDD process, we believe in automating application security testing. That’s why we created StackHawk!
Check Out How it Works
This post demonstrates how to leverage StackHawk to automate testing for AppSec bugs in your CI/CD pipeline. We’ll walk through:
- An Example App, “Vulny Django”, used for testing StackHawk
- How I Dockerized my Django App
- Building and testing my app in GitLab
- Finding application security bugs in the pipeline with StackHawk
Note: StackHawk is a dynamic application security testing (DAST) scanner that actively tests the code that developers write for security bugs
Example App: Polling with Django + Local StackHawk Scan
I took the Django tutorial on making a polls application and modified it into a project I call Vulny Django to test StackHawk. I’m not the best coder, so as I created the app, I wanted to run the tests that we built in with the tutorial alongside the StackHawk DAST scanner. Running StackHawk locally allows me to ensure that I have not introduced any AppSec bugs as I build.
The StackHawk scanning capability is configured via a yaml file that describes how to scan your application. In this example app, we have a pretty simple StackHawk config to scan locally, as we mostly have to instrument how to log in to the admin page and a few URLs to exclude. With this configuration, I can easily scan my Polls app and the admin interface from my local laptop using the command:
vulny_django_play/ $ docker run -e API_KEY=${HAWK_API_KEY} --rm -v $(pwd):/hawk:rw -it stackhawk/hawkscan:latest
This command dynamically scans my running Django app to test it for security issues that can be triaged on the StackHawk platform.
Containerizing The Polls App for Testing in Pipeline
I have also dockerized my Django project, because why wouldn’t you!? I want to be able to scan my Django project in GitLab’s awesome DevOps platform. To do that, my application needs to be running somewhere. While I could deploy my app onto a staging site and have HawkScan, the StackHawk scanner, scan it there, I choose the ephemeral route, where everything only lives temporarily while I test it. This allows me to break builds if I find an issue before deploying my app to a place that may be being used for user testing or other things.
Using docker, I can create a docker network and attach my Django and StackHawk/HawkScan containers to it so they can talk to each other. Doing that looks like this on the command line.
/vulny_django_play/ $ docker network create scan_net
/vulny_django_play/ $ docker run --detach -p 8020:8020 --network scan_net --name vuln-django --rm vuln_django:latest
/vulny_django_play/ $ docker run -e API_KEY=${HAWK_API_KEY} -e HOST=http://vuln-django --rm -v $(pwd):/hawk:rw --tty --network scan_net stackhawk/hawkscan:latest
With these commands, we are:
- Building the docker virtual network named ‘scan_net’
- Running the Django docker image attached to the scan network that we built (
docker build . -t vuln_django:latest
) - Running the Django docker image attached to the scan network that we built (
docker build . -t vuln_django:latest
) - Running HawkScan while attaching it to the scan_net and overriding the HOST variable in the stackhawk.yml to point at the docker network name of the Django App
PSA: Django will not let you put underscores in a host name like this http://vuln_django because it’s not RFC compliant. If you do that, you will Google search for a while to try and figure out why your app is not working in the docker network only to find that if you change that to a dash, http://vuln-django, everything will work fine and you will be embarrassed (actually that last part might just be me).
Once this is all set, you’ll have two Docker containers talking to each other over a virtual Docker network. This is a perfect time to start working on our GitLab config, since we can do Docker-In-Docker in GitLab.
Go Go Gadget GitLab!
GitLab’s DevOps Platform has awesome capabilities. I wanted to wire up my Django project with a platform that could run my tests on PRs when I commit code, or when I want to merge code. GitLab CI/CD made that process pretty easy. They employ a pretty simple yml config file to instrument what the pipeline should look like and do.
I chose a few tests to run, like flake8 and the migrations and tests I had written to run in the test phase using the python:3.7-buster image. So here we are installing the python library requirements, running flake8, then pre-populating the DB with info and running the test suite.
test:
stage: test
image: python:3.7-buster
script:
# This configures the test stuff
- apt-get update -qy
- apt-get install -y python-dev python-pip
- pip install -r src/requirements.txt
- flake8 vuln_django --max-line-length=127
- cd src
- python manage.py migrate
- python manage.py test
This allows me to make sure my code is healthy before I do anything else, like, say...
Testing the Build of The Docker Container
Now it’s time for me to make sure my docker container will successfully build in CI/CD. Here, I need to tell GitLab Runner how to build my image, which is SUPER easy. I tell it I need the docker image and that I would also like the Docker-in-Docker server to make sure that doesn’t introduce any issues with my build:
docker-build:
stage: build
image: docker:19.03.1
services:
- docker:19.03.1-dind
script:
- docker build -t vuln_django:latest .
This section is pretty straightforward and is similar to building a docker image on your workstation or laptop. This again is just making sure the image will build so that we can get to the good stuff.
Scanning My Dockerized Polls App with StackHawk
Now it’s time to scan the dockerized version of my Django app. We know we can scan the application on our workstation in either Virtual Environment or dockerized versions. We know we can make one Docker container talk to another Docker container, so let's tell the GitLab runner how to do that.
- We’ll need the docker image to build upon
- We’ll need the Docker-in-Docker service
- We’ll build the Docker virtual network
- We have to build the Django container and download the StackHawk/HawkScan container
- We’ll have to set the two Docker images to run on the virtual network
- We’ll use a YML override to tell the StackHawk scanner about a new environment and where to find the running application.
This section of our .gitlab-ci.yml file looks like this:
hawk_scan_execute:
stage: scan
image: docker:19.03.1
services:
- docker:19.03.1-dind
script:
- docker build -t vuln_django:latest .
- docker network create scan_net
- docker pull stackhawk/hawkscan:latest
- docker run --detach -p 8020:8020 --network scan_net --name vuln-django --rm vuln_django:latest
- |
docker run --network scan_net --volume $(pwd):/hawk:r --tty --rm \
--env API_KEY="hawk.${HAWK_API_ID}.${HAWK_API_SECRET}" \
--env BRANCH=GitLab:${CI_COMMIT_BRANCH} \
--env NO_COLOR=true \
stackhawk/hawkscan:latest stackhawk.yml stackhawk-gitlab.yml
Here we used an override yml file and configuration call to tell the scanner some new things. That override file looks like this:
app:
env: GitLab-CI
# The url of your application to scan
host: http://vuln-django:8020 # (required)
We are overriding two settings in the yml file: env and host. The host override is pretty simple, just identifying how the scanner can find the application, so for this configuration we gave it a simple, short name. The env is so I can tell in the StackHawk app which environment ran my scan. I could have other pieces of technology introduce or remove security bugs here, so I want to be able to differentiate. The StackHawk scanner will make the Environment in the Portal GitLab-CI.
(Interestingly, We had a customer use this to identify git branch name.. They wanted to know which branch introduced any issues they found. I think this is a really neat way to think about how to group results, but I think I prefer my Environment more loosely grouped, like CI/CD or GitLab.. I will and I will push that branch name to a Tags feature we will implement later.)
Now I have a configured GitLab Runner for my Django project, with the output looking like this:
And the results in the StackHawk Platform look like this:
Now it’s Easy to Know Before You Go!
As you can see, it’s really easy to implement DAST AppSec Scanning into the GitLab CI/CD pipeline and have the results available in an organized fashion that can help you understand the AppSec bugs that may exist in your application before it goes live. If you’d like to integrate StackHawk into your CI/CD pipeline, sign up for StackHawk Early Access and we’ll get you started. Also, if you would like to play with my Django app, feel free to fork it and use it to test out StackHawk in your CI pipeline.
Top comments (0)