Security For Software Developers
Security is an important topic for all Software Developers to keep in mind when writing or building software. Any mistake can cause a whole business to go bankrupt. That is why, in this article, we will talk about some security concepts that are important to know. We will also discuss how we can make our software more secure by implementing specific measures.
Security became its own sector in IT (called CyberSecurity), and while we cannot cover everything in this article, we will discuss the most important concepts that need to be applied in any application you plan to build.
What is a Security in Software?
Security is the practice of ensuring software systems are protected from online hackers and malicious attacks. It helps us keep our data away from evil entities, our systems from being abused, and even from keeping our cloud infrastructure bill from becoming too expensive.
When it comes to building software, implementing security measures should be the default and not the exception. Making it difficult with no authorization to access our system, making sensitive data difficult to read and understand, and ensuring every action being done is logged and can be viewed later in case of a possible attack.
First, we will begin by going through some important concepts that need to be understood by anyone writing software. These concepts include hashing, encryption, and more.
Next, we will go through some security measures that you can start implementing on your software to make it more secure and less likely to be affected by attacks.
Concepts
There are dozens of concepts involved with cybersecurity, but in this article, we will focus on the concepts that you, as a Software Developer, will encounter often.
Authentication
Authentication is the process of ensuring that the correct user, or entity, has access to a system.
There are countless examples of this today. Gmail first checks that you are authenticated, or have access to the system, because allowing you to use it. WhatsApp might use your phone number to send you a One-Time Password, or OTP, to ensure that you actually have access to that phone number.
The way this works, is you might have database with a table for your users. Each user has a name, email, and password. To ensure that the correct user has access to the correct account, you will ask them for their email and password. These two provided values are then checked to make sure they match the values in the database, and if they do match correctly, the user is then “authenticated” and given access to the system.
Authorization
While authentication defines where a user can or cannot access the system. Authorization checks what permissions this user has access to.
Say you have a blog that allows users to create accounts and write articles. You might define a rule that specifies that only the creator of an article can edit or delete it. Now, imagine that User A creates a blog article. User B wants to make edit the article that User A created and published. User B will not be able to do so because he is not the creator of that article, User A is.
To give you another example of authorization, say you have a Sales Management system, where clients are inserted and new contracts are created for each client. You have 2 user types in this system, a Manager and an Employee. The Manager can create, edit, and delete clients or contracts, while the Employee can only manage contracts. If a user of type Employee tries to create a contract, the request will complete successfully. But they tried to create a client, they won’t be allowed to, because they are not “authorized” to do so.
Hashing
Hashing is a process in which we transform data (usually text), such as a password, to incomprehensible data or values.
It is basically a mathematical function that relies on a specific algorithm, which takes your data, runs it through this function by applying the set algorithm, and returning a meaningless value.
The biggest use of this concept is when storing passwords. Say you want to create a database table to store your users. Each user has a Name, Email, and Password. The name and email of each user are not so “sensitive” data, at least we will not consider them to be so in this case. But the password is a highly sensitive piece of data. A hacker having access to a users password allows them to access their account and manipulate data.
That is why we do not store password in “plain-text”, rather we hash them, then save them.
Hashing the password makes it (almost) impossible for a hacker to know the original piece of text.
Let us look at an example. Say your password is simply “12345678”, if we run it through a hashing function using the SHA1 algorithm, we will get the following output:
$ echo -n 12345678 | sha1
7c222fb2927d828af22f592134e8932480637c0d
Another great benefit of hashing is that any time you hash the same value, it will always print out the same output. In the example above, no matter how often we run the function with the same algorithm (SHA1 in this example) and with the same input, 12345678 in this case, it will always print out the exact same output.
This is great because it allows us to store our password in the database in a hashed format, making it incomprehensible to hackers, but anytime a user tries to sign-in to their account using their password, that value will be hashed and compared to the original hash that is stored in the database. If the hashes match, then the password is correct!
Encryption
Encryption is a process than also converts data into another form of data that is incomprehensible. But there is are two fundamental difference between encryption and hashing.
When a hash is created, it cannot be reversed. Meaning if you have access to a hash output, you cannot “de-hash” it to get the original value. While in the process of encryption, you are able to decrypt the encrypted value to get the original value.
The second difference is that encryption algorithms rely on a key that you, the encryptor, provide it with in order for it use to aid the creation of the encrypted value.
A great use case of encryption is the ability to access other servers using the SSH utility.
In order to SSH into a different server, the secure way of doing it is to have an SSH key generated through an algorithm like RSA, and connecting to the server using the key.
The way this works is that you generate an RSA keys which prints 2 different keys, 1 is a private key and the other is a public key. The public can be shared and set-up on any resource that requires secure access, such as the server in this case. Then, when you try to access the resource, or the server, you would try to access it by using your private key.
I won’t dive deep into how it happens by essentially, a value on the server is first encrypted using your public key. Now this value can be decrypted ONLY by using the private key. Therefore the SSH function can encrypt a random value using your public key, sent to you, and if you are able to decrypt it using your private key, the SSH will give you access to the server.
Validation
Validation is concept involved mainly when you accept input from a user. It is the process of making sure all provided inputs are “valid”. When I say valid, I do not necessary mean that they are correct, like in the case of inserting a users password. What I mean is that you need to make sure that input actually matches the type of entry it is supposed to be.
Say you have a website that has a register page, it accepts a Name and Email. On the back-end, you accept these two values, process them, and save them in the database.
A user can manipulate your system and insert values that are not actually just names and emails. Rather, they can be attempts of hacking into your system. A well-known method of doing so is called an “SQL Injection”. It is a technique to try to access a websites database by using SQL to be sent to the back-end. If the input is not validated, the SQL can be ran and it might give unprivileged access to the system, or worse, full access to the database tables.
Let us go through a simple example. Say you have a sign-in page that accepts an Email Address. When you enter the input and submit, the back-end takes this value and checks if such a user exists, like so:
SELECT * FROM users WHERE email = "<THE INPUT>"
This will return the full user row for that email. But now say the input was not just a normal email address, but an SQL statement like so:
email@example.com OR 1=1
Instead of the SQL query checking for a proper email address, it will run the following query:
SELECT * FROM users WHERE email = "email@example.com" OR 1=1;
This will return ALL rows from the users table because 1=1
is always true, which ends up being true for all rows, returning all rows as the result of the query.
This was a simple example of an SQL Injection, there are much more advanced techniques nowadays, that is why you need to validate all inputs that come from the users.
Vulnerabilities
A vulnerability represents a bug or a flaw in a system which makes “weaker”, security-wise. If a vulnerability exists on a system, it can mean that this system has a bug that allows a hacker to access it through that bug.
An example we already discussed previously is the lack of input validation in some systems, allowing hackers to use the SQL Injection method to access all users information.
Another vulnerability that can be found on some websites today, is having the debug mode of an application, which is a mode that returns additional information to an invalid request to help with debugging and development, be active while on production, where all users can access.
This vulnerability, if found and utilized by a hacker, can allow them to access sensitive information on your system, such as API Keys, users information, and more.
Vulnerbilities can be found on website, web applications, mobile applications, frameworks, and even programming languages. An example of a vulnerbility that can be found in a C language system, is accepting input from a user, without specifying the limit of how many characters the program should accept. The expected input might be just 10 characters, referring to a name, but a hacker might find this vulnerability and insert hundreds or thousands of characters, causing issues in the memory and the server/system.
As an example, we can check the vulnerabilities found in an NPM package that we might be using. We can find the list by typing in the package name in the following URL: https://security.snyk.io/
This website has a list of known vulnerabilities that can be found on different packages.
For this example, I used one of the most widely installed NPM package, lodash: https://security.snyk.io/package/npm/lodash
If you open the website, you will find a list of vulnerabilities for the current version of that package.
Security Measures
While we could not cover all concepts in CyberSecurity that are essential for a Software Developer to know, we did cover some of the most important concepts.
In this section, we will focus on different security measures we can apply to our systems to make sure they are much more secure.
Least Privilege
The least privilege security measure ensures that a user can only access what they require having access to and absolutely nothing else.
Say you have to deploy a web application on a server. You can upload the code or binary of your application and simply use the root user to run the application. But this creates a major issue. If a hacker was able to find a vulnerability to access your system and run Linux commands, they would be doing it using the root user, the most privileged user in the system, this means that the hacker has full access to the system and can do anything they want.
To make sure these types of things do not happen, you can create a low privilege user that acts as the owner of only the application and nothing else. This ensures that only this user cannot perform any other action on the system.
Another example of this is on cloud providers, like AWS for example. You can create a user and assign them whatever privilege you think they need. Say that this user can upload images to a bucket, or directory of images, in the AWS service S3. By providing them only the “PutObject” permission, you are ensuring they can only upload objects to this bucket. They won’t be able to create another bucket, delete the current one, or any other action, because they do not need to have access to these actions in the first place.
Using Latest Versions
As we saw in the “Vulnerabilities” section, some packages might have vulnerabilities that can make your system unsecure. One way to slightly counter this issue, is by using the newest versions of all the tools we use.
The reason for that is the package maintainers will see the reported vulnerabilities and try to fix them, prompting the creation of a new version of that package.
Say you install a package wiht version v3.0.1. A few days later, a vulnerability might be found on that version of the package, making it unsecure. The developer fixes the issue and pushes it to a new version v3.0.2. If you do not update your usage of the package from v3.0.1 to v3.0.2, you are making yourself application unsecure with the possibility of a hacker using that known vulnerability to access your system.
Therefore, it is best to use only the latest versions of packages.
Logging Everything
Logging is a powerful tool that allows you to keep track of everything that happens in the system. There are various types of logs that you can set-up including Application logs, Activity Logs, Security Logs, and more.
You can keep Application Logs to keep track of any bugs or issues that were encountered during a request. You can keep Activity Logs to see which user performed which action, you can make sure that a user does not have an incorrect permission and is able to do actions they shouldn’t be able to do. You can keep logs of what commands have been ran on the server.
Logging has no limits, just make sure you log the correct things and to not log data that might be exposing sensitive information, such as passwords and authorization tokens.
Keeping Resources Private
Similar to the “Least Privilege” security measure, this measure tells us that some resources should only be accessed by specifed entities.
Say you have a directory where all user-uploaded files and images are stored. The files and images can be accessed on the browser by simply providing the filename, along with the path. If a hacker finds this URL path, they might try to find all the files available in that directory and download them to have access to them.
To ensure such things do not happen, apply the “Least Privilege” security measure and only allow specific entities to access the system resources.
Setting Up a Firewall & a WAF
The next step in ensuring a high level of security on your system, is it implement a firewall and a WAF.
A firewall will disallow untrustworthy sources from accessing your system or server. For example, it will only allow access on HTTPS (on port 443) and not on HTTP (on port 80). It will ensure that only the right entities can access your server.
A WAF, or Web Application Firewall, is an additional layer of security on top of a firewall, that ensures all requests going to your application are checked before letting them through. For example, it will ensure that the request does not have any SQL that might cause an SQL Injection attack. It will also stop other types of attacks such as Cross-Site Scripting (XSS) and more.
A combination of a firewall and a WAF will ensure you have a high level of security in your application, server, and architecture.
Conduct Security Assessments
Once we are done implementing all of the security measures, we make sure it has all been implemented correctly and nothing has been forgotton by conducting security assessments. An initial security assessment can be done to make sure your system ticks all of the boxes.
To be more confident of the system, it is recommended to perform a Penetration Test every few months. A Penetration Tester will attempt to “hack” your system through different methods like by doing an SQL Injection, Privilege Escalation, or basically anything they can do to find as many vulnerabilities as possible. After they find these vulnerabilities, you as the developer, will be provided the list, describing each one and how it can be recreated for you to find it and be able to fix it.
It is better if a trusted Penetration Tester find these vulnerabilities before a real hacker does.
Conclusion
Security is a top concern when it comes to building and deploying applications. A simple misconfiguration can cause millions of dollars and the jobs of thousands of people. It is important to make sure your system(s) complies with the regulations set by your government.
I hope you enjoyed this article and benefitted from it. See you in the next one!