Domain 5: Secure Software Implementation

Defensive Coding Techniques and Secure Design Principles

The four main risk management strategies are:

  • Remediation: Fix the underlying issue to eliminate the risk. This is the preferred and most effective solution.

  • Mitigation: Implement security controls that reduce the risk; however, some residual risk may remain.

  • Transference: Transferring responsibility for the risk to another party, such as the user, an insurance provider, etc.

  • Acceptance: Doing nothing and accepting the results of the risk if it occurs

Declarative security is a container-based approach that defines the “what” of security and leaves the “how” to the operations team. This results in more flexible security that is tailored to the deployment environment.

Imperative security defines the “how” of security. This provides more granular security management and is better suited to implementing complex business rules. However, it is less portable and reusable.

Type safety refers to a programming language’s ability to avoid errors caused by the use of different types of variables in an application. For example, type-safe code encodes the lengths of variables, preventing accidental access of data outside of the bounds of a variable.

Locality refers to the fact that references close together in code are also often close together in memory. Buffer overflows are an example of an exploit that takes advantage of this fact.

Bootstrapping is the process by which a computer or application starts up based on the knowledge of where startup scripts, etc. are located. It is potentially vulnerable to malicious scripts or modifications to configuration files.

Cryptographic agility refers to the ability to swap out cryptographic code as needed if a particular algorithm is broken. The goal is to avoid rewriting or recompiling code, and the process often relies on configuration files that specify the cryptographic library functions to use.

The /SAFEH switch supports safe exception handling.

The /GS compiler flag can protect against some stack overflow attacks.

System-of-systems integration involves integrating multiple systems into a single, functioning whole.

Trust contracts define trust requirements for any data crossing a trust boundary.

Error handling is how an application responds when something unexpected occurs.

Exception management is writing code to implement error handling.

Defense in depth involves using multiple security controls together to provide protection even if one fails. Some examples include:

  • Injection Protection: Input validation, output encoding, and prepared statements are all protections against various injection attacks. Combining two or more further decreases the probability of a successful output.

  • XSS Protection: Cross-site scripting involves injecting malicious scripts into a webpage. Blocking active scripts, encoding outputs, and validating input can strengthen XSS protections.

  • Security Zones: Security zones segment networks, software, etc. based on access controls. Security solutions deployed on zone boundaries have another chance to identify a threat before it causes harm to the organization.

The principle of economy of mechanism states that software should be designed to be as simple as possible, making it easier to understand and reducing its attack surface. This is accomplished by:

  • Removing Unnecessary Functionality: Additional "bells and whistles" features may improve the user experience, but they also create more opportunities for security flaws or bugs

  • Keeping Security Simple: Security controls should be as simple as possible to ensure complete operation and minimize the chance of bugs

  • Ease of Use: Secure Sign On (SSO) and similar enhancements to the user experience reduce the probability that a user will try to bypass security mechanisms and eliminate high-risk authentication code

The principle of least privilege states that users, software, etc. should only have the access and permissions needed to do their jobs. Some means of enforcing least privilege include:

  • Modular Programming: Modular programming involves breaking an application into many pieces, each with a unique, simple purpose. Modularity makes software easier to troubleshoot and maintain and can enforce least privilege since each module has limited access and permissions.

  • Non-Admin Accounts: Some users will need to perform privileged actions, which require privileged accounts. However, these privileged accounts should only be used when needed, with less privileged accounts used for all other actions.

Split keys and role separation are security controls for separation of duties. Account logouts help software to fail to a secure state.

Separation of duties focuses on breaking privileged actions into multiple steps that must be performed by different parties. Security controls associated with separation of duties include:

  • Split Keys: Dividing an encryption key across multiple locations prevents a single compromised system or account from leaking sensitive data.

  • Role Separation: During the development process, a programmer should not be part of the testing and quality assurance team responsible for reviewing their own code.

Software that fails secure defaults to a secure state in the event of an issue but can be easily restored to normal operations. Design mechanisms that support this include:

  • Default Deny: Access requests to corporate resources should be denied by default until a user or application demonstrates the necessary permissions.

  • Account Lockouts: After a certain number of failed access attempts, a user account should be locked to protect against brute force attacks.

  • Error Handling: Software should be designed to explicitly handle errors and not produce overly verbose error messages that reveal the internal workings of the application.

Complete mediation requires that access controls be applied every time, even to follow-on requests within a session. Related security controls include:

  • Code Path Analysis: During the design phase, all code paths that require elevated permissions or sensitive data should be identified. These code paths should all pass through a single interface that applies access controls and other policy enforcement.

Removing unnecessary functionality and ease of use are related to economy of mechanism. Error handling is related to failing securely.

Anti-tampering solutions are designed to protect software against malicious modifications. Some common techniques include:

  • Code Signing: Valid digital signatures can only be created with knowledge of the appropriate private key. Signing the application's code ensures that it is authentic and has not been modified since the signature was generated.

  • Version Control/Revision Control: Version control systems (like git) record every change to software. These systems allow comments to be applied to commits, limitations on who can commit, comparisons between versions, and reversion to a previous version of a file or release.

  • Obfuscation: Code obfuscation is designed to make production code more difficult to decompile and understand. Obfuscation can help to protect against tampering because it makes it more difficult to determine where and how to change the code.

Encryption is not a common anti-tampering solution because encrypted code can't be executed.

Examples of common cryptographic failures include:

  • Hard-Coded Credentials: Authentication information (passwords, keys, etc.) hard-coded into an application is vulnerable to exposure and difficult to change. This creates failures in authentication and enables account takeover.

  • Missing Encryption of Sensitive Data: Certain types of sensitive data should be encrypted to protect the business and its customers and to comply with applicable regulations. Log files, error logs, and backups are common examples of locations where this issue occurs.

  • Use of a Broken or Risky Cryptographic Algorithm: DES, MD5, SHA-1, and other algorithms are considered broken and insecure. Using broken or custom encryption algorithms leaves data vulnerable to exposure. Additionally, the use of a weak random number generator (RNG) to generate keys is a common problem.

  • Download of Code without Integrity Check: Code downloaded from the Internet may be malicious or modified by an attacker. All downloaded code should be compared to the provided hash value or digital signature to ensure that it has not been tampered with.

  • Unsalted Hash: Hash functions always provide the same output for a given input. This can be problematic for password management since it reveals which accounts have the same password and makes it possible to use precomputed rainbow tables to crack weak passwords. Salting hashes by including a random value as part of the input ensures that identical passwords produce different hashes and protects against the use of rainbow tables.

Password Attacks

Birthday attacks take advantage of the fact that it is much more probable that any two members of a group have a collision (i.e., the same birthday) than that a particular member of the group has a collision with another member.

Dictionary attacks use a list of inputs (such as common passwords) to try to guess the input that produced a particular hash.

Salting involves adding a random, public value to the input to a hash algorithm (such as a password) so that identical inputs (such as identical passwords) do not produce the same hash.

Rainbow tables are precalculated lookup tables that map hash inputs to outputs and are designed to make future attacks faster. Salting can protect against rainbow attacks.


Software Security Analysis

A few different types of tools exist for software security analysis. These include:

  • Static Application Security Testing (SAST): SAST or static analysis tools analyze the source code of an application for vulnerabilities. Since they use source code, they can be applied earlier in the SDLC than other tools that require a running application. Additionally, they provide better test coverage and can pinpoint an error within an application’s code. However, SAST tools are language-specific and cannot identify some types of vulnerabilities that are only detectable in running code.

  • Dynamic Application Security Testing (DAST): DAST or dynamic analysis tools test a running application for vulnerabilities by sending it malicious or anomalous inputs and analyzing its behavior or responses. DAST can be cheaper than SAST, often has fewer false positives, and can identify issues that are only apparent at runtime. However, it has poorer code coverage, cannot pinpoint where an issue exists within the code (only that it does exist), and requires a running application (making it only usable later in the SDLC).

  • Interactive Application Security Testing (IAST): IAST solutions use instrumentation to gain internal visibility of a running application while running tests against it. IAST solutions can pinpoint vulnerabilities in an application and are more easily integrated into CI/CD pipelines. However, IAST can be more expensive, slows code execution, and is a less mature solution.

  • Runtime Application Self-Protection (RASP): RASP uses instrumentation to monitor and protect an application in production. Based on visibility into inputs, outputs, and application behavior, RASP can identify and block even zero-day attacks against an application. However, RASP does increase the size and complexity of the application that it protects.

  • Software composition analysis (SCA): provides visibility into the collection of third-party libraries and modules used by an application. This can help with vulnerability detection, license management, and similar functions.


Code and Peer Reviews

Code review is a process by which other developers inspect code for security or efficiency issues. Some of the common checks performed during code review include:

  • Inefficient Code: Complex or obfuscated code may need to be simplified to improve analysis or execution.

  • Known Vulnerabilities: Code should be checked against the OWASP Top 10, SANS Top 25, and errors that have previously been found within an organization's code.

  • Errors and Exception Handling: Code should fully test for error cases and handle all possible exceptions.

  • Injection Flaws: Code should include input validation to protect against injection attacks.

  • Cryptographic Strength: Cryptography should be implemented using trusted algorithms and libraries and use strong random number generation.

  • Unsafe and Deprecated Function Calls: Code should only use approved functions and APIs, and unneeded functions should be removed.

  • Privilege Levels: Code should be implemented in accordance with the principle of least privilege.

  • Logging: Code should properly log errors without revealing unnecessary information.

  • Secure Key Information: Cryptographic keys, passwords, and other authentication information should be properly used and protected.

Infinite loops can occur when unhandled states occur in conditional logic. For example, code designed to read until it finds a particular letter could read forever if presented with all-numeric input.

Race condition vulnerabilities can occur if multiple threads of execution can read/write values at the same time. For example, two threads may update a value simultaneously, causing one update to overwrite the other.

Mutual exclusion occurs when race condition protections cause thread deadlock. If thread A is waiting for thread B to perform action X before it performs action Y and thread B will only perform X if thread A performs Y, then neither can execute.

Recursion is when a function within an application calls itself again.

Common Vulnerabilities and Controls

The OWASP Top Ten is a list of some of the most common and impactful vulnerabilities in web applications.

The SANS Top 25 Software errors lists some of the most common errors that affect software in general.

The Common Weaknesses Enumeration (CWE), maintained by MITRE, classifies the various types of errors that can occur in software.

The Common Vulnerabilities and Exposures (CVE) list, also maintained by MITRE, describes specific vulnerabilities that have been identified in a particular application.

Vulnerabilities

Injection is a major, common vulnerability that usually features highly on vulnerability lists. Some common types of injection vulnerabilities include:

  • SQL Injection: SQL injection attacks involve providing malicious input that is included in a database request. SQL injection can read, write, or delete data contained within a database accessible to a vulnerable application.

  • Command Injection: Command injection vulnerabilities allow an attacker to run commands in the system terminal. For example, an application may run a command in the shell using user-provided input, which may be crafted to change the intent of the command or run additional commands.

  • Integer Overflow: Integers have a fixed size in memory and are only able to store a certain range of values. If a value to be stored in a variable exceeds this range, it wraps around and is interpreted as a smaller value.

  • Path Traversal: In a filepath, ../ indicates that the system should look in the next directory up in the file system. Path traversal vulnerabilities allow an attacker who can specify the name of a file to be read/written by an application to read/write files outside of the intended directory.

  • Cross-Site Scripting (XSS): Modern webpages use scripts to add interactivity and other functionality to webpages. If user-provided input is used as part of a webpage's HTML code, a malicious user can have part of their input interpreted as a script, which will be run in the browser of anyone visiting the page. Injection vulnerabilities can be non-persistent/reflected, persistent/reflected, or DOM-based.

  • Cross-Site Request Forgery (CSRF): Cross-site request forgery (CSRF) attacks involve tricking the browser of an authenticated user into performing an HTTP request without their knowledge/consent. For example, a user logged into social media could have their password changed if a malicious webpage tricked their browser into performing a password change request and the social media site lacked CSRF protections.

Poor input validation is the cause of many of the most common vulnerabilities. Some common errors include:

  • Buffer Overflows: Buffer overflow vulnerabilities occur when a program attempts to write more data to a memory location than fits in the allocated space. Buffer overflows can be exploited to overwrite critical data stored in memory or execute malicious code.

  • Canonical Form: Data can be encoded in various ways, such as URL or Base64 decoding, and software commonly converts it to canonical form before processing it. This makes it possible for an attacker to bypass input validation schemes that inspect the data before it is canonicalized.

  • Missing Defense Functions: User authentication and authorization functions help to control access to privileged functionality and sensitive data. If these defenses are missing or can be bypassed, it places application security at risk.

  • Output Validation Failures: The output from one function or application may be used as input by another. Application output should also be sanity-checked for errors that could cause problems down the line.

Controls

File integrity monitoring (FIM) ensures that configuration files or other important data have not been tampered with before use via a hash value, checksum, or digital signature.

Watchdogs check for a specific issue in an application. For example, time to live (TTL) values in packets protect against traffic endlessly looping through the network.

Last updated