There’s no argument that data is the fuel of the future and the expansion of data has led to the rapid adoption of cloud services. As one of the main categories of cloud computing services,
Software as a Service (SaaS) has largely become the ‘new normal’ for organizations, eliminating the need to install and run applications on their own computers or in their own data centers.
While this offers myriad benefits for companies and their customers, from a build and deploy perspective developers are finding themselves under increasing pressure to create SaaS applications at speed. The Twelve-Factor App was introduced to help developers build web-based apps quickly and efficiently. The methodology is simple yet powerful, advocating the creation of apps that are both scalable and maintainable in the modern era.
While the guidelines promote scalability, resiliency, continuous delivery and maintainability, the fact that software continues to be written and deployed at an ever faster rate raises another important question. Is it possible to build a Twelve-Factor app securely? Is there a 13th factor that’s been forgotten?
WhiteHat Security Chief Scientist Eric Sheridan and his team believe that rather than a 13th step, security should be embedded into each of the existing Twelve- Factors. As such, they have created a Security Addendum to the Twelve-Factor App specifically for developers and architects, which provides actionable guidance on how to materially improve the state of security across each of the Twelve-Factors.
This article takes a look at each factor and explains how security can be implemented into each one.
The first factor in the Twelve-Factor app focuses on codebase and advises putting all code into one repository or source control system that can be tracked in a version control system (such as GitHub or Subversion) and make use of multiple deployment environments (think staging, development, production, etc.)
While this is the ideal, it’s important to remember that code is only as secure as the systems used to build it. As the guardian of the code and the systems in which it is housed, what steps can a developer take to ensure that the repository is properly secure?
● Lock it down. These days source code is the core of most businesses, therefore it’s imperative to protect it. The best way to do this is to impose restrictions by limiting access to the source code repository to authenticated users only. Additionally, limit commits to pull requests as opposed to direct commits. In this way, pull requests are reviewed by the team responsible for the master code, which then decides whether to merge the new code with the existing base, or not.
● No more secrets. Write code as if everything you did was open sourced. Operating ‘out in the open’ may actually be the best protection. It means writing application code with the assumption that it could be disclosed, so ideally the aim will be to have no secrets in the actual application. Externalize secrets and make their location known via startup arguments. This means putting secrets into configuration files where people can find them. Use the environment to protect the configuration.
● Review code regularly. Enforce secure coding guidelines via review of all pull requests. Equally important is to educate the team on solutions to pull requests that fail code reviews. Collaboration by sharing feedback and experiences will go a long way towards promoting awareness and supporting application coding that is undertaken quickly, correctly and securely.
The second factor of the Twelve-Factor app methodology calls for dependencies to be explicitly declared and isolated. All the environments in which code runs will need to have some dependencies, such as a database or an image library. Most modern applications consist of just 10% of built code, and up to 90% of borrowed code. Because open source is used everywhere, it’s logical that it can enter the code from everywhere and often, application security vulnerabilities come along with it.
According to the Department of Defense and Security, of all recorded security threats in the U.S., 90% occurred as a result of exploits against defects in software, rather than holes in the network. In order therefore to ensure application security, it’s important to have an understanding of what third party components are in your code. Are they affected by known security vulnerabilities? Are they up-to-date and do they comply with license policies?
Using software composition analysis makes it easy to know which applications are using a particular library, either directly or transitively.
● Know your composition. Software composition analysis will enable you to identify third party and open source components that have been integrated into your applications. It informs you of factors such the license of each component, and libraries that need to be upgraded or patched. Essentially vulnerability data is gathered from many different sources and software composition analysis can identify which ones have been validated.
● Know your risks. Software composition analysis provides information about license risks and can therefore help organizations reduce these risks that may be hidden in open source agreements. This extends to identifying and remediating those dependencies that may introduce security and/or legal risks.
● Review dependencies regularly. Now that you know what licenses each of these dependencies uses, it will be easy to identify and remove those licenses that conflict with business policies. Automate the extraction of composition and liabilities and enforce risk acceptance policy via integration into the build pipeline.
The third factor of the Twelve-Factor App advises storing configurations in the environment. According to 12-factor.net, an app’s config is everything that is likely to vary between deploys (staging, production, developer environments, etc). This includes resource handles to the database, credentials to external services such as Amazon S3 or Twitter, and per-deploy values such as the canonical hostname for the deployment.
It goes on to explain that apps sometimes store config as constants in the code. This is a violation of Twelve-Factor, which requires strict separation of config from code. Config varies substantially across deploys, code does not.
Twelve-Factor encourages the externalizing of that information, but the security ‘gotcha’ can lie in the security of the environment itself. For example, if a properties file is marked as ‘world readable’, anyone with access to that system can begin to read production properties, which can include confidential credentials to backend services, secret keys and tokens.
Therefore, when externalizing it’s very important to audit the environment. Identify and apply hardening guidelines to the environment, and take the opportunity to leverage a third party security team to assess the environment.
The fourth factor of the Twelve-Factor methodology suggests treating backing services as attached resources. It defines backing services as anything that’s outside of your app, which can be treated as a resource. It helps to encourage encapsulated development and smaller programs for example, but from a security perspective, backing services can also inadvertently encapsulate vulnerabilities. Remember:
● You assume their risk. Understand the security posture of the backing service and write code as if it is being attacked, or is attacking you.
● Secure your communications. Establish all connections using Transport Layer Security, authenticating to the backing service using a least privileged account.
● Resource security abstraction. Encapsulate security checks within the Resource abstraction, and limit the need for users of the Resource abstraction to be security aware.
The fifth factor recommends to strictly separate build and run stages. This factor relates more to processes, with an emphasis on identifying and separating each stage of app development, and encouraging automation between each so as to accelerate the process. From a security point of view, keep in mind these key activities during the build, release and run stages:
● Build — enforce security policy. The Build Stage is responsible for automating enforce of the security policy, and breaking builds that fail the said policy.
● Release — security go/no-go. The Release Stage should provide a consolidated view of the application’s risk, thereby allowing for a ‘go/no-go’ decision with respect to Release.
● Run — production protection. The Run Stage should provide capabilities to reduce business impact of exploited vulnerability (whether known or unknown).
In the sixth factor, the Twelve-Factor methodology encourages executing the app as one or more stateless processes by using small programs that communicate over the network. Many organizations are undertaking a ‘re-platforming’ journey, in which the overarching platform is broken up into smaller programs that are more service oriented, enabling changes to be made more quickly.
Unfortunately, a major security drawback of this journey is that when you start to break up a big building block into smaller pieces, the attack surface increases. This means there are more places where requests can be sent to your infrastructure, which equates to more opportunities to send an attack. Always assume therefore that all process inputs are controlled by hackers, and create one or more processes that are dedicated exclusively to security services.
The seventh factor focuses on exporting services via port binding and is actually an extension of factor VI. It encourages the integration of the network handling traffic code inside your running application. For example, PHP apps may run as a module inside Apache HTTPD, or Java apps might run inside Tomcat.
The challenge is that these modules must still be configured, which can lead to security risks if an app is bound to privileged ports or protected with poor passwords. To elevate security, avoid binding to privileged ports, instead using port forwarding to unprivileged ports. Once done, immediately drop privileges once bound to a privileged port.
Factor eight or concurrency suggests scaling out via the process model. A simple explanation for this factor is to picture a lot of little processes handling specific requirements, such as web requests, API calls, or sending tweets. Keeping all these working independently means that the application will scale better, and you’ll be able to manage more activities concurrently.
The security challenge to this is that the ability to scale requires that we pay attention to APIs that are known to introduce Denial of Service issues. One such API is known as “readLine”. Implementations of this method are available on almost every software development platform and yet is subject to Denial of Service. “readLine” will continuously read bytes from a given input stream until a newline character is found. Assume the attacker controls that stream… what if the attacker never provides a newline character? What will happen? More often than not, this will result in errors and stability issues stemming from memory exhaustion.
● Ban DoS-able API. Document relevant DoS-able API for your platform (ex. Readline) and ban them.
● Resource Closure. Expose simplistic pattern to facilitate closing of I/O resources (ex. Scope).
In the ninth factor, disposability, the methodology suggests maximizing robustness with fast startup and a graceful shutdown. This factor focuses on getting code and app deployments quickly out of the starting blocks and functioning immediately. Likewise your application also needs to be strong against crashing, and if does crash, it needs to be able to restart cleanly.
An important item to remember with disposability is to apply signatures and expirations to limit the life of derived security assertions. If the code is written without an expiration for example, and it’s intercepted over the wire, that token can easily be re-used, something that you don’t want to happen.
The tenth factor, DEV/product parity suggests keeping development, staging and production as similar as possible. The Twelve-Factor app puts a lot of emphasis on keeping services the same between the various phases of the product development lifecycle. However, when striving for DEV/prod parity as you move through the Twelve-Factors, do not share product secrets, as Uber found out the hard way when it stored a sensitive database key on a public GitHub page.
● Enforce the separation of duties. This means that DEV can’t see any secrets in QA, QA can’t see any secrets in STAGE, and STAGE can’t see any secrets in PROD.
● Replicate security services. Simply put, product security services must be replicated in the DEV, QA, and STAGE processes.
The eleventh factor suggests to treat logs as event streams. The most important security step in this factor is to log for security in such a way that anyone who is aggregating the log can easily extract the security log messages to avoid being burdened with stack traces for example.
So in other words, create a log record for each security critical event with supporting information, as well as a ‘SECURITY’ log record category to assist in aggregation.
The final and twelfth factor, focuses on admin processes and running admin/management tasks as one-off processes.
From a security standpoint, admin processes must be subject to the same security scrutiny as prescribed for factors I to XI. Incorporate one-off admin processes as part of ‘product’ discussions and understand the security risks associated with one-off admin processes.
The WhiteHat Twelve-Factor App Security Addendum Checklist contains all of the information discussed in this article. It is published as a list so as to be digestible and useful for developers.