Posted: June 19th, 2022
Download and read the document and answer all questions in the document. Please see attached document H5 & APA Criteria doc.
Defensive Coding Practices
In this chapter you will
•Learn the role of defensive coding in improving secure code
•Explore declarative vs. programmatic security
•Explore the implications of memory management and security
•Examine interfaces and error handling
•Explore the primary mitigations used in defensive coding
Secure code is more than just code that is free of vulnerabilities and defects. Developing code that will withstand attacks requires additional items, such as defensive coding practices. Adding in a series of controls designed to enable the software to operate properly even when conditions change or attacks occur is part of writing secure code. This chapter will examine the principles behind defensive coding practices.
Declarative vs. Programmatic Security
Security can be instantiated in two different ways in code: in the container itself or in the content of the container. Declarative programming is when programming specifies the what, but not the how, with respect to the tasks to be accomplished. An example is SQL, where the “what” is described and the SQL engine manages the “how.” Thus, declarative security refers to defining security relations with respect to the container. Using a container-based approach to instantiating security creates a solution that is more flexible, with security rules that are configured as part of the deployment and not the code itself. Security is managed by the operational personnel, not the development team.
Imperative programming, also called programmatic security, is the opposite case, where the security implementation is embedded into the code itself. This can enable a much greater granularity in the approach to security. This type of fine-grained security, under programmatic control, can be used to enforce complex business rules that would not be possible under an all-or-nothing container-based approach. This is an advantage for specific conditions, but it tends to make code less portable or reusable because of the specific business logic that is built into the program.
The choice of declarative or imperative security functions, or even a mix of both, is a design-level decision. Once the system is designed with a particular methodology, then the secure development lifecycle (SDL) can build suitable protections based on the design. This is one of the elements that requires an early design decision, as many other elements are dependent upon it.
Bootstrapping refers to the self-sustaining startup process that occurs when a computer starts or a program is initiated. When a computer system is started, an orchestrated set of activities is begun that includes power on self-test (POST) routines, boot loaders, and operating system initialization activities. Securing a startup sequence is a challenge—malicious software is known to interrupt the bootstrapping process and insert its own hooks into the operating system.
When coding an application that relies upon system elements, such as environment variables like path, care must be taken to ensure that values are not being changed outside the control of the application. Using configuration files to manage startup elements and keeping them under application control can help in securing the startup and operational aspects of the application.
Cryptography is a complex issue, and one that changes over time as weaknesses in algorithms are discovered. When an algorithm is known to have failed, as in the case of Data Encryption Standard (DES), MD5, RC2, and a host of others, there needs to be a mechanism to efficiently replace it in software. History has shown that the cryptographic algorithms we depend upon today will be deprecated in the future. Cryptography can be used to protect confidentiality and integrity of data when at rest, in transit (communication), or even in some cases when being acted upon. This is achieved through careful selection of proper algorithms and proper implementation.
Cryptographic agility is the ability to manage the specifics of cryptographic function that are embodied in code without recompiling, typically through a configuration file. Most often, this is as simple as switching from an insecure to a more secure algorithm. The challenge is in doing this without replacing the code itself.
Producing cryptographically agile code is not as simple as it seems. The objective is to create software that can be reconfigured on the fly via configuration files. There are a couple of ways of doing this, and they involve using library calls for cryptographic functions. The library calls are then abstracted in a manner by which assignments are managed via a configuration file. This enables the ability to change algorithms via a configuration file change and a program restart.
Cryptographic agility can also assist in the international problem of approved cryptography. In some cases, certain cryptographic algorithms are not permitted to be exported to or used in a particular country. Rather than creating different source-code versions for each country, agility can allow the code to be managed via configurations.
Cryptographic agility functionality is a design-level decision. Once the decision is made with respect to whether cryptographic agility is included or not, then the SDL can build suitable protections based on the design. This is one of the elements that requires an early design decision, as many other elements are dependent upon it.
EXAM TIP When communications between elements involve sessions—unique communication channels tied to transactions or users—it is important to secure the session to prevent failures that can cascade into unauthorized activity. Session management requires sufficient security provisions to guard against attacks such as brute-force, man-in-the-middle, hijacking, replay, and prediction attacks.
Handling Configuration Parameters
Configuration parameters can change the behavior of an application. Securing configuration parameters is an important issue when configuration can change programmatic behaviors. Managing the security of configuration parameters can be critical. To determine the criticality of configuration parameters, one needs to analyze what application functionality is subject to alteration. The risk can be virtually none for parameters of no significance to extremely high if critical functions such as cryptographic functions can be changed or disabled.
Securing critical data such as configuration files is not a subject to be taken lightly. As in all risk-based security issues, the level of protection should be commensurate with the risk of exposure. When designing configuration setups, it is important to recognize the level of protection needed. The simplest levels include having the file in a directory protected by the access control list (ACL); the extreme end would include encrypting the sensitive data that is stored in the configuration file.
Configuration data can also be passed to an application by a calling application. This can occur in a variety of ways—for example, as part of a URL string or as a direct memory injection—based on information provided by the target application. Testing should explore the use of URLs, cookies, temp files, and other settings to validate correct handling of configuration data.
Memory management is a crucial aspect of code security. Memory is used to hold the operational code, data, variables, and working space. Memory management is a complex issue because of the dynamic nature of the usage of memory across a single program, multiple programs, and the operating system. The allocation and management of memory is the responsibility of both the operating systems and the application. In managed code applications, the combination of managed code and the intermediate code execution engine takes care of memory management, and type safety makes the tasking easier. Memory management is one of the principal strengths of managed code. Another advantage of managed code is the automatic lifetime control over all resources. Because the code runs in a sandbox environment, the runtime engine maintains control over all resources.
In unmanaged code situations, the responsibility for memory management is shared between the operating system and the application, with the task being even more difficult because of the issues associated with variable type mismatch. In unmanaged code, virtually all operations associated with resources and memory are the responsibility of the developer, including garbage collection, thread pooling, memory overflows, and more. As in all situations, complexity is the enemy of security.
Type safety is the extent to which a programming language prevents errors resulting from different data types in a program. Type safety can be enforced either statically at compile time or dynamically at runtime to prevent errors. Type safety is linked to memory safety. Type-safe code will not inadvertently access arbitrary locations of memory outside the expected memory range. Type safety defines all variables, and this typing defines the memory lengths. One of the results of this definition is that type-safe programming resolves many memory-related issues automatically.
Locality is a principle that given a memory reference by a program, subsequent memory accesses are often predictable and are in close proximity to previous references. Buffer overflows are a significant issue associated with memory management and malicious code. There are various memory attacks that take advantage of the locality principle. There are also defenses against memory corruption based on locality attacks. Address Space Layout Randomization (ASLR) is a specific memory management technique developed by Microsoft to defend against locality attacks.
No application is perfect, and given enough time, they will all experience failure. How an application detects and handles failures is important. Some errors are user driven; some can be unexpected consequences or programmatic errors. The challenge is in how the application responds when an error occurs. This is referred to as error handling. The specific coding aspect of error handling is referred to as exception management.
When errors are detected and processed by an application, it is important for the correct processes to be initiated. If logging of critical information is a proper course of action, one must take care not to expose sensitive information such as personally identifiable information (PII) in the log entries. If information is being sent to the screen or terminal, then again, one must take care as to what is displayed. Disclosing paths, locations, passwords, userids, or any of a myriad of other information that would be useful to an adversary should be avoided.
Exception management is the programmatic response to the occurrence of an exception during the operation of a program. Properly coded for, exceptions are handled by special functions in code referred to as exception handlers. Exception handlers can be designed to specifically address known exceptions and handle them according to pre-established business rules.
There are some broad classes of exceptions that are routinely trapped and handled by software. Arithmetic overflows are a prime example. Properly coded for, trapped, and handled with business logic, this type of error can be handled inside software itself. Determining appropriate recovery values from arithmetic errors is something that the application is well positioned to do, and something that the operating system is not.
Part of the development of an application should be an examination of the ways in which the application could fail, and also the correct ways to address those failures. This is a means of defensive programming, for if the exceptions are not trapped and handled by the application, they will be handled by the operating system. The operating system (OS) does not have the embedded knowledge necessary to properly handle the exceptions.
Exceptions are typically not security issues—however, unhandled exceptions can become security issues. If the application properly handles an exception, then ultimately through logging of the condition and later correction by the development team, rare, random issues can be detected and fixed over the course of versions. Exceptions that are unhandled by the application or left to the OS to handle are the ones where issues such as privilege escalation typically occur.
Application programming interfaces (APIs) define how software components are connected to and interacted with. Modern software development is done in a modular fashion, using APIs to connect the functionality of the various modules. APIs are significant in that they represent entry points into software. The attack surface analysis and threat model should identify the APIs that could be attacked and the mitigation plans to limit the risk. Third-party APIs that are being included as part of the application should also be examined, and errors or issues be mitigated as part of the SDL process. Older, weak, and deprecated APIs should be identified and not allowed into the final application.
On all interface inputs into your application, it is important to have the appropriate level of authentication. It is also important to audit the external interactions for any privileged operations performed via an interface.
There are a set of primary mitigations that have been established over time as proven best practices. As a CSSLP, you should have these standard tools in your toolbox. An understanding of each, along with where and how it can be applied, is essential knowledge for all members of the development team. These will usually be employed through the use of the threat report. The standard best practice–based primary mitigations are as follows:
•Lock down your environment.
•Establish and maintain control over all of your inputs.
•Establish and maintain control over all of your outputs.
•Assume that external components can be subverted and your code can be read by anyone.
•Use libraries and frameworks that make it easier to avoid introducing weaknesses.
•Use industry-accepted security features instead of inventing your own.
•Integrate security into the entire software development lifecycle.
•Use a broad mix of methods to comprehensively find and prevent weaknesses.
Defensive coding is not a black art; it is merely applying the materials detailed in the threat report. Attack surface reduction, an understanding of common coding vulnerabilities, and standard mitigations are the foundational elements of defensive coding. Additional items in the defensive coding toolkit include code analysis, code review, versioning, cryptographic agility, memory management, exception handling, interface coding, and managed code.
EXAM TIPConcurrency is the process of two or more threads in a program executing concurrently. Concurrency can be an issue when these threads access a common object, creating a shared object property. Should they change the state of the shared object, the conditions for a race condition apply. Controlling concurrency is one method of controlling for race conditions.
EXAM TIPTo maintain the security of sensitive data, a common practice is tokenization. Tokenization is the replacement of sensitive data with data that has no external connection to the sensitive data. In the case of a credit card transaction, for example, the credit card number and expiration date are considered sensitive and are not to be stored, so restaurants typically print only the last few digits with XXXXs for the rest, creating a token for the data, but not disclosing the data.
Learning from Past Mistakes
Software engineering is not a new thing. Nor are security issues. One of the best sources of information regarding failures comes from real-world implementation errors in the industry. When company ABC makes the news that it has to remediate a security issue, such as a back door in a product left by the development team, this should be a wake-up call to all teams in all companies.
Errors are going to happen. Mistakes and omissions occur. But to repeat problems once they are known is a lot harder to explain to customers and management, especially when these errors are of significant impact and expensive to remediate, both for the software firm and the customer. Learning from others and adding their failures to your own list of failures to avoid is a good business practice.
Part of the role of the security team is keeping the list of security requirements up to date for projects. Examining errors from other companies and updating your own set of security requirements to prevent your firm from falling into known pitfalls will save time and money in the long run.
This chapter opened with an analysis of the differences between declarative and programmatic security. An examination of bootstrapping, cryptographic agility, and secure handling of configuration parameters followed suit. Memory management and the related issues of type-safe practices and locality were presented. Error handling, including exception management, was presented as an important element in defensive coding. The security implications of the interface coding associated with APIs was presented. The chapter closed with an examination of the primary mitigations that are used in defensive coding.
•Declarative security refers to defining security relations with respect to the container.
•Programmatic security is where the security implementation is embedded into the code itself.
•Cryptographic agility is the ability to manage the specifics of cryptographic function that are embodied in code without recompiling, typically through a configuration file.
•Securing configuration parameters is an important issue when configuration can change programmatic behaviors.
•Memory management is a crucial aspect of code security.
•In managed code applications, the combination of managed code and the intermediate code execution engine takes care of memory management, and type safety makes the tasking easier.
•In unmanaged code situations, the responsibility for memory management is shared between the operating system and the application, with the task being even more difficult because of the issues associated with variable type mismatch.
•Type-safe code will not inadvertently access arbitrary locations of memory outside the expected memory range.
•Locality is a principle that, given a memory reference by a program, subsequent memory accesses are often predictable and are in close proximity to previous references.
•Exception management is the programmatic response to the occurrence of an exception during the operation of a program.
•APIs are significant in that they represent entry points into software.
•A set of primary mitigations have been established over time as proven best practices.
Defensive coding practices is one of the most critical proactive security countermeasures in SDLC. If software developers follow certain security best-practices, most of the weaknesses can be eliminated. In this module’s readings, you looked at defensive tactics used in the development of software. You also learned OWASP proactive controls.
Extract defensive coding practices from Chapter 13 of the Conklin& Shoemaker. Explain each coding practice in one short paragraph.
For each coding practice, describe a corresponding CWE (https://cwe.mitre.org/) and OWASP proactive control (https://owasp.org/www-project-proactive-controls/)
Place an order in 3 easy steps. Takes less than 5 mins.