I found myself in the office on Saturday night, mainly because the frozen pizza selection is more expansive than mine at home, and I wanted to get a head start on my project for this week. It was a normal Static Application Security Test (SAST), which follows a mostly pre-defined process, with embellishments depending on the languages and frameworks at play.
The first step in that process is to search for hardcoded passwords. I dug out the simple and ugly password regex I’ve created for this and did a search for /pa?ssw?o?r?d?|pwd/gi across the entire codebase. This regex covers all of the standard ways I’ve seen “password” used as a variable name in code. Without fail I got back:
After digging through all the results and parsing out the false positives I ended up with a total of 30 hardcoded passwords. All of them were database connection strings spread across 20 total users, including multiple users with admin access to the database. Our recommendation for this is simple:
Passwords should never be hardcoded in the source code.
The reasoning behind this is that there are multiple attack paths that result in source code/arbitrary file disclosure. Error messages, public Github repos, arbitrary file read, “oopsies” email attachments, and shoulder surfing being just a few.
A typical escalation path that exploits hardcoded passwords could start with an XML External Entity (XXE) Injection. An application that is vulnerable to XXE will allow us to read (almost) any file on the server. Through this an attacker will fingerprint the technology at play and target the important source code files. For example, a web application using the Python Django framework will contain a settings.py file. This file will sometimes contain hardcoded passwords for the DB connection. With some luck/bruteforce an attacker can find the source code directory and read the settings.py file via XXE.
POST /xxe HTTP/1.1
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY settings SYSTEM "file:///django-app/settings.py" >]>
An attacker can now take this information and connect directly to the database, which is public-facing because someone assumed authentication was enough. Once an attacker has this amount of access, any number of paths can be followed to further infiltrate the network. This attack extends to anything that requires a password: admin pages, config pages, mail servers, etc…
We need various forms of secrets (passwords, api keys, etc…) on the box somehow and the goal is to find the method that minimizes the organization’s risk to the greatest degree. Our suggested remediation regularly has the goal of assigning them through environmental variables on the server. Environmental variables are the recommended method due to the low likelihood of an attacker gaining access to them. I’ve only seen them exploited in two common scenarios, overly-verbose debug pages left running in prod, or using OS command execution to list all of the environmental variables*. Both of which are easily combatable in large code repositories. Environmental variables also allow a more scalable solution, as rotating secrets will only require one configuration change.
Like most things in security, except 0days, all the information is out there. A lot of people just aren’t aware of the vulnerability or what the proper way to fix it is. Because of that I won’t rehash how every platform implements environmental variables, but I will identify what I think are the best resources for doing so.
The handling of sensitive data for an app should always be done at the deployment/orchestration level. This ensures that secrets are stored and managed away from the web servers and databases. Here are some of the popular deployment and orchestration frameworks, with their related resources:
This is an interesting method using Docker Secrets. Unfortunately they are stored in files, but are the recommended method inside of the Docker ecosystem.
In the end, secrets don’t belong in the code. Proper distribution will decrease the reach an attacker has through other methods of attack and protect an organization by allowing them to rotate secrets easily and often.
This blog is an attempt at describing a holistic solution to secret handling that will work in every environment. Using environmental variables is still not the most secure method, as plaintext sensitive information is available to every user on the box. To combat this a platform-specific method is usually required, which Windows and Linux both offer.
We’re curious to hear how other organizations handle secrets in their code and what improvements could be made to further advance the topic. Let us know @NetSPI or by leaving a comment below!
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
YouTube session cookie.
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Analytics cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.
Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.
Cookies are small text files that can be used by websites to make a user's experience more efficient. The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission. This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.
Discover why security operations teams choose NetSPI.