Security as Code: Why a Mental Shift is Necessary for Secure DevOps
Inertia is “the tendency to do nothing or remain unchanged.” It is a strong force in physics. It also often holds sway in the technology industry.
By 1996, the first official definitions of Internet Protocol Version 6 (IPv6) were created. Over 20 years later, IPv4 is still the main version used. IPv6 is slowly growing, but it is not going to be the main IP version for the foreseeable future. Inertia is strong when it comes to technology.
This is true of mental models as well. Often a certain way of thinking takes root, and it becomes more and more difficult to change that thinking. Eventually, those who are new to an industry will simply follow what everyone else is doing.
However, changes in the environment necessitate changes in thinking. DevOps is one such change in environment. DevOps practices are revolutionizing how software is built, making it easier to deliver quality software more frequently than ever.
Unfortunately, some believe that application security has been left behind in the process. If security is to keep up with DevOps practices, both developers and security professionals need to shift their mindset. Developers need to embrace security, and security professionals need to embrace development practices.
Let's take a look at why traditional security thinking doesn't fit into the DevOps world and what new way of thinking needs to take its place.
Traditional Security Practices
First, let’s begin with a “day-in-the-life” of traditional application security.
A development team slaves away on the newest application that will make a company loads of money. It uses the latest and greatest technology. The developers can't wait to release it. That is until the application security team takes a look.
One month before the application is released, the security team reviews the architecture and the code. They find a laundry list of security issues and deliver them to the development team to fix.
Now one of two things happens. Either the application release has to be postponed or the application is released with several vulnerabilities that could be exploited.
There are opposite forces at play between DevOps practices and traditional security. The developers want to deliver value as quickly as possible (a DevOps practice), while the security team wants to pause and make sure everything is secure and stable before releasing it (a traditional security practice).
So how can this opposition be resolved?
Security as Code
One of the fundamental tenets of DevOps is “Infrastructure as Code.” Technologies such as Ansible, Chef, and Puppet allow you to define your servers and infrastructure with programming language and deploy it in an automated fashion. With these tools, you can deploy the same setup for each instance of a server automatically.
Treating infrastructure as code has been a major enabler of DevOps practices, but it also requires a mental shift on the part of those practicing it. System administrators are following development practices and keeping code in source control. Developers now learn more about server setup. It enables both to speak the same language.
Automated testing has been a strong catalyst for the DevOps movement. It is important to push the functional testing to the “left,” or earlier in the development lifecycle. This makes it so you can find and fix bugs more quickly, and fewer bugs make it to production.
Application security is the next in line for automated testing. Application security concerns can now be automated as never before. But in order for these automation practices to address security concerns, a mental shift is required on the part of developers and security professionals. We need to shift security “left.” We need security as code.
Developers, Think Security
Before the recent focus on agile practices and automated testing, developers had the tendency to write code without testing it themselves. It was more common to throw the code over to a quality assurance team and let them find the bugs.
It was eventually accepted that finding and fixing bugs later in the development cycle costs much more money than finding them earlier. Developers now had to think in terms of how to test their code. Test-driven development tries to ensure that the code matches the functionality required.
Security should be no different. Security is part of quality and should be tested just like functionality.
A developer who thinks about security from the start of a project will practice Test Driven Security. This is done by writing security test cases that validate that your software is secure in an automated fashion from the start.
Implementing Security Test Cases
What does this look like? Developers create tests for functionality based on the requirements of the application. The same holds true for security requirements.
For example, if you're building a REST API that requires authentication, then you create tests that ensure no restricted URLs can be hit without proper authentication first.
Look at the example below (there are more here). This is a test in a Node.js REST API that makes sure that a 401 HTTP code is returned when an authentication token is not sent with the request.
If this code ever fails, then you have a major issue with your authentication code and need to take a look. Run these tests locally during development and in the build pipeline. They will give you confidence that the basic security requirements of your system are covered.
Does this mean that you need no reviews of your code? No, humans are still better at finding complex bugs that lead to exploits. Automate the basics and allow humans to find the complex issues.
Another testing tool that developers and quality assurance testers can use is called Gauntlt (pronounced “gauntlet”). Gauntlt allows you to create security test cases using sentence-like structures similar to the popular Cucumber framework tests. It looks like this:
This test checks the ports that are open on the server using Nmap. This can be an effective smoke test after deployment to ensure only the necessary ports are open on a web server. Tests like this one show the usefulness of security test cases and that tests are not difficult to add to your project.
Developers need to start regarding security testing in the same light as unit and integration testing. Automate your tests to provide confidence in your application’s security while maintaining speed of delivery.
Security Practitioners, Think More Like Developers
On the flip side, traditional security experts don't typically follow development cycles. Many don't know much about software development practices at all. Application security teams wait until a development team taps them on the shoulder and asks them for a review of their application. This is usually right before the application hits production.
The security team does their review and then passes on a PDF file of the results found. This doesn't work in a DevOps environment because it doesn't scale well enough to keep up with application changes. Large companies with many applications and projects going on at the same time will never have a big enough security team to review them all in a timely manner.
Therefore, application security teams need to think of security as code as well. This flavor of security as code is not application test cases, but using code to automate security testing tools to make testing more efficient.
Let's take a look at how this works.
Automate Your Penetration Testing
OWASP Zed Attack Proxy (ZAP) is a powerful penetration testing tool used to test web applications and find exploitable vulnerabilities. It has many different ways to scan applications out of the box and is quite effective with just a push of a button.
However, the real value of a tool like ZAP is its support of custom scripting. ZAP allows you to create custom scripts that automate complex tests that a human could do.
A good strategy that application security teams can use when using tools like ZAP is to automate your manual testing results. Perform manual penetration tests on your apps as you normally would, and document interesting and complex exploits that are found. Then transform these manual tests into automated scripts using ZAP.
Now you can point that script at any number of similar applications and test them in a fraction of the time it takes to set up a manual test. Check out the example below:
Take a look at the script here in ZAP. It checks to make sure that the application does not accept JSON Web Tokens that are not signed. Run this against all web applications that require JSON Web Tokens, and you'll find problems sooner without manual penetration tests.
Don't be scared off by what I'm about to say next: Security practitioners may have to create their own code repositories and keep them up to date. Open up a GitHub account and play around with it to learn how it works. Ask a friendly developer at your company to show you how to create projects in whatever source control system you use. Once you learn how source control works, you’ll find great benefits to using it.
Developers need to think security. Security experts need to think development. Automation is the only way to keep up in a DevOps environment.
Enable Self-Service Security for Developers
Another key tenant of DevOps is developer self-service. Developers can now create servers and environments on demand whenever they need it and tear them down when they’re done.
Application security teams should recognize this and embrace self-service for development teams. Use automated tools to enable developers to find bugs sooner. Integrate security testing tools in the build pipeline and make sure they run with every commit. Tools like ZAP and Gauntlt can be set up as tasks in a CI server such as Jenkins with little difficulty.
To the extent possible, the application security team should introduce security tools into the developers' daily work. IDE plugins exist that help developers find security bugs as they are writing code instead of weeks or months afterward. Puma Scan is a plugin for Visual Studio that scans C# code for security issues, and Find Security Bugs is a popular plugin for Eclipse that does the same for Java code. These plugins keep your software safer while teaching developers how to code securely.
Finally, when scans are run against the code base and builds, developers need to see the results. Create consumable dashboards that clearly show the health of an application from a security standpoint.
Instead of only giving this responsibility to security practitioners, allow the development teams to see what problems exist and create tickets for them in whatever project management or bug management software they use.
Security as Code: The Path to Secure DevOps
Securing DevOps workflows can seem daunting. This feeling stems from holding on to old ways of doing things that are no longer relevant in this new and exciting world.
Development teams need to treat security as quality. Give security a first-class status in testing to secure your applications from the beginning.
Just as importantly, security cannot get in the way of development anymore. Find ways to automate security testing. Give developers assistance in building secure software, and help them take the next steps.
When we implement security as code, both technically as well as mentally, secure DevOps will become a reality.