Defensive programming

Last updated

Defensive programming is a form of defensive design intended to develop programs that are capable of detecting potential security abnormalities and make predetermined responses. [1] It ensures the continuing function of a piece of software under unforeseen circumstances. Defensive programming practices are often used where high availability, safety, or security is needed.

Contents

Defensive programming is an approach to improve software and source code, in terms of:

Overly defensive programming, however, may safeguard against errors that will never be encountered, thus incurring run-time and maintenance costs.

Secure programming

Secure programming is the subset of defensive programming concerned with computer security. Security is the concern, not necessarily safety or availability (the software may be allowed to fail in certain ways). As with all kinds of defensive programming, avoiding bugs is a primary objective; however, the motivation is not as much to reduce the likelihood of failure in normal operation (as if safety were the concern), but to reduce the attack surface – the programmer must assume that the software might be misused actively to reveal bugs, and that bugs could be exploited maliciously.

intrisky_programming(char*input){charstr[1000];// ...strcpy(str,input);// Copy input.// ...}

The function will result in undefined behavior when the input is over 1000 characters. Some programmers may not feel that this is a problem, supposing that no user will enter such a long input. This particular bug demonstrates a vulnerability which enables buffer overflow exploits. Here is a solution to this example:

intsecure_programming(char*input){charstr[1000+1];// One more for the null character.// ...// Copy input without exceeding the length of the destination.strncpy(str,input,sizeof(str));// If strlen(input) >= sizeof(str) then strncpy won't null terminate. // We counter this by always setting the last character in the buffer to NUL,// effectively cropping the string to the maximum length we can handle.// One can also decide to explicitly abort the program if strlen(input) is // too long.str[sizeof(str)-1]='\0';// ...}

Offensive programming

Offensive programming is a category of defensive programming, with the added emphasis that certain errors should not be handled defensively. In this practice, only errors from outside the program's control are to be handled (such as user input); the software itself, as well as data from within the program's line of defense, are to be trusted in this methodology.

Trusting internal data validity

Overly defensive programming
constchar*trafficlight_colorname(enumtraffic_light_colorc){switch(c){caseTRAFFICLIGHT_RED:return"red";caseTRAFFICLIGHT_YELLOW:return"yellow";caseTRAFFICLIGHT_GREEN:return"green";}return"black";// To be handled as a dead traffic light.// Warning: This last 'return' statement will be dropped by an optimizing// compiler if all possible values of 'traffic_light_color' are listed in// the previous 'switch' statement...}
Offensive programming
constchar*trafficlight_colorname(enumtraffic_light_colorc){switch(c){caseTRAFFICLIGHT_RED:return"red";caseTRAFFICLIGHT_YELLOW:return"yellow";caseTRAFFICLIGHT_GREEN:return"green";}assert(0);// Assert that this section is unreachable.// Warning: This 'assert' function call will be dropped by an optimizing// compiler if all possible values of 'traffic_light_color' are listed in// the previous 'switch' statement...}

Trusting software components

Overly defensive programming
if(is_legacy_compatible(user_config)){// Strategy: Don't trust that the new code behaves the sameold_code(user_config);}else{// Fallback: Don't trust that the new code handles the same casesif(new_code(user_config)!=OK){old_code(user_config);}}
Offensive programming
// Expect that the new code has no new bugsif(new_code(user_config)!=OK){// Loudly report and abruptly terminate program to get proper attentionreport_error("Something went very wrong");exit(-1);}

Techniques

Here are some defensive programming techniques:

Intelligent source code reuse

If existing code is tested and known to work, reusing it may reduce the chance of bugs being introduced.

However, reusing code is not always good practice. Reuse of existing code, especially when widely distributed, can allow for exploits to be created that target a wider audience than would otherwise be possible and brings with it all the security and vulnerabilities of the reused code.

When considering using existing source code, a quick review of the modules(sub-sections such as classes or functions) will help eliminate or make the developer aware of any potential vulnerabilities and ensure it is suitable to use in the project. [ citation needed ]

Legacy problems

Before reusing old source code, libraries, APIs, configurations and so forth, it must be considered if the old work is valid for reuse, or if it is likely to be prone to legacy problems.

Legacy problems are problems inherent when old designs are expected to work with today's requirements, especially when the old designs were not developed or tested with those requirements in mind.

Many software products have experienced problems with old legacy source code; for example:

  • Legacy code may not have been designed under a defensive programming initiative, and might therefore be of much lower quality than newly designed source code.
  • Legacy code may have been written and tested under conditions which no longer apply. The old quality assurance tests may have no validity any more.
    • Example 1: legacy code may have been designed for ASCII input but now the input is UTF-8.
    • Example 2: legacy code may have been compiled and tested on 32-bit architectures, but when compiled on 64-bit architectures, new arithmetic problems may occur (e.g., invalid signedness tests, invalid type casts, etc.).
    • Example 3: legacy code may have been targeted for offline machines, but becomes vulnerable once network connectivity is added.
  • Legacy code is not written with new problems in mind. For example, source code written in 1990 is likely to be prone to many code injection vulnerabilities, because most such problems were not widely understood at that time.

Notable examples of the legacy problem:

  • BIND 9, presented by Paul Vixie and David Conrad as "BINDv9 is a complete rewrite", "Security was a key consideration in design", [2] naming security, robustness, scalability and new protocols as key concerns for rewriting old legacy code.
  • Microsoft Windows suffered from "the" Windows Metafile vulnerability and other exploits related to the WMF format. Microsoft Security Response Center describes the WMF-features as "Around 1990, WMF support was added... This was a different time in the security landscape... were all completely trusted", [3] not being developed under the security initiatives at Microsoft.
  • Oracle is combating legacy problems, such as old source code written without addressing concerns of SQL injection and privilege escalation, resulting in many security vulnerabilities which have taken time to fix and also generated incomplete fixes. This has given rise to heavy criticism from security experts such as David Litchfield, Alexander Kornbrust, Cesar Cerrudo. [4] [5] [6] An additional criticism is that default installations (largely a legacy from old versions) are not aligned with their own security recommendations, such as Oracle Database Security Checklist, which is hard to amend as many applications require the less secure legacy settings to function correctly.

Canonicalization

Malicious users are likely to invent new kinds of representations of incorrect data. For example, if a program attempts to reject accessing the file "/etc/passwd", a cracker might pass another variant of this file name, like "/etc/./passwd". Canonicalization libraries can be employed to avoid bugs due to non-canonical input.

Low tolerance against "potential" bugs

Assume that code constructs that appear to be problem prone (similar to known vulnerabilities, etc.) are bugs and potential security flaws. The basic rule of thumb is: "I'm not aware of all types of security exploits. I must protect against those I do know of and then I must be proactive!".

Other tips to secure your code

The three rules of data security

  • All data is important until proven otherwise.
  • All data is tainted until proven otherwise.
  • All code is insecure until proven otherwise.
    • You cannot prove the security of any code in userland, or, more commonly known as: "never trust the client".

These three rules about data security describe how to handle any data, internally or externally sourced:

All data is important until proven otherwise - means that all data must be verified as garbage before being destroyed.

All data is tainted until proven otherwise - means that all data must be handled in a way that does not expose the rest of the runtime environment without verifying integrity.

All code is insecure until proven otherwise - while a slight misnomer, does a good job reminding us to never assume our code is secure as bugs or undefined behavior may expose the project or system to attacks such as common SQL injection attacks.

More Information

  • If data is to be checked for correctness, verify that it is correct, not that it is incorrect.
  • Design by contract
  • Assertions (also called assertive programming)
  • Prefer exceptions to return codes
    • Generally speaking, it is preferable[ according to whom? ] to throw exception messages that enforce part of your API contract and guide the developer instead of returning error code values that do not point to where the exception occurred or what the program stack looked liked, Better logging and exception handling will increase robustness and security of your software[ citation needed ], while minimizing developer stress[ citation needed ].

See also

Related Research Articles

<span class="mw-page-title-main">Buffer overflow</span> Anomaly in computer security and programming

In programming and information security, a buffer overflow or buffer overrun is an anomaly whereby a program writes data to a buffer beyond the buffer's allocated memory, overwriting adjacent memory locations.

In the field of computer security, independent researchers often discover flaws in software that can be abused to cause unintended behaviour; these flaws are called vulnerabilities. The process by which the analysis of these vulnerabilities is shared with third parties is the subject of much debate, and is referred to as the researcher's disclosure policy. Full disclosure is the practice of publishing analysis of software vulnerabilities as early as possible, making the data accessible to everyone without restriction. The primary purpose of widely disseminating information about vulnerabilities is so that potential victims are as knowledgeable as those who attack them.

<span class="mw-page-title-main">Legacy system</span> Old computing technology or system that remains in use

In computing, a legacy system is an old method, technology, computer system, or application program, "of, relating to, or being a previous or outdated computer system", yet still in use. Often referencing a system as "legacy" means that it paved the way for the standards that would follow it. This can also imply that the system is out of date or in need of replacement.

procmail is an email server software component — specifically, a message delivery agent (MDA). It was one of the earliest mail filter programs. It is typically used in Unix-like mail systems, using the mbox and Maildir storage formats.

freedesktop.org (fd.o), formerly X Desktop Group (XDG), is a project to work on interoperability and shared base technology for free-software desktop environments for the X Window System (X11) and Wayland on Linux and other Unix-like operating systems. Although freedesktop.org produces specifications for interoperability, it is not a formal standards body.

<span class="mw-page-title-main">Race condition</span> When a systems behavior depends on timing of uncontrollable events

A race condition or race hazard is the condition of an electronics, software, or other system where the system's substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable.

Uncontrolled format string is a type of software vulnerability discovered around 1989 that can be used in security exploits. Originally thought harmless, format string exploits can be used to crash a program or to execute harmful code. The problem stems from the use of unchecked user input as the format string parameter in certain C functions that perform formatting, such as printf . A malicious user may use the %s and %x format tokens, among others, to print data from the call stack or possibly other locations in memory. One may also write arbitrary data to arbitrary locations using the %n format token, which commands printf and similar functions to write the number of bytes formatted to an address stored on the stack.

<span class="mw-page-title-main">Privilege escalation</span> Gaining control of computer privileges beyond what is normally granted

Privilege escalation is the act of exploiting a bug, a design flaw, or a configuration oversight in an operating system or software application to gain elevated access to resources that are normally protected from an application or user. The result is that an application with more privileges than intended by the application developer or system administrator can perform unauthorized actions.

Code injection is the exploitation of a computer bug that is caused by processing invalid data. The injection is used by an attacker to introduce code into a vulnerable computer program and change the course of execution. The result of successful code injection can be disastrous, for example, by allowing computer viruses or computer worms to propagate.

In programming and software development, fuzzing or fuzz testing is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The program is then monitored for exceptions such as crashes, failing built-in code assertions, or potential memory leaks. Typically, fuzzers are used to test programs that take structured inputs. This structure is specified, e.g., in a file format or protocol and distinguishes valid from invalid input. An effective fuzzer generates semi-valid inputs that are "valid enough" in that they are not directly rejected by the parser, but do create unexpected behaviors deeper in the program and are "invalid enough" to expose corner cases that have not been properly dealt with.

The Windows Metafile vulnerability—also called the Metafile Image Code Execution and abbreviated MICE—is a security vulnerability in the way some versions of the Microsoft Windows operating system handled images in the Windows Metafile format. It permits arbitrary code to be executed on affected computers without the permission of their users. It was discovered on December 27, 2005, and the first reports of affected computers were announced within 24 hours. Microsoft released a high-priority update to eliminate this vulnerability via Windows Update on January 5, 2006. Attacks using this vulnerability are known as WMF exploits.

Session poisoning is a method to exploit insufficient input validation within a server application. Typically a server application that is vulnerable to this type of exploit will copy user input into session variables.

A software code audit is a comprehensive analysis of source code in a programming project with the intent of discovering bugs, security breaches or violations of programming conventions. It is an integral part of the defensive programming paradigm, which attempts to reduce errors before the software is released. C and C++ source code is the most common code to be audited since many higher-level languages, such as Python, have fewer potentially vulnerable functions.

<span class="mw-page-title-main">Comment (computer programming)</span> Explanatory note in the source code of a computer program

In computer programming, a comment is a programmer-readable explanation or annotation in the source code of a computer program. They are added with the purpose of making the source code easier for humans to understand, and are generally ignored by compilers and interpreters. The syntax of comments in various programming languages varies considerably.

A security bug or security defect is a software bug that can be exploited to gain unauthorized access or privileges on a computer system. Security bugs introduce security vulnerabilities by compromising one or more of:

In software, a stack buffer overflow or stack buffer overrun occurs when a program writes to a memory address on the program's call stack outside of the intended data structure, which is usually a fixed-length buffer. Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than what is actually allocated for that buffer. This almost always results in corruption of adjacent data on the stack, and in cases where the overflow was triggered by mistake, will often cause the program to crash or operate incorrectly. Stack buffer overflow is a type of the more general programming malfunction known as buffer overflow. Overfilling a buffer on the stack is more likely to derail program execution than overfilling a buffer on the heap because the stack contains the return addresses for all active function calls.

Secure coding is the practice of developing computer software in such a way that guards against the accidental introduction of security vulnerabilities. Defects, bugs and logic flaws are consistently the primary cause of commonly exploited software vulnerabilities. Through the analysis of thousands of reported vulnerabilities, security professionals have discovered that most vulnerabilities stem from a relatively small number of common software programming errors. By identifying the insecure coding practices that lead to these errors and educating developers on secure alternatives, organizations can take proactive steps to help significantly reduce or eliminate vulnerabilities in software before deployment.

Bugtraq was an electronic mailing list dedicated to issues about computer security. On-topic issues are new discussions about vulnerabilities, vendor security-related announcements, methods of exploitation, and how to fix them. It was a high-volume mailing list, with as many as 776 posts in a month, and almost all new security vulnerabilities were discussed on the list in its early days. The forum provided a vehicle for anyone to disclose and discuss computer vulnerabilities, including security researchers and product vendors. While the service has not been officially terminated, and its archives are still publicly accessible, no new posts have been made since January 2021.

In computer science, robustness is the ability of a computer system to cope with errors during execution and cope with erroneous input. Robustness can encompass many areas of computer science, such as robust programming, robust machine learning, and Robust Security Network. Formal techniques, such as fuzz testing, are essential to showing robustness since this type of testing involves invalid or unexpected inputs. Alternatively, fault injection can be used to test robustness. Various commercial products perform robustness testing of software analysis.

The Java platform provides a number of features designed for improving the security of Java applications. This includes enforcing runtime constraints through the use of the Java Virtual Machine (JVM), a security manager that sandboxes untrusted code from the rest of the operating system, and a suite of security APIs that Java developers can utilise. Despite this, criticism has been directed at the programming language, and Oracle, due to an increase in malicious programs that revealed security vulnerabilities in the JVM, which were subsequently not properly addressed by Oracle in a timely manner.

References

  1. Boulanger, Jean-Louis (2016-01-01), Boulanger, Jean-Louis (ed.), "6 - Technique to Manage Software Safety", Certifiable Software Applications 1, Elsevier, pp. 125–156, ISBN   978-1-78548-117-8 , retrieved 2022-09-02
  2. "fogo archive: Paul Vixie and David Conrad on BINDv9 and Internet Security by Gerald Oskoboiny <gerald@impressive.net>". impressive.net. Retrieved 2018-10-27.
  3. "Looking at the WMF issue, how did it get there?". MSRC. Archived from the original on 2006-03-24. Retrieved 2018-10-27.
  4. Litchfield, David. "Bugtraq: Oracle, where are the patches???". seclists.org. Retrieved 2018-10-27.
  5. Alexander, Kornbrust. "Bugtraq: RE: Oracle, where are the patches???". seclists.org. Retrieved 2018-10-27.
  6. Cerrudo, Cesar. "Bugtraq: Re: [Full-disclosure] RE: Oracle, where are the patches???". seclists.org. Retrieved 2018-10-27.