A More Secure Development Lifecycle IV: Secure Design Techniques
In my previous posts, I identified security requirements that needed to be addressed in any software development project. I also wrote about the techniques that can be used to gather security requirements. In this post, I will give an overview of the design considerations for a software project.
The design phase of software development involves deciding how to implement the identified requirements. I will describe several design considerations for some of the requirements discussed in my previous posts. Mano Paul’s book Official (ISC)2 Guide to the CSSLP provides guidance on secure design for security requirements that I do not cover here.
If you identified confidentiality as a requirement, you may want to use cryptographic techniques to help secure your data. When implementing cryptography, the goal is to make an attacker have to use a lot of effort to break the encryption. A large key size makes it harder for an attacker to break the encryption. The key itself should be protected to ensure that an attacker does not obtain it. Key Rotation is another process that should be defined. If a key is replaced, any archived data will be unusable so a process needs to be created to decrypt and encrypt the data with the new key.
There are two types of encryption algorithms, symmetric and asymmetric. Symmetric algorithms have one key that is used for both encryption and decryption. This type of algorithm is fast and efficient for protecting large amounts of data, but there are several problems with it. First, the key exchange between both parties has to be handled securely, which means you’ll need an out-of-band process. This type of cryptography is also not scalable since each sender and recipient has a key. This can cause a key management problem as the number of keys increases.
Asymmetric encryption is commonly known as public key encryption. It involves two keys, a private key and a public key. The private key is kept in a secure location; the public key is provided to anyone who wants to encrypt data for the holder of the private key. This type of cryptography also provides non-repudiation since the private key can be used to sign and the public key to verify the signature, meaning the origin of the data is always verified. Since asymmetric encryption is slower than symmetric, it is not appropriate for protecting large amounts of data.
You should use these differences to determine which kind of encryption best fulfills your confidentiality requirements. You may use a combination of the two due to the differences in the data you want to protect. If you are exchanging customer data with a single vendor, symmetric encryption would be a better choice. If you are exchanging small amounts of data with several customers, asymmetric encryption would be appropriate. Most importantly, do not write your own encryption algorithm; use an industry-tested algorithm and be aware of its status. The one you are using can become compromised over time.
Integrity requirements demand that there are no unauthorized changes to the data or software. There are several design implementations for this requirement. The first is hashing, which is used to verify that data has not been changed. When data is transferred, a hash of the data is sent with it. The receiver hashes the data and compares it to the hash value that was sent. If the hash values do not match, the data has been changed. Since the data will be visible to anyone who intercepts the data transfer, this process should only be used when confidentiality is not a requirement.
Hash values cannot be reversed, but they can be compared to a table of hash values to find a match. Add a salt value to the data before it is hashed to make these tables less effective.
Another integrity design option is using referential integrity on database tables in a relational database. A relational database could have a customer table that is linked to an order table by the customer number. The customer table would have one record per customer while the order table would have several records per customer. If a customer record is deleted, the order table could have records that do not refer to a customer. These are called orphaned records. Referential integrity makes sure that the references in a database table remain intact even after a deletion.
Resource locking on databases is also used to ensure integrity; this forces all data on a table to be updated as a group. This has to be designed carefully to make sure that locking records does not result in deadlock conditions.
The last integrity technique is code signing. The code is digitally signed so that the origin of the code can be confirmed. This is especially important with mobile code since it is downloaded from a remote location.
There are several design principles that should be built into every software application. I will touch on some of the principles in this post. You can read Mano Paul’s book Official (ISC)2 Guide to the CSSLP for more design principles and an in-depth look at the principles I mention in the blog post.
The first principle is Least Privilege. Building this principle into the design of the software means taking access rights into consideration when designing the application’s components. Designing access controls during the component design phase means the controls can be verified in the testing phase. Many SDLC processes add access controls right before deployment to production which means that additional access may have to be granted to make the software function. Many times administrator privileges are granted to allow the component to function so that the implementation is not delayed. There may be good intentions to restrict access later but the task could be overlooked. Unfortunately, granting expanded access could expose vulnerabilities in the application.
Defense in Depth is the principle of layered security. If an attacker is able to breach one layer, he or she will be stopped at the next layer. An example of Defense in Depth is using input validation and prepared statements for database access to defend against injection attacks. If the attacker is able to circumvent the input validation, the prepared statements will not allow the attack code to be executed.
Fail Secure is the principle that any failure in the software does not allow the user to continue using the software. For example, the user is denied access by default and the account is locked after a certain number of login attempts. I once encountered a website that I had not used in a while. As I was trying to remember my password, I realized that I had failed to enter the correct password three times but was still allowed in the site. Fail Secure would have locked me out of the application.
Another design principle is Economy of Mechanisms. This means that the more complex the design of the application, the more likely there will be vulnerabilities due to a larger attack surface. Modular programming supports the principle of economy of mechanisms since code reuse is a part of the design. If code is repeated throughout the application, it is far more likely that there will be bugs that can be exploited. Single Sign-on is also an example of Economy of Mechanisms since it simplifies user authentication.
One of the design processes that help to build security into an application is threat modeling. Threat modeling is an iterative process that helps identify the points an attacker would target in an application. Threat modeling focuses on entry and exit points since that is point where an attacker could gain access to the application.
One of the benefits of threat modeling is that design flaws can be identified and dealt with in the design phase. The threat model can also be used by the testing team to identify which tests are needed to test the security of the application. Some challenges of threat modeling are that it can be time consuming and it needs to be a part of a mature SDLC. It also needs trained employees to perform the modeling.
An example of a threat model is displayed below. The trust boundaries are shown in red. These boundaries show where an attacker could compromise the application. The threat model also uses the STRIDE framework to classify threats. STRIDE stands for Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, and Elevation of privilege. After the type of threat is identified, mitigation strategies can be developed.
This example of a threat model is from an article by Chas Jeffries of Microsoft entitled Threat Modeling and Agile Development Practices. Microsoft also publishes a book by Michael Howard and Steve Lipner entitled The Security Development Lifecycle: A Process for Developing Demonstrably More Secure Software that provides a good description of the threat modeling process. Mano Paul’s Official (ISC)2 Guide to the CSSLP also provides a good description of the process.
Completing the Design
After identifying threats and mitigation strategies, you should determine what kind of architecture the application will have. There are many different architectures with each one exhibiting benefits and challenges. Some examples of architectures are distributed computing, service oriented architecture, and software as a service. Comparing the benefits and challenges against the requirements and design choices helps to facilitate choosing an architecture. Many times, the architecture is chosen at the beginning of the project instead of allowing the choice to be determined by the requirements of the application.
After all of these design tasks have been completed, you should have a secure design and architecture review. The functionality of the application and the security design should be reviewed. The deployment environment should also be included in the review to make sure that the network design functions correctly with the chosen architecture. Defense in Depth should be verified in the design along with confidentiality, integrity, and availability. To minimize recoding, the review should be completed and signed off on before coding starts.
As my four posts have demonstrated, adding security to the SDLC involves more than using security frameworks. I would encourage you to read the books and blog posts I have referenced and engage experienced professionals if you are starting this process.