The COVID-19 pandemic thrust the world into an era of massive digital business transformation. Tighter purse strings have created the need for cost-effective solutions to meet these new challenges while maintaining business operations. This has led to a sudden, unprecedented shift to the adoption of cloud-native applications.
But this migration from onsite to offsite brings a new set of security challenges. While cloud-native applications are considered to be reasonably secure, there are still opportunities for exploitation. Containers, orchestrators, and APIs present in an application’s surrounding infrastructure represent new attack surfaces. In addition to the cloud service itself, each of these layers has an array of user-defined settings intended to help users apply their security policies. This manual configuration is fraught with opportunities for user error and misconfiguration that opens the enterprise to potential attacks.
Fortunately, there are steps you can take to ensure the security of your cloud-native applications that don’t require a lot of time and resources:
1. Scan all applications for vulnerabilities
Attackers seldom go directly after mission-critical applications. Instead, they look for the weak link, a back-office internal application or a marketing app built for a one-and-done campaign. Then, they traverse from there through your containers and orchestrators to reach the crown jewels. This is why it’s important to test all of your software whenever it’s changed.
2. Set deployment policies for what is acceptable and assess drift/exceptions
Use automation to apply policies that reflect your risk appetite. Then regularly assess drift that happens when security configurations of the cloud service, containers and/or orchestrators are changed, or when deployment resources themselves are changed. To detect this, for each security setting, allowed resources should be listed, and each deployment assessed for exceptions.
3. Test your APIs and leverage fuzz testing
As modern software embraces the re-use of third-party code, the functions are held together via APIs. You must ensure your APIs are safe. To do so you must understand the expected output for a given input and test for the unexpected.
Fuzz testing has been around for a while, but it really shines when behavior fuzzing is applied to testing API operation parameters. First, the fuzz engine captures a valid operation, then it sets operation parameters to unexpected values in an effort to cause unexpected behavior and errors.
4. Identify and manage your secrets
APIs often require that secrets be passed to allow one piece of code to talk to another piece of code. These secrets can include passwords, SSH keys, tokens, and so on. Common mistakes in handling secrets include putting them in the code itself, not rotating them, and not backing them up. In fact, one of the most frequently recurring mistakes is to simply store secrets in a plain-text project configuration file or in environmental variables. Fortunately, a secret detection scan can identify secrets accidentally or intentionally committed to your code repository, allowing the developer to remove and invalidate the exposed secret before it can be used in an attack. Secrets can be managed through purpose-built solutions such as Vault by HashiCorp, or AWS Secrets Manager.
5. Monitor and protect East/West traffic among pods
Traffic within this cloud-native infrastructure can also cause security issues, like Kubernetes pods exchanging data with unknown or malicious sources, compromising the concerned cluster. To combat this, network security constructs (think firewalls) should be applied between groups of containers (pods) preventing users from escalating permissions, traversing the infrastructure to unauthorized apps, and so on. Network Policies are rules that regulate how pods can communicate with other pods and other endpoints.
6. Container host security
In addition to monitoring traffic within your application’s infrastructure, you will want to prevent an attacker from gaining access to a container hosting an application that is accessible from the Internet. For example, if an attacker traverses containers to access critical apps and data, they can gain access initially via exposed credentials, an external dependency, or through command execution where the app doesn’t validate input properly. From here, an attacker can deliver and execute an exploit that connects to the attacker and waits for commands or modifies configurations on the container’s file system to escalate their privileges.
Lateral movement can also be achieved where the attacker probes other hosts in the container's network. To do this, you’ll want to scan your dependencies and containers during development but also enable logging of system calls on any containers in your Kubernetes cluster.
By deploying a network policy to your Kubernetes cluster, the compromised container will not be allowed to create an outbound connection to the attacker through the internet. Similarly, the executable exploit is prevented from probing other pods in a cluster network due to policy restrictions.
When implementing a CI security solution, simplicity and integration wins. By making security scanning an automated by-product of your developers’ natural workflow, you can more efficiently and effectively reduce security and compliance risks.
Cindy Blake is Senior Security Evangelist at GitLab, a startup that’s leading the explosive DevOps market with an innovative single application approach for the entire software development lifecycle. Cindy collaborates around best practices for integrated DevSecOps application security solutions with major enterprises. Her recent book, “10 Steps to Securing Next-Gen Software,” combines her cyber security experience with a background in lean and software development and simplifies the complexities of today’s software evolution into pragmatic advice for security programs.