IBM Developer

Article

Understanding software security with simple code examples

Protect against vulnerabilities and malicious exposure

By Sahdev Zala

A good understanding of software code can help prevent security vulnerabilities. It’s a common misbelief that the security flaws in our software code are due to complex code that cannot be easily understood and addressed. However, the truth is that small mistakes and negligence regarding security aspects makes software vulnerable.

Security issues can be captured and fixed following the secure software development lifecycle (SSDLC). Here, I primarily focus on coding and code review. Moreover, I will show you how easy it is to spot and fix code that can expose some of the highly ranked security areas. The example code used is written in Golang, but the examples are simple enough to be easily understood and reused in any language.

You can watch the following brief and fun video version of this article, which allows you to play along in a coder challenge.


Video will open in new tab or window.


You might also be interested in reading “12 ways to improve your open source security” and watching a short video on the “3 A’s of Open Source Security.”

The Open Web Application Security Project (OWASP) Top 10 standards document represents a broad consensus about the most critical security risks to web applications. The OWASP regularly evaluates and updates these areas. In 2021, the top 3 security issues were broken access control, cryptographic failures, and injection, which are the security issues I’ll cover in this article.

Broken access control

Access control prevents an unauthorized access of resources and data. An example of broken access control is to fail to set the desired permission on the directories and files. The following program creates a directory using a built-in function MkdirAll from the Golang standard library package os. It’s good practice to use functions provided by the language packages, but they should be studied before using or they could pose a risk due to incorrect usage.

Image shows creating a directory using a built-in function MkdirAll from the Golang standard library

In this example, the expected directory permission is 0700. However, what if the dirPath already exist with a different permission, let’s say with 0777? The os.MkdirAll won’t fail if the directory already exists, and that will leave you with a directory with undesired permissions, which poses a malicious access risk. The best practice is to always make sure that directories and files have the desired permissions. In our sample snippet, a quick solution would be to return an error if the directory path already existed with an undesired permission. While it is a good approach to rely on trusted third-party libraries and tools, always understand the code before using it.

This example was inspired by a public CVE on the etcd open source project.

Cryptographic failures

A cryptographic failure occurs when sensitive data is not protected during transit or at rest. The following program shows a simple example. It asks a user to provide a password and saves that password in the database.

Image shows example when sensitive data is not protected during transit or at rest

So what does the program miss from a security perspective? If you look carefully, you will notice that it misses these security issues:

  • It’s not hiding password input on the command line. Some of the risks here include that someone can see the password when the user types it, and it might be accessed by someone through command-line history when used on a shared machine.
  • It saves password in plain text.
  • No checks are performed on the password strength.

These issues can be easily addressed by:

  • Always hiding the echo for password or any sensitive input as possible -- For example, in Golang, you can use the ReadPassword function.
  • Not saving a password in plain text -- The password must be salted and hashed. In our sample snippet code above, before passing the password to the savePassword method, it should be salted and hashed. For example, here you should use Golang package bcrypt functions.
  • Checking the password for its strength -- At a minimum check its length; longer passwords are proven to be safer.

Injection

One of the root causes for data injection vulnerability is a failure to validate input data. The example provided here shows how missing an input validation results in incorrect output and poses the risk of eventually crashing the application and the system. This example program sends hourly reminders according to the user’s desired time interval.

Image shows how missing input validation results in incorrect output and possibly crashing the application and the system

The issue in this sample code snippet is that the code doesn't check if the user-specified value is 0 or negative. In such a case, reminders can be sent unexpectedly all the time, and it will fill the disk space due to infinite logging and eventually crash the application. Before processing user input in any form, you should always validate it to make sure that it is not something unexpected.

Conclusion

Software security vulnerabilities can be costly and devastating. There are many areas where such vulnerabilities can be introduced in the software, so it is important to employ solid security methodologies as part of the software development lifecycle. This article provided simple code examples to illustrate some common security risks areas as identified by the OWASP Top 10. It showed that security-related issues don’t always lie in complex code, and how keeping simple things in mind during coding and code review can help protect software against vulnerabilities and malicious exposure.