5 Security Concepts Every Developer Should Understand
A constant danger in software development is developers that do not or will not understand how to secure their software.
The current state of technology has created an environment where applications are constantly being attacked. Applications are often the weakest link in a company’s infrastructure.
That means that it is unprofessional for a software developer to build software without understanding how to secure it.
Some may feel that learning how to develop good code is hard enough and that there’s no time to learn about security, too. But the fact of the matter is that secure code is good code. A bug in production could cause big issues. An attacker using a vulnerability in your software to steal sensitive data is just as bad (and in some cases worse).
Write code with this in mind: Secure code is good code.
Developers don’t need to be security experts to build secure software. There are experts available to provide guidance when needed and in tricky situations, but every developer should strive to learn key security concepts.
Let’s look at the five security concepts that all developers can and should understand.
#1: The CIA Triad
One of the core security concepts that forms the foundation of any security activity is the CIA triad. This is not a clandestine government organization; rather, it’s the acronym for the triad of Confidentiality, Integrity, and Availability. All security activities exist to support and protect these three qualities of data. Other key security concepts branch off of these foundational concepts.
Simply put, confidentiality means that data should be made available only to those authorized to view it. The key concepts that branch off of confidentiality are authentication, authorization, and encryption.
Authentication is the process of a user proving that they are who they claim to be within a system. This can be done in three ways: by something you know (password or PIN), something you have (token), or something you are (biometrics). Only data classified as public should be available to those who haven’t been authenticated. Authentication is a basic need of almost every website.
Authorization is not the same thing as authentication. Authorization is the process of determining if the authenticated identity is allowed to perform an action or see a piece of data. For example, logging into your bank’s website doesn’t give you access to see every account balance in the system. Instead, you are authorized only to view your own account balances.
Finally, encryption is a major method of ensuring confidentiality. When data is at rest or in transit, encryption can prevent any snooping eyes from seeing the real data, thus protecting its confidentiality. Sticking with the bank example from above, banks use an HTTPS connection to encrypt what is sent back and forth between client and server. This means that sensitive information such as login credentials, account numbers, or social security numbers are not readable by someone eavesdropping on the connection.
While confidentiality ensures that data is seen only by those authorized to see it, integrity ensures that data is changed only by those authorized to change it.
Integrity is ensuring that the data you see is the real data and hasn’t been changed in transit or at rest by someone else. The key concepts branching off of integrity are hashing, authorization, accountability, and auditing.
Authorization is discussed above in connection with confidentiality, but it also impacts on integrity. It is important for a system to have the ability to determine which identities are authorized to see the data (confidentiality) as well as change the data (integrity). If an unauthorized party is able to change data, a failure of integrity has occurred.
Another key concept dealing with integrity of data is accountability. Accountability is the ability of a system to know who changed what and when. While it is preferred that unauthorized changes are not made to your data, accountability helps if that unfortunate event does occur. It will ensure that the change is at least detected and can be repaired if necessary in a timely fashion.
A related principle is non-repudiation, or ensuring someone cannot deny that they did something. This is usually handled by logging events inside an application. Important events can be logged with information including the user who completed the action, the action taken, and what time the action occurred.
Non-repudiation helps facilitate auditing at a later date, if that is required for compliance purposes. This action also ensures that if data is changed for some reason, the records for who changed it and for what purpose can be retrieved if needed.
Finally, hashing is a tool that can be used to ensure integrity. Hashing involves passing data through a one-way function so that the hash of the data is easy to compute, but given the output, the input is near impossible to figure out. Also, if even one bit changes in the input, the output is completely different.
Through the process of hashing, if you have a hash of data stored somewhere, you can compute a new hash; if they match, you can be assured that the data hasn’t changed. For example, this mechanism is used by Windows Update to ensure that updates haven’t been changed in transit or by some other means.
Availability is the assurance that the system and its data will be accessible to authorized users when needed. This process ensures that your website is up when users need it, or your REST service is available when your clients want to call it. Denial of service (DoS) and distributed denial of service (DDoS) attacks are attacks against availability.
Some major topics related to availability include disaster recovery, failover, and resiliency.
Disaster recovery plans assist in the recovery of systems in the event of a major disaster, whether natural or man-made. You should understand what will happen if your servers are destroyed by a flood or other disaster. Web developers may not always create these plans, but they should know what they contain.
Failover is achieved by redundancy; in the case of a loss of service, another instance of that service can instantly take over, providing near-constant availability. Think of a set of Docker containers that can be spun up or destroyed when problems arise. This redundancy also is achieved through load-balancing that can redirect traffic if something goes wrong with a server environment.
Resiliency is a system’s ability to withstand attack. Whether it’s a DDoS attack or another type of attack, a resilient system will not simply go down at the drop of a hat. Avoiding single points of failure is the best first step in resiliency.
The CIA triad is the foundation of all security principles. It should be understood by developers because developers will need to build functionality into their systems that support confidentiality, integrity, and availability.
Check out this graphic as an easy reference for how different concepts branch off of the three foundations of security.
#2: Least Privilege
The next key security concept for developers is least privilege. Least privilege is the principle that users should only have the access needed to do their job and no more, and only for the time it is required for them to have this access.
You may be thinking that this is more of a principle of information security and should be a policy for identity governance and administration, rather than something that developers need to understand to do their job. But that’s not entirely true.
You may think of least privilege as being about individual users. However, the same principle holds for applications.
For instance, is your application connecting to a database using an account that has superuser access, such as dbo in SQL Server? This can open you up to exploitation. If your application has a SQL injection vulnerability and it is exploited, the attacker could drop tables and do all sorts of nasty stuff to your application’s database or hop to another application’s tables.
A more secure approach would be to create an ID just for your application (often referred to as a service account) and only grant the permission to that ID absolutely needed by your application to do its work. If your application’s ID has only read-and-write access, then your tables won’t be destroyed (or worse yet, another application’s tables) if the worst happens.
A secondary strategy to support least privilege in databases is giving applications access to views and stored procedures, and not the underlying tables.
Database administrators and other database experts can create the views using the underlying tables, but once they are done, your application will only see the view as a read-only source of data instead of the base tables. Stored procedures can update the data in a controlled manner instead of allowing the application to update any table it wants.
Least privilege also means that your application should only have access to the parts of the server file system necessary.
A directory traversal attack happens when an adversary tries to navigate to areas of the server that are outside of your website. They may do this by passing in a “../” character string that will tell the server to navigate to the parent directory (outside of your site’s folder structure).
This can be prevented by your application not having access to sensitive directories and not having root access to the server.
#3: Complete Mediation
Complete mediation is probably a term you don’t hear too often. But this is definitely a concept that developers should understand, especially if you are building REST services consumed by a client-side framework, like AngularJS.
Complete mediation is the principle that ensures that no potentially sensitive actions can be done without verifying that the user is authorized for the action. This can be illustrated by User Access Control in the Windows operating system.
No matter how many times you enter an admin password to install a program or delete a file in a protected directory, you will have to do it every single time. If you rename a protected file, enter your admin credentials, and then try to rename it again, you will be prompted again for admin credentials.
This principle is important in web applications using REST services that are being consumed by a client. Take, for example, a SPA application that uses REST services to provide functionality. A naïve developer might assume that the URLs for the REST service are not published anywhere, and thus will only be called by the UI application. Then, as long as the UI application knows who you are, the REST service doesn’t need to authorize each call.
That would be a wrong assumption to make. Someone could easily grab the URLs you are calling via “View Source” or a proxy such as Fiddler. Then they could craft requests and send them to the REST URLs without interacting with the client at all.
This is where complete mediation comes into play with security authorization. Each call to a REST endpoint needs to be authorized. Server-side code should always perform authorization before doing any work, as the client (such as a page loaded into a browser) can be easily manipulated.
It is best to architect the code in such a way that all calls can be passed through a single authorization module that cannot be bypassed. That is the essence of complete mediation.
Instead of this:
#4: Cryptography/Cryptographic Agility
Cryptography is highly relevant to any software that holds sensitive data, PII, PHI, or anything that is beholden to industry standards such as PCI-DSS or HIPAA.
Since this data is extremely sensitive, it’s important for developers to understand which algorithms to use in which situation, as well as which algorithms are stronger than others.
Cryptographic algorithm choice is just the beginning when it comes to good secure software design. Cryptographic agility is even more important when designing secure software.
Cryptographic agility is a design principle that states that software should be free to change its cryptographic algorithms whenever it is needed. It’s a design principle because it requires you to think about this during the design phase, before you actually write any code.
A great example of this would be the .NET framework.
The .NET framework is built with cryptographic agility in mind. It also allows a developer to design with cryptographic agility. Below is a UML diagram of the .NET framework’s hashing implementation (source: blackhat.com).
Cryptographic agility comes into play in two ways with this design. First, there is a HashAlgorithm base class that each hashing algorithm class inherits. Essentially, when a new algorithm is developed, all that’s needed is a new class that inherits from HashAlgorithm (or SHA512 or SHA1, as the case may be).
Second, a developer using this namespace to develop his/her code can use the Create() method of the HashAlgorithm base class. This method takes a string parameter indicating which algorithm is desired, allowing the developer to write the application so a new implementation can easily be sourced in using a configuration file or a database record. Here is an example of what this code might look like (also from blackhat.com):
The Java JDK is actually structured in a very similar manner to allow for the same cryptographic agility, and Blackhat provides further examples on this structure.
There are two things to look out for that can cause issues when trying to implement cryptographic agility. First, if you use a configuration file or database to source in the algorithm, make sure to use least privilege and sufficiently protect it so that only an administrator can update it. The last thing you need is a hacker switching you to MD5 hashing and then breaking all of your hashes before you have a chance to react.
Second, carefully plan out your database column sizes for the encrypted or hashed data. Let’s say you originally built your database many years ago and you were using MD5 to hash data back then. If you only allocated 128-bits for the column size (the size of MD5 digests), then you will not be able to upgrade to SHA-256 or SHA-512 (at least, not easily).
There is a balance to be found, but try to keep a consistent buffer when planning column sizes. For instance, you may decide to allocate double the size of the digest so that new algorithms with longer digests can be accommodated without code or database changes.
#5: Threat Modeling
The final concept every developer should understand is threat modeling. I did a post a while back on this topic, so refer to that post for a more detailed discussion. Instead, I’d like to talk more here about why it is important for developers to understand threat modeling for security purposes.
As a software developer, it is your responsibility to make sure that your software, or the pieces of it that you write, are not used to cause harm to your or your client’s organization; you must ensure that your software is secure.
An important piece of this process is threat modeling. It involves modeling how an attacker might attack or misuse your software.
This assessment is done in four steps: looking at how data flows through your application, examining trust boundaries, identifying threats, and using attack trees to identify security controls and mitigations.
Threat modeling should happen toward the end of the design phase, when the design is just about finished. By that point, you can use these techniques to examine how your application appears to an attacker and determine the areas that need attention to prevent attacks.
Take for instance, the previous topics in this post of least privilege, complete mediation, and cryptographic agility. By themselves, these are abstract concepts and it may be difficult at first to see exactly where you need them.
If you have a basic design in place for your application, then threat modeling can really help to provide clear direction on how and where these concepts belong.
Do you depend heavily on a database and foresee SQL injection as a threat? Then least privilege will be useful.
Have you designed your solution with a single page application that is calling REST services to get all of its data? Do you foresee attackers trying to call those services without authorization? Then you’ll know where complete mediation comes into play.
Does your application require strong encryption to protect sensitive data? Then consider building cryptographic agility into your application to accommodate advances in the field of cryptography.
It is important to build security into the software from the start. It is much harder, and more expensive (not to mention less effective) to try to tack on security after software has already been built.
Protect Your Applications!
Application security is a big field, and it is getting more and more important as time goes on and more attacks come through applications. The five security concepts that we’ve discussed can help developers who are not application security experts build more secure software. To recap, they are:
- The CIA triad – What forms the foundation of security and what pieces are built on top of that foundation.
- Least privilege – Allow your application only the access it needs for only the time it needs it.
- Complete mediation – Don’t allow any code paths to bypass authorization.
- Cryptography/Cryptographic agility – Know your algorithms and design your application to switch them when necessary.
- Threat modeling – Understanding how bad guys may attack your software and design countermeasures.
Attackers will be looking for weakness in your software. Building in security is the only way to effectively defend against detrimental attacks. Use these five concepts to make security a part of your daily work and produce more secure software.
Why? Because secure code is good code.