As discussed previously, most of the software we write is going to depend on on a great deal of other software. It may take the form of frameworks, libraries, utilities or any other code that allows our code to do what it was designed to do. It was already discussed that to keep our software secure we need to keep all these dependencies up to date, but how can we trust that these dependencies aren’t doing anything malicious. The harsh truth is that you can’t and you need to find a way to deal with it. In this post first we will look at why you can’t trust other code, then we will discuss with three strategies with with to deal with this problem: choosing wisely, using and auditing tool, and writing it yourself.
I will confess a bias here, I have only written software using free open source frameworks and libraries, so I don’t have experience with code you pay for, I would think that most of the same concerns apply. The fundamental problem is that when you use someone else's code you are telling the computer that is running it to execute instructions that you don’t know about, hopefully most of these were what you intended, that was you imported it in the first place. However without you having read through all the code paths you are now using there is no guarantee that it isn’t acting maliciously as well. It could also be a bug that was included in the dependency that allows an attacker to cause your software to act in a way that it was not intended to, which by extension makes your software vulnerable. This is something we have to be conscious of as developers, but also not let this thought paralyse us. Once you accept that the software that your software depends on may, intentionally or unintentionally, make your software vulnerable, you can start dealing with it.
The first step in dealing with the danger the inherent risk associated with relying on the security of other people's code is choosing who you rely on wisely, and revisiting that choice as often as is practicable. There is some safety in numbers, check to see if many others are downloading this dependency, if it is popular there is a good chance that one of those many sets of eyes will have picked something something malicious. Check that is has been updated recently, an unmaintained repository is a sign that vulnerabilities are not, and will not be addressed. You can also put more stock is repositories that are associated with big software names, is it is likely that they will not want the reputational damage of publishing vulnerable or malicious code. However this shouldn’t be the only criteria, as sometimes the library you need won’t fit this criteria. Also if we only pick popular packages we miss out on innovative new ideas. Always check you can read and understand the source of what you are importing, many eyes are what makes open source code safer for everyone. When you aren’t quite sure it is time to ask for a second opinion, and luckily there are many tools out there that help with this, that are well worth looking into.
What tool you choose will depend on what language you use, but if you are writing in a language that is reasonably popular there is a good chance that Snyk could work with you project, I will write a post on on integrating Snyk into a project at a later date. Whatever tool you choose the concept is simple, it runs through your dependency list, and checks its database to see if the version of the dependency that you are using has any known vulnerabilities or is known to be malicious. Even package management tools like NPM are helping out, by adding your dependencies every time you install them, all that is required is run one of these tool, and it will let you know if there is a problem that is now making your software vulnerable. If you have kept your dependencies up to date, fixing this should be reasonable painless.
The final way to deal with the dangers of the code you depend on is to choose not to depend on it. It is always good practice to make sure you are adding the minimal number of dependencies to achieve your goal. Before you add a dependency of questionable origin have a look at the source code, and see if this is something that you can implement yourself. The piece of mind that you gain from implementing sensitive application flows, like login and payment, can be invaluable. Try not to go too ‘tinfoil hat’ and reimplement everything yourself, you will never finish. Weigh up the sensitivity of your application with how much productivity you are willing to sacrifice to suspicion of other people's code. However re-implementing a web server yourself will take you a long time, and you will probably get in wrong. When you write code yourself, you just own the mistakes. Keep this in mind before you remove every dependency from you project.
In conclusion we have established that you can’t completely trust the code that you import, but that doesn't mean that you shouldn’t import any libraries, you are just as likely to write a bug that makes your code vulnerability as another person. Choose what you add wisely and always make sure that you can understand what you are importing. If you are implementing a sensitive flow, like a login screen consider implementing as much of it yourself as you can, but be sensible. Use the dependency auditing tools that are available to check what you have imported doesn’t have any known vulnerabilities. Happy coding.