A method to assess ‘forgivable’ vs ‘unforgivable’ vulnerabilities

This section provides an overview of how we determined the Implementation Score for each of the primary mitigations. The mitigations are organized according to their complexity, starting with those that are easiest to implement.

Input Validation

The OWASP Cheat Sheet (OWASP, 2021) details various strategies for input validation:

  • Utilization of data type validators that are natively provided by web application frameworks (such as Django Validators and Apache Commons Validators)

  • Validation against JSON Schema and XML Schema (XSD) for inputs in these formats.

  • Type conversion (for example, Integer.parseInt() in Java, int() in Python) with strict exception handling protocols.

Input Validation falls under the Libraries or Frameworks mitigation category. However, its significant presence in the mitigation list justifies a separate mention.

Score Evidence
Cost 1 Minimal or no direct and indirect costs
Knowledge 1 Well understood and widely available
Feasibility 1 Available implementations for most programming languages
Total 3 (Easy)

Output Encoding

Output encoding and escaping are critical defensive techniques designed to mitigate injection attacks. Output encoding translates special characters into a safe representation that is harmless to the target interpreter, such as converting the < character to the < string when adding content to an HTML page.

Escaping involves prepending a special character to prevent misinterpretation; for instance, adding a character before a " (double quote) so it is recognized as text rather than the end of a string.

Output Encoding is categorized under Libraries or Frameworks mitigation, yet its dominance in the security strategies warrants individual recognition.

Score Evidence
Cost 1 Minimal or non-existent direct costs
Knowledge 1 Broadly understood and readily available
Feasibility 1 Numerous implementations exist for all programming languages
Total 3 (Easy)

Reduce the Attack Surface

The term “attack surface” refers to the aggregate number of input and output points that may be vulnerable to exploitation by an attacker. A broader attack surface increases the potential for attack and the likelihood of introducing vulnerabilities. At times, this measure may also indicate other aspects of quality in addition to security. For instance, an application with numerous entry and exit points might necessitate extensive testing to enhance code coverage. This could also encompass the use of functions, libraries, and frameworks.

The attack surface is defined as the collective code, interfaces, services, protocols, and practices accessible to users, with a focus on what unauthenticated users can access (Microsoft, 2019). Thus, developers should aim to minimize the application’s exposure early in the development cycle, particularly during the design phase, and should continue to assess potential reductions during coding and runtime.

Score Evidence
Cost 2 Removing functionalities will require time and incurr costs
Knowledge 1 The concept is commonly recognized and understood
Feasibility 2 Technically achievable, though justifying the removal of functionality may present challenges
Total 5 (Medium)

Enforcement by Conversion

This approach entails converting input into a controlled and distinct representation. For instance, in PHP, one common method to prevent SQL injection is applying intval() to numeric inputs, thereby ensuring the result is a number.

Score Evidence
Cost 1 Typically integrated into the language
Knowledge 3 Not widely adopted or well understood
Feasibility 1 Does not depend on any prerequisites
Total 5 (Medium)

Sandbox or Jail

This method involves executing code within a ‘sandbox’ or a similar constrained environment that enforces rigid boundaries between the process and the operating system. This setup can effectively limit which files may be accessed in a specific directory or the commands that the software can execute.

Examples on the operating system level include the Unix chroot jail, AppArmor, and SELinux. Generally, managed code can offer certain protections. For example, java.io.FilePermission in Java SecurityManager allows software to impose constraints on file operations.

However, this solution may not always be sustainable, restricting only the operating system’s impact while other parts of the application may still be compromised.

Score Evidence
Cost 1 Typically included with the language/OS
Knowledge 2 The concept is partially known and understood
Feasibility 2 Technically viable, yet poses limitations
Total 5 (Medium)

Secure Programming

This expansive topic primarily falls within developers’ responsibilities. Examples of secure programming include:

  • Setting pointers to NULL after freeing them
  • Conducting sanity checks on all modifiable pointers before use to prevent NULL pointer dereferences
  • Strictly defining protocols to easily identify all out-of-bounds behavior and adhering to the protocol’s specifications
  • Creating a new object during data deserialization rather than merely deserializing it
  • Explicitly defining a final object() to prevent deserialization
  • Utilizing library calls instead of external processes to recreate desired functionalities
Score Evidence
Cost 2 Requires skilled developers
Knowledge 2 The concept is partially understood and known
Feasibility 2 Technically practical with few prerequisites
Total 6 (Medium)

Compilation or Build Hardening

Hardening the toolchain involves examining four critical areas: configuration, preprocessor, compiler, and linker. The development environments and developers themselves form a part of the software supply chain, so any compromise of their accounts can lead to attackers gaining control over segments of this supply chain. Many developers are now working from home, making them potential entry points for malicious code or the theft of credentials associated with production services. Thus, hardening now also encompasses strengthening the security of the tools required for their tasks. This includes source code management (SCM) tools, binary artifacts, and CI/CD pipelines.

Build hardening entails the use of automated mechanisms for buffer overflow detection, provided by certain compilers or compiler extensions, such as Microsoft Visual Studio /GS flag, and others that offer mechanisms for monitoring and validation. This also includes features like Control-flow Enforcement Technology (CET) Shadow Stack, which defends against return-oriented programming (ROP) attacks.

Score Evidence
Cost 1 Typically included in the toolchain
Knowledge 2 Not extensively understood
Feasibility 3 May affect performance and typically depends on the compiler or architecture
Total 6 (Medium)

Separation of Privilege

Separation of privilege, also called privilege separation, pertains to the division of user privileges across multiple separate accounts, as well as the compartmentalization of privileges among different application or system components, tasks, or processes.

A broader definition could state that multiple conditions must be satisfied to gain access to a specific process or object, where each control could refer to a permission.

Score Evidence
Cost 2 Will incur time costs
Knowledge 2 The concept is generally known and understood
Feasibility 2 Technically feasible (illustrated by XP to Vista implementation) but requires certain prerequisites
Total 6 (Medium)

Libraries or Frameworks

The distinction between a library and a framework can be summarized as:

  • In a library, the application code invokes the library code.
  • In a framework, the library code calls the application code.

It is generally recommended to use the latest version of an established library or framework to achieve the necessary functionality, primarily to reduce redundancy, save development time, and in some instances, to avoid constructing complex functionalities like cryptography from scratch. The underlying security assumption is that the library or framework will not introduce vulnerabilities or will provide elements that facilitate avoiding vulnerabilities.

As with the selection of a programming language, choosing a library or framework entails several considerations, including:

  • Size and complexity—opt for a lightweight library or framework to avoid unnecessary bloat.
  • Performance requirements.
  • Utilize automated bundle tracking to manage updates.
  • Assess the impact on web accessibility.
  • Consider backward compatibility.
  • Review licensing agreements.
  • Evaluate documentation.
  • Ensure correct implementation.
  • Monitor for updates.
  • Prioritize security features.
  • Consider community/vendor support, as some libraries benefit from organizational commitment to security and updates.

However, migrating between frameworks brings significant implications. The process is often complex, time-consuming, and may involve substantial code rewriting (LinkedIn, 2023; Opsview, 2023).

Score Evidence
Cost 2 May incur licensing costs
Knowledge 2 Current knowledge levels as reported by JetBrains (2022)
Feasibility 2 Performance and security impacts must be considered
Total 6 (Medium)

Secure Architecture and Design

Securing the design and architecture of a product must take place from the project’s inception. Retrofitting security architecture or core design features later in development is costly and challenging.

Examples include:

  • Segmenting the product into anonymous, regular, privileged, and administrative components.
  • Refactoring the product to eliminate the need for dynamic code generation.
Score Evidence
Cost 2 A fundamental aspect of software development
Knowledge 3 Not widely utilized or understood
Feasibility 2 Technically achievable with minimal prerequisites
Total 7 (Hard)

Language Selection

Choosing the right programming language significantly influences an application’s security. While contemporary languages like Rust and Swift promote type-safety and memory safety, languages such as C, C++, and assembly fall short in this regard.

Developers often face constraints when selecting programming languages for their projects:

  1. The ecosystem: Is there adequate support, and will it be sustainable long-term?
  2. The target environment/platform: web, mobile, embedded device, or OS application?
  3. The specific requirements in terms of libraries, features, and tools offered by the language.
  4. Performance considerations: Is the language capable of meeting the performance demands?
  5. Are developers proficient in this language?

As projects begin, developers should prioritize selecting a type-safe and memory-safe programming language. The State of Developer Ecosystem 2022 survey from JetBrains, encompassing responses from 29,269 developers globally, indicated that half are considering adopting new languages. The top prospects for new languages include Go, Rust, Kotlin, TypeScript, and Python, reflecting that current knowledge levels are generally assessed at level 2.

However, many modern applications and operating systems are still built using C and C++. For instance, Google’s Chrome has announced plans to support third-party Rust libraries (Google Security Blog, 2023) while still being heavily reliant on millions of lines of C++. Transitioning this legacy entails significant technical complexities, evaluating the feasibility of implementing this mitigation would be categorized at level 3.

Score Evidence
Cost 3 Identified constraints are likely to elevate costs
Knowledge 2 Current knowledge levels per JetBrains, 2022
Feasibility 3 Could be constrained by requirements or time constraints in retroactive application (Google Security Blog, 2023)
Total 8 (Hard)

Mitigation Strategies

Based on an article from ncsc.gov.uk: https://www.ncsc.gov.uk/report/a-method-to-assess-forgivable-vs-unforgivable-vulnerabilities

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top