Post

Risk Analysis

Risk Analysis

Risk Analysis

Risk analysis is in itself an art form. While those operating in cybersecurity under the umbrella of GRC will consider this to be paramount, it also usually happen that this is trivialised by those operating under more technical specialities. Risk analysis is the impersonation of “explicit is better than implicit”. Astute readers will here recognise the “Zen of Python”, or PEP-20.

Risk analysis is the art of making the implicit explicit. It uses frameworks, matrices and tried methodologies to get a kickstart in the risk analysis process, it is however important to sometimes think out of the box. This is however an engineering process, and shouldn’t be left solely to intuition.

Implicit

I am on the spectrum. Where? Somewhere over the rainbow. What usually happens is that there are two usual cases. Either I have a strong affinity with the domain I’m currently exploring, and what’s implicit seems natural to me and has been unearthed, by force of obsession, or it’s the other case and the affinity is lower. Then it seems mystical to me.

This leads me to the following statement. In the first case, if risks are natural to me to identify, in the latter case I will not see it explicitly. This seems like a hopeless situation. However. If I were, in the first situation to explicitly document and mention them, and you do the same, then we have a fallback for the second case. Making explicit the idea is better than keeping it implicit, as then it makes everything easier for everyone.

This website has a few decisions that seem natural to me and to be the perfect case to illustrate the meaning. Everything above, I’ve had to explicit as the decisions I had taken were based on implicit risks that I never bothered to actually explicit. This accurately follows what is usually done in production environments, where assumptions persist.

What is implicit here? It runs nginx. The content is statically generated. SELinux ensures that nginx can only read from the directory my website is hosted in. I’ve configured automatic updates to reduce the time to patch in case of vulnerabilities in services. The certificate auto-renews with Let’s Encrypt.

Explicit

When I’ve decided, back in 2021 to create this after my previous version burned down in Strasbourg, I decided several things :

  • I’ll have tested backups (they burned along my previous ambition)
  • I’ll have something simple
  • I’ll use it as a pet-project to learn CI/CD
  • I don’t want to have an interpreter, server-side
  • I don’t want to touch that server to administer the content, so GitOps This translates into a risk posture, and some risks are less explicit than others.

First, only having static content, no command interpreter makes the system far less susceptible to intrusions. Mistakes in code are not feasible from my end, leaving that to the services deployed and used. The risk is having an interpreter and code which needs to be secured, using some skills I don’t have. By removing the interpreter entirely, I remove the risk associated to me not being a developer and making mistakes in live code.

Second, I’ve taken a simple decision. Since there’s no specific SLA associated to that system, I’ve configured automatic upgrades and reboots. This makes vulnerability management easier, as instead of planning patching based on priority, everything has the same priority. Ignore the noise, reduce the operational burden, improve time to patch. This has some caveats, but I won’t describe them here.

Using CI/CD and GitOps for deployment is there for a single purpose, backups are irrelevant for that system as it is disposable. The target of the backup is not the system, but its ecosystem. If it were to burn down, I redeploy and be done. A little bit of pain for some configs, but the backups live in several locations and I’ve redeployed it already. The risk is having the server burn, the solution is to just make it cattle. The server dies? I redeploy. Backups are looser for that server because I use Git, have multiple backups of that repo, have many versions and can redeploy at will. This follows the 3-2-1-1-0 principles and is tested almost “real-time”. 3-2-1-1-0 stands for :

  • 3 copies
  • 2 different systems
  • 1 off-site
  • 1 offline
  • 0 failure

The system is simple. In case of intrusion, there’s not a lot worth stealing, as my content is public and I don’t have a database. Confidentiality loss is not the risk. The most problematic is the ability, in a full compromise, to use my domain to host malicious content. Here, using the famous CIA triad to illustrate allows to properly orient the idea. Confidentiality ? I don’t have drafts on this server, only what’s published. Someone would need to compromise my entire environment to get to these, which are of low value. Availability ? If the system is unavailable, so be it. In the event of a DDoS, it is needed to get the real IP of the server to DDoS, and circumvent Cloudflare. Integrity ? Now is the real deal. If someone hosts malware on that website, I’m assimilated as hosting malware. If this blog were to host Shai-Hulud, even if I enjoy Dune, I won’t enjoy the reputational impact associated with it. This orients my priority.

Using CI/CD, however, has some risks associated. I could absolutely bork the pipelines and have something utterly broken get there. Well, I did because why not? Using Gitlab-CI is marvelous, and a lot can be accomplished with it. However, it requires proper discipline. Hence, to the risk of “having broken tooling get into production”, the solution is pretty simple. First, the main branch is protected and can only be edited through merge requests. A dev branch is used, which deploys the same thing on another server which is hidden within a private network. Each article warrants its own “feature branch”, because this blogpost here is a feature. This can be tested and validated, and once done, a merge request opened. This is how bigger development teams work, usually for convenience, but it is also an answer to some of the risks of a large development team, it’s usually just never made explicit.

Using GitOps also means one thing. I publish content here using git commit, then git push, and a pipeline does it for me. By nature, humans are lazy and will cut corners, this is why we should have systems perform the most repetitive and boring tasks. Who wants to deploy an HTML page 300 times a day? Either scripted, automated, hook based,… Automation is the way to remove “human error” from the equation. However, you now do DevOps as you automatically propagate error to all servers. The risk here is for me to become lazy and complacent, and make mistakes because I’ve done it many times and cut corners. The mitigation is using a tool in my stead.

Explicitly mentioning that makes the posture far easier to get, to understand. The system is made simple and stupid so it’s security is easier to ascertain.

This is my pet-project that I’ve used to develop other projects. Foundations are necessary to get in place other, more complex setups. But think for a moment. Using git, one of the worst things you could do is push credentials to a public repo. How do you protect against that? Simple! Use a git pre-commit-hook such as gitleaks or trufflehog to detect credentials, and block the commit if credentials are detected, and use a .gitignore to prevent known files from getting added to the repo. Layer those defenses, add in a good onboarding of newcomers, have a good environment with good security and be open with them on the reason those exist. Also, these checks are required server-side as well. The pre-commit-hook is a “Preventative Control”, while the check on the repo is a “Detective Control”. The most common analogy is to network with IPS and IDS. The risk is getting your production credentials on a public repo and waking up with 200k€ in AWS bills. You don’t want that, use the simple solution of having Git slap you instead.

Closing thought

The presented case is simple. I made the choice of having Jekyll as a site generator in order to remove classes of vulnerabilities entirely. That means, no RCE, no LFI, no SQLi coming from my code. Those still top the most common vulnerabilities, and probably will for a long while. This blogpost is including organically threat modelling, as its tightly bound to risk analysis. This is also a simple case which is useful to demonstrate that automation is cybersecurity, and automating the trivial tasks will improve the security posture. Simplicity is the ultimate sophistication.

First, you explicit the risk, even if you think this is trivial and trivially answered to, and then you formulate the counter, and disseminate it.

Then comes the second step. You think of residual risk. The tone through this blogpost should make it already feel present, but I’ve taken some liberties and totally skimmed over some risks. These are residual, maybe accepted, maybe mitigated? This second step includes also looking into the mitigation itself. What if Git introduces a new class of vulnerabilities? What if I have introduced a new class of vulnerabilitie by some action I’ve taken? Refining this over will generate some graph, which will represent the risk analysis as a cloud the deeper you go. This is normal, this is how we observe risk, not as a single entity, but as a constellation of tiny dots. I’ve made some implicit assumptions through this blogpost. These, once explicited, will widen that constellation and make it more interesting.

Risk analysis is a real art form. It’s the art of making an implicit an explicit. That’s the role of a good communicator, and sometimes an education role. A risk analyst needs to know what makes an average human. They need to know what people tend to sweep under the rug. They need to have made these mistakes first. Create a homelab, make the mistakes. Fail fast, fail often, fail controlled.

This post is licensed under CC BY 4.0 by the author.