August 4, 20220 mins read
According to Wappalyzer, PHP powers over twelve million websites. Not bad for a 28-year-old language! Despite its age, PHP has kept up with modern development practices. With support for type declarations and excellent frameworks like Laravel and Symfony, PHP is still a great way to develop web apps.
PHP works well in containerized environments. With an official image available on Docker Hub, developers know they can access well-tested PHP container images to build on.
Still, developers must use caution when building container images for PHP applications. Because PHP is so ubiquitous and widely used, PHP applications present a large, interesting target for attackers. Additionally, because we’re working with containers and microservices, we’re likely to be using PHP containers alongside third-party applications, which creates another avenue for vulnerabilities.
Too often, developers assume that container isolation means containers are inherently secure. This incorrect assumption leads to complacency. Being mindful of security is still essential, but with a little diligence and caution, developing and running secure containerized PHP apps is not difficult.
To implement these best practices yourself, and to follow along with this demonstration, be sure to have the following tools:
Docker installed on your machine. We’ll use Docker for running our PHP containers and using Docker’s built-in functionalities to secure our PHP containers.
A free Snyk account. We’ll use Snyk’s security scanning tools to highlight — and fix — vulnerabilities in our containers.
Container best practices to keep in mind
Containers are a convenient way to run PHP apps. And although container runtimes like Docker isolate containers from each other and from the host operating system, that isolation isn’t perfect and has been circumvented in the past.
However, there are several steps you can take to make your PHP containers more secure, including the following.
Set limit resources and use quotas
Configuring resource quotas per container limits the number of system resources a container uses. These resources include the memory and CPU the container consumes and disk I/O bandwidth. PHP developers can use several container runtime options to limit the container’s resource access.
–-cpus <value> flag passes the number of CPUs the container can use. When we set
--cpus 3.5, for example, it allows the container to use precisely three and a half of the available CPUs or CPU cores.
If you need more granular control, read about the
cpu-period flags in the Docker documentation. Note, though, that you’ll need to understand Linux scheduler periods to use the flags effectively.
Here’s an example of how to run a PHP container with CPU usage limits:
1$ docker run -it --cpus 3.5 php:7.4-cli /bin/bash
–memory= flag sets the maximum memory available for the container’s use. We can use a positive integer suffixed by
g to specify bytes, kilobytes, megabytes, or gigabytes, respectively.
For example, using the command below, let’s limit our container’s memory reservation to 800MB and set the RAM to a maximum capacity of 1.5 gigabytes.
1$ docker run -it --memory 1.5g --memory-reservation 800m php:7.4-cli /bin/bash
Limiting the resources available to a container minimizes an attack’s potential impact because it reduces the chance a container can consume so many resources that it interferes with the operation of the host OS or other containers. This approach enhances each container’s security and increases the host environment’s overall security.
Containers without quotas leave a system vulnerable to denial of service (DoS) attacks. A DoS attack may halt a system’s running services, resulting in degraded service for consumers. This demonstrates how quotaless containers can become a security concern for the business if not handled properly.
When you’re running PHP containers using Kubernetes instead of Docker, understanding Kubernetes’s context settings is a key component of maintaining container security.
Avoid running containers as root
It’s best to avoid running our containers as a root user or running them under a user account with more privileges than necessary. Instead, we should always follow the principle of least privilege when creating user accounts for our containers.
Most container runtimes offer ways to run our containers under a non-root user account. Docker provides a rootless mode to run the container runtime and all its containers with a regular user account.
Kubernetes also offers several options for running rootless nodes and containers. More broadly, any OCI-compliant container runtime will let us specify exactly what user and group the runtime should use via the UID and GID properties in our runtime configuration file.
In fact, those of you who are using Snyk to scan their Kubernetes configuration files or their Terraform IaC files will benefit from catching the above mentioned misconfigurations such as Memory limits not set, or Container is running as root which Snyk will point out visually including a reference to the lines of code in the the YAML file that doesn’t follow these security best practices:
Using Docker as an example, running our PHP containers in rootless mode isn’t much different from when Docker runs as root. All of the usual Docker commands should work, but remember that the privileges we can grant to a container will be limited to the privileges of the user account under which we’re the Docker daemon. There’s one catch: if you’ve already installed the Docker daemon in its default configuration, you’ll need to uninstall it and reinstall it in rootless mode by following the instructions in Docker’s documentation.
Avoid using a root user inside containers
In addition to running containers in rootless mode, we should try to avoid using a root user inside our container. Although this might seem redundant if we’re already running the container in rootless mode, it provides an additional layer of defense against malicious users.
We can run our PHP container as a non-root user by using the
USER directive on the
Dockerfile so that the container runs in a user or group context with as few privileges as possible.
If a Docker base image doesn’t include a low-privileged user, then we can add a non-root user as part of the image build process and it will be available for use when creating a new container image, as the following Dockerfile shows:
1FROM php:7.4-cli 2# the command below adds a new user or a group with a userid 1002 3RUN groupadd -g 1002 appuser && \ 4 useradd -r -u 1002 -g appuser appuser 5# command below changes a default root user to one with non-root privileges 6USER appuser 7CMD ["touch", "/root/example.txt"]
Official PHP container images come with an non-privileged user account named nobody, so if you are building a container images based on one of the one of the official Docker base images, you can use this user instead of manually creating your own (as demonstrated above):
1FROM php:7.4-cli 2USER nobody 3CMD ["touch", "/root/example.txt"]
Now, if we run the Docker image in interactive mode and try to see the text file created for the root user in the Dockerfile, we’ll see that we’re not allowed to:
1$ cat /root/example.txt 2cat: /root/example.txt: Permission denied
The main takeaway here is that avoiding the use of root both at the runtime level and inside containers means that a malicious Docker application image, or a compromised container application, would need to perform two successful privilege escalation attacks in order to breach into the host OS of which the container is running on, and from there to continue on to lateral movement.
Using rootless containers isn’t always possible, however. Sometimes containerized applications need access to hardware like the GPU; Fortunately, there are additional layers of defense we can add.
Fix container vulnerabilities with automated tooling
Many potential vulnerabilities lurk inside containers. Any libraries and applications included in your base image and any additional packages you install may contain CVEs or other vulnerabilities. If this were the case, how would you know? Most development and DevOps teams don’t have time to manually track the security history of every package installed in their containers.
Fortunately, automated container scanning can help. Snyk Container is free to use and can automatically scan our images and notify us right away of vulnerabilities in the packages and open source libraries inside our container, meaning we can roll out fixes as soon as a vulnerability shows up before attackers can exploit it. Snyk will also automatically propose fixes by opening pull requests with updated base image and recommended image tags to use.
Snyk makes it fast and simple to scan our containers for vulnerabilities. If we have Node.js and npm installed, all we need to do is run
npm install snyk@latest -g to install the Snyk CLI.
This command will open an authentication page in our browser:
"blog-php-container-authenticateA successful authentication message will appear in our terminal when we click the Authenticate button.
Now, we can scan use the Snyk CLI to scan our containers by running:
1snyk container test <your-image>
When run against a base PHP 7.4 image, we get the following:
Ideally, we should run a container scan in our CI/CD pipeline to ensure we never deploy a vulnerable container to production. We can also use the container monitor command to send our container image’s test information to Snyk. This way, Snyk can display the information in our dashboard and notify us of potential future vulnerabilities in our image.
1snyk container monitor <your-image>
Running it against the same PHP image we just tested results in the following:
Wasn’t that easy? And we’re not done. We can also use Snyk Code to ensure our PHP code is secure before containerizing it.
Navigate to the folder where your PHP application’s manifest file is located, and run the command below to see the output:
1snyk code test
We can run a Snyk code test against an older Laravel project to test a common scenario: finding vulnerabilities in an outdated but still-working PHP project. The result is below:
Similar to container monitoring, we can also let Snyk monitor our codebase by running
snyk monitor in our PHP app’s main directory. Then we’ll see the application appear in our Snyk dashboard.
Security scanning at the container and code level offers us an excellent way to ensure we ship secure containers and code.
Use trusted container images
One way to secure our PHP containers is to pull images from secure and trusted online registries. These trusted registries are less likely to have images with outdated or malicious code that could expose our container environment to a data breach. Docker Hub, for example, contains a variety of trusted images that Docker thoroughly checks for vulnerabilities and whose vendors are also verified by Docker that users can access.
Not all images on Docker Hub are trustworthy, however. We should review the image’s content for potential security vulnerabilities when using online registries. We can scan the image for malicious code that threatens the container before downloading the image to the host system. To ensure this step is never missed, we can directly integrate a security tool like Snyk into our development tools or workflows to monitor and fix potential security vulnerabilities in our containers.
Beyond scanning, we can ensure that we use trusted container images by using Docker Hub, the Docker Verified publisher program and Docker Official Images. The Docker Verified publisher program validates that images retrieved from here are from trusted sources, thus assuring users of not pulling unsafe images from false repositories.
Alternatively, we can use images verified by Docker Content Trust. Docker 1.8 introduced Docker Content Trust to reinforce the digital signing of images and image authentication. Image signing validates the source of a container image and verifies that the image hasn’t been tampered with. It also stipulates the policies determining the validated images users pull to their systems.
Despite pulling Docker’s official images from Docker verified publishers or Docker Content Trust to secure your container environment, it’s essential to use Snyk to secure our container.
PHP developers own their container security
Securing PHP containers from potential security threats requires implementing several security techniques throughout the application lifecycle — from the build to deployment and runtime. In addition to following PHP application best practices for container security, we can turn to Snyk to help us maintain container security through automated tooling, such as:
Automated security testing in CI/CD pipelines, such as GitHub Actions, or AWS Code Pipeline integrations
Combined security intelligence for both containers and source code
Kuberntes integration for monitoring and observing containers in our Kubernetes clusters for any new or pre-existing vulnerabilities
Developer-first approach that flags security issues in your own PHP Code and makes it simpler for developers to fix issues — even without an extensive security background
Snyk Container is a container vulnerability management tool that helps us identify and resolve container security concerns thoroughly and efficiently. With integrated IDE checks, automated CI/CD integration, native git scanning, and production environment testing capabilities, Snyk Container makes our Kubernetes containers and workloads secure through the entire software development lifecycle.
Additionally, Snyk can help identify vulnerabilities by integrating security scanning into development tools, like VS-Code and JetBrains, while applications are being developed, bringing you more information about the type of vulnerabilities and suggested fixes.
Snyk can also help developers identify vulnerabilities that compromise container security. It fixes these issues automatically, enhancing productivity and helping development teams spend less time fixing code and more time creating applications. Start your free account today. You can also try our free online PHP code checker tool to see how the Snyk code engine analyses your code for security and quality issues.