Chapter 29

Section: Part VII:  Bringing It All Together

Chapter 29. Secure Application Development, Languages, and Extensions

IN THIS CHAPTER

        Security And Software

        What Is a Secure Application?

        A Security Architecture

        Security-Aware Designs

        Secure Coding Practices

This chapter covers secure application development including the design and implementation of Internet applications, systems, and the languages used to create them.


 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

Security And Software

This chapter explores the importance of security to software development efforts including Internet systems, applications and the languages used to create them. Although software development encompasses a large variety of possible efforts, the need for security is common across them all. Strong security is a continuous process, not a feature, that arises from comprehensive and well-thought-out design and development methods and a continuous analysis throughout the life of the application. While there is no such concept as guaranteed security, the goal is to create software with security in mind and establish a high degree of protection that makes attack and exploitation difficult or unfeasible.

This chapter presents several guidelines that assist in evaluating and bolstering the security of an application or product through the phases of a development cycle. The approach used here differs from common practice. Many sources of information on secure application development focus on the technicalities of the code and implementation, while ignoring other elements in the process. This discussion outlines a complete process that introduces security to the entire development cycle.

Security in an application comes not only from the code used to create it. It begins with the initial idea for an application and carries through the design and implementation phases to form a cycle of security analysis. Each of these phases depends on the others to provide the highest level of application security needed and possible. This cycle continues throughout the application's lifespan and should be considered with each of its incarnations, enhancements, or modifications. The information presented here is important for both technical and non-technical readers. Managers and non-technical staff who work in a software development environment need to understand the role of security within their application, the procedures and methods by which security is created, and the issues they might face as a result of weak or strong security. Software developers and engineers need to understand methods to design and write code with security in mind, as well as the security issues that might arise from their efforts.

Application development includes a wide array of software development efforts. Although this chapter focuses primarily on Internet-related applications, the concepts presented are applicable and important to all forms of software development. The discussion covers software applications that run on the desktop computer or server, Web-based applications, operating system issues, and Internet appliances. A thought process that is mindful of security can be developed by considering the material presented in this chapter; this is large step toward the creation of highly secure applications. As a result of high security, the reliability of an application is also enhanced.

Developers achieve security awareness and thoughtfulness through careful implementation and an aversion to pitfalls. Development managers should emphasize development processes and procedures that enforce security requirements. At the end of this discussion developers and development managers should be able to establish a procedure for secure application development that includes the creation of a security architecture and the ability to examine the design and implementation for relevant security issues.


 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

What Is a Secure Application?

The concept of a secure application might conjure analogies to a battle-hardened, armored tank, ready to respond to all natures of attack and impervious to all but the strongest bombs. While such tight security might be the goal in some applications, it is likely the exception rather than the rule. Security should be applied and integrated as is appropriate for the specific task or function at hand. An application cannot be made secure by following a checklist of safeguards that, when put together, spits out an armored application. A secure application emphasizes thoughtful analysis, diligent design, and a focused implementation. Its developers consider the security issues of similar applications and analyze innovations for new risk areas while balancing the level of security required by the users of the application. In order to make all of this happen, it is useful to know the effects of insecurity on an application.

Note

The term application often refers primarily to one particular program or set of programs. The guidelines presented here are useful and applicable to many forms of software development including Internet and system applications and embedded Internet Appliances. The term application should therefore be understood to refer to any of these development efforts, as the concepts are applied to them.

 

The Enemy Within (Your Code)

Common and well-known attacks such as denial of service, buffer overflows, and race conditions can plague applications with poor methods of input validation and data protection. This section provides a brief overview of common vulnerabilities that need consideration when first developing an application.

Tip

Be sure to consider the relevance and effects of the attacks described here when examining an application under development. Also look at similar applications and those with which the new application competes.

 

Configuration Issues

Security problems with the configuration of an application and the environment in which it runs are very common. Applications often store configuration information in a file or database, write runtime information to other files, and provide multiple services simultaneously. Default configurations a computer system's operational settings upon installation and without any user modification can be very dangerous. These out-of-the-box configurations exist to help users run the application with the least amount of effort, but can unwittingly leave them open to attack.

The default configuration often enables every option available in the software, to demonstrate its rich feature set. There is a disadvantage to having every option enabled the user might not understand or even be aware of the enabled options provided. If a vulnerability is discovered, the user is unknowingly at risk. It is not possible to force a user to read and understand documentation about all configuration options, therefore a responsible developer thinks carefully about which options are enabled for regular operation and which should remain disabled.

Two well-known examples illustrate the dangers of insecure default configurations. The first example is inetd, the UNIX Internet Server daemon.

Inetd enables users to access a UNIX system via protocols such as Telnet, FTP, and remote shell. (Many other services are available through inetd; see the manual page for inetd for more information.) Typical early versions of inetd had a standard configuration that enabled all of its services. Various security holes were found in many of these services, making many systems vulnerable to attack, often without the knowledge of administrators.

Another example of insecure default configurations can be found in Web server software.

Apache on UNIX and the Internet Information Server (IIS) from Microsoft are two of the most popular Web server applications in use on the Internet. These Web servers come with sample applications that demonstrate various pieces of functionality. In early versions of Apache and Microsoft Internet Information Server (IIS), these samples had numerous vulnerabilities and were installed by default, allowing attackers to compromise the systems. Many users and administrators were not even aware of the existence of these applications, or that they were present on their Web servers. These common holes are still present today and part of the many Web server security analyses.

Tip

Take care to document features when creating them and provide information about them in any configuration files.

 

Race Conditions

As the name indicates, a race condition is a window of opportunity in a running application that allows another process or application to exploit the privilege or functionality of the first. Race conditions can occur when complex or multistep procedures run, when an application interacts with other processes or resources, or when a functionality is poorly organized.

Figure 29.1 shows a simple example of a race condition. An application normally runs with the privileges of its user. The application then enters a section of code that increases the normal privileges and modifies a system setting. Upon completion of the modification, the privileges return to normal. Through a weakness in the implementation, a rogue application attempts to "win the race" by exploiting the higher privileges. If the program uses Inter-Process Communication (IPC) or temporary files, or takes a long time to perform the operation and does not protect these methods, the rogue application can insert its own commands, which are executed by the application.

Figure 29.1. A race condition: the window of opportunity.

graphics/29fig01.gif

Note

An operating system typically supports several methods by which different processes or applications can communicate. These methods are known as Inter-Process Communication (IPC) methods; they include the use of sockets-based communication, semaphores, pipes, and shared memory.

 

Figure 29.2 demonstrates a race condition with temporary files. In the course of normal execution, the running application makes changes to system settings by creating a temporary file with new settings and then copying that file to the appropriate location. When finished, the running application then deletes the temporary file and continues running. If a window of opportunity exists, during which an attacker can insert his own configuration information into the file after it is created and just prior to its being copied, a race condition is present. This can occur as a result of poorly organized functions, wherein related steps are not grouped logically within the code, or if the temporary file is not properly protected.

Figure 29.2. A temporary file race condition.

graphics/29fig02.gif

The access points of IPC methods such as sockets or shared memory should be initialized with the proper protections, or a rogue application can interact with the application. Good and logically organized code and functionality minimizes the window in which a race condition can occur. This can be done by grouping areas of privileged functionality where the code would otherwise repeat multiple privilege increment and decrement calls. Related functionality should also be organized and executed efficiently.

Tip

Before creating a function that requires higher privileges or performs complex interactions with components outside of the application, take a moment to organize the functionality and find an efficient way to write the code. The security of an application is also enhanced by a good design process and code reviews that identify poorly organized code segments.

 

Buffer Overflows

Buffer overflows are perhaps the most notorious and widely publicized attacks. These are complex attacks that exploit the fundamental hardware and software capabilities of a system.

For those who are not software developers, a few concepts need explanation. First, it is useful to understand what a buffer is. The computer system has a pool of Random Access Memory (RAM) organized into small chunks by the operating system that runs applications. In order to share this memory among the operating system's many processes and applications, a special memory manager coordinates which chunks of the RAM pool are in use and which are available to run an application. When an application is first run, memory is allocated for the application and all of its functions and variables.

As the application runs, more memory can be allocated for new variables and de-allocated when no longer in use. A buffer is a chunk (or several chunks) of memory used to store a variable. Different buffers can and often do exist side-by-side in memory. A buffer that holds a variable can exist next to a piece of memory that holds a function or another application. For example, when you enter your username at a prompt or window, the program has declared a buffer, in which the characters of the name are stored. Figure 29.3 shows a buffer that is used for the username "NAME."

Figure 29.3. A buffer that holds the value "NAME".

graphics/29fig03.gif

A buffer overflow occurs when a buffer is too small to accommodate the amount of data provided. The data that does not fit into the buffer will overwrite the next chunks of memory. Herein lies the danger of buffer overflows. The memory that is overwritten with the extra data can be another variable in the running application, a variable for another application, or the application's stack. The stack contains application-specific information, such as the physical locations of the application's functions and variables.

This alters the path that the application would normally follow, causing it to use bad data, crash, or execute new functionality. The execution of new functionality is usually the goal of a buffer overflow exploit whether to provide access to the system or to modify its settings.

When its owner accesses the overwritten memory next, the new data might be invalid, and the application can crash or function improperly. Buffer overflows are exploited by crafting the overflow data into something useful that the machine can understand. This could mean executing another program, causing harm to the system or stealing information.

To better demonstrate this concept, consider the postal machine that scans letters for their destination city. Assume envelope A is destined for New York, NY, and envelope B is destined for Boston, MA. The ZIP code of envelope B has special wet ink that "overflows," overwriting the ZIP code of New York on envelope A with that of Boston when the envelopes are automatically stacked at the postal facility. The postal machine scans envelope A and reads the overwritten ZIP code. The letter is then routed to Boston.

Buffer overflows are often more malicious than mere accidents, so let us assume that a valuable sum of cash is destined for a postal box in New York. The thief, or attacker, knows the exact location of the letter and creates an envelope with runny ink that overwrites the ZIP code on the envelope with the ZIP code for Boston when they are stacked. The attacker rents the Boston postal box with the same number as the original destination in New York and proceeds to steal the money.

A postal machine is only capable of recognizing ZIP codes, no matter from where they come. A computer is capable of executing instructions, no matter from where they come. Therefore, a buffer overflow attack that overwrites the original instructions of an application with new instructions can cause the computer to execute anything an attacker wishes.

The exploitation of a buffer overflow in an application is an extremely dangerous problem. Figure 29.4 demonstrates the memory layout of a typical buffer overflow. The application developer declares a buffer that will hold an 8-character username. An attacker tests for an overflow by entering a 10-character name, "myusername" at the prompt. The program then starts to copy that name to the previously declared buffer, but the name is 10 letters; the last 2 letters overflow the buffer and crash the program. After confirming that a buffer overflow exists, the attacker crafts a special set of input made of valid machine instructions that executes a malicious program to steal passwords on a system. The attacker then uses this special input set as the input to the username prompt of the affected program, again causing a buffer over flow. Now, the memory is corrupted with these new instructions in such a way that they are executed, causing the malicious program to run and cause harm.

Figure 29.4. The memory layout of a buffer overflow.

graphics/29fig04.gif

Tip

For in-depth technical detail on buffer overflows, the following articles are useful resources:

Compromised Buffer Overflows, from Intel to SPARC Version 8 by Mudge, a gifted hacker and VP of @Stake, a top security consulting firm.

http://www.l0pht.com/advisories/bufitos.pdf

The Tao of Windows Buffer Overflow by DilDog, another gifted hacker of @Stake.

http://www.cultdeadcow.com/cDc_files/cDc-351/

Smashing the Stack for Fun and Profit by Aleph1 of SecurityFocus.com, originally issue 49 of the Phrack online magazine.

http://www.securityfocus.com/data/library/P49-14.txt

 

Data Protection

The scope of data protection is broad it extends from the internal methods of an application out to the operating system and all of the systems to which it connects. This description focuses on data protection within an application and the operating system in which it runs. These concepts can then be extended to interconnected systems.

Applications seldom function completely independently of the underlying operating system and other applications. There are many ways for an application to interact with its operating system. Information is often shared via the previously mentioned IPC mechanisms and files are a common method by which information is stored, manipulated, or moved within a system.

It is important to remember that, in most cases, the application and its operating system are using the same physical processor and memory while this might seem obvious, the security ramifications are often forgotten. A developer cannot assume that the memory used by an application is accessible only by that application. The underlying operating system controls access to memory, and therefore has access to all memory available whether used or unused. If an application is using and manipulating information that should be considered secret, it is vital to protect internal data. Important and secret information such as passwords and encryption keys require special handling. Without the proper protection and initialization, this information can be accessed by exploiting operating system functionality. Establishing authentication and access control methods can protect data. These mechanisms validate the sender of a message within the system and determine which processes, applications, and systems can access a particular resource.

Temporary Storage

Temporary storage in files is not itself a vulnerability, but it merits discussion because it exacerbates the problems of data protection and race conditions. Temporary storage is used when manipulating information such as application or system configuration files and when updating other data stored in files. When temporary files are initialized with weak security, an application becomes vulnerable to attacks that allow unexpected information to be inserted into the files. Weak security in temporary files is defined by the permissions that allow or disallow an outsider to access the contents of the file, and the predictability of the filename. An attacker that knows the name of the temporary file prior to its creation already has a jump on the race condition. At the moment when the file is created and initialized an attacker can attempt to modify it. The race then begins to insert data before the function finishes and removes the file. The results of an exploit vary depending on the use of the temporary storage; typical examples are modification of a system configuration and the alteration of user information and application data.

Tip

When using temporary files, check for their existence during initialization of the routine and set permissions to restrict access only to the application.

 

Denial of Service

Denial of service is a common form of attack and can be initiated from the network or on a local system. These attacks exploit a design's failure to address the negative events in an application. Applications should be developed with an understanding of the functionality they provide and the functionality they do not provide. This allows the developer to build safeguards into the application that protect it from denial of service. These attacks come in several flavors network bandwidth saturation, system resource utilization, and application flaws.

Network bandwidth saturation results when the entire capacity of a network link is filled with data, preventing new communications from proceeding and slowing down those already in progress. This occurs when the network hardware, which is a specialized computer, is unable to process new network data quickly enough. Therefore, the network hardware is overcome, causing delays in new network traffic or even its complete cessation.

System resource utilization is similar to network bandwidth saturation, except the saturation occurs on the individual system instead of the network wire to which it attaches. System resources memory, disk storage, processor utilization, and operating system specific features such as processes and files all have limits; physical limitations of the hardware naturally reflect on the limitations within the operating system. Examples of physical limitations to a system are the amount of memory and disk storage available, and how fast a processor can execute instructions. Examples of operating system specific limitations that are dependent upon the hardware configuration are the number of files and processes that can exist and the number of users who can work simultaneously.

System-based denial of service exploits the confines of these limitations by using all of the available resources of the target. These attacks come in many forms. Many operating systems stop functioning properly when all of the disk storage space or memory is used. Users can be denied access if the maximum number of users is exceeded. A processor can be completely utilized by endless complex functions, causing all other functions to slow or halt. An application that creates multiple processes can cease to function if the process limits of the system are met.

Developers can be misled into believing that these network and system limits make it impossible to avoid or prevent denial of service attacks, but this is not the case. Strong design and implementation of an application can overcome and protect against denial of service and many other forms of attack. There are caveats, however the nature of the Internet creates situations wherein factors beyond the control of the developer are present. It is impossible to completely eliminate all possibilities for attack because of the Internet's dependencies on external environments. The goal then becomes to establish an environment that makes it difficult for an attack to succeed. Forethought and analysis in the design and development of an application limit the situations in which an attack succeeds.

To help safeguard against denial of service in networked applications, it can be useful to set high-watermarks within the application that limit and detect abnormally frequent connection attempts, such as 20 connections-per-second for a given service. These abnormalities might be signs of denial of service if an attacker is attempting to starve the resources of the system. Other protections include resource monitoring and limitation that give the application complete control of its execution.

Tip

To protect against denial of service attacks, begin to consider where potential vulnerabilities exist in an application. Start early in the design phase and continue the analysis through the completion of the application.

 

Input and Output Methods

User-supplied or external input is the most obvious and prevalent point of entry for attacking an application. Whether data is coming from a network, from a keyboard connected to the system, or from a user environment, the security of the input method demands careful consideration. These functions are the doorways to the application for many of the aforementioned attacks; they allow the user to alter the path of the running application by providing different inputs. Application design and implementation must have a defined set of criteria for input. This includes the explicit data types and values that are and are not acceptable, the reaction to unacceptable data, and the path of that data through the application.

Application output can also pose a risk when users blindly write data to files, terminals, or devices. It is equally important to validate output before actually committing the data. The conditions that allow for successful output should be defined and documented, in order to provide a level of assurance within the application that any outbound information has been checked. Application output often uses external functionality provided by the operating system or another application; therefore, it is extremely important to provide some protection methods before setting the data out into the wild where it could react poorly with the external elements.

Tip

Criteria for user-supplied input should be defined, and associated functionality should be developed to support its validation. These criteria should outline what is and is not acceptable. Anytime that externally defined data crosses the boundary of an application or module, it should be validated.



 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

A Security Architecture

A security architecture for an application outlines a comprehensive method for the development of highly reliable and secure applications. It establishes a process and a framework by which the security needs of an application are analyzed, defined, and implemented. This section explores in detail several components that constitute a security architecture.

Components of a Security Architecture

A comprehensive security architecture is best achieved through an increasingly granular approach that begins from an external viewpoint and progresses through the details of the implementation. The following components organize the information needed for the creation of an application's security architecture:

        Risk assessment and response

        Security requirements

        Design phase security

        Implementation phase security

Set the Stage for Security

Risk assessment is an important process in the development of any product or application. The creation of an application begins with the spark of an idea. It is likely that this application idea solves a problem, or provides new usefulness or innovation that was previously done ineffectively or inefficiently, or not done at all, by existing applications. While analysis is done to determine the shortcomings in function of the older applications, security considerations are often forgotten. The tendency to focus strictly on the functionality an application provides and the benefits to the explicit dilemma it solves increases the potential for security risk. It is extremely important to solve the security problems of an application, as well as the functional problems.

Therefore, the first stage in the creation of an application's security architecture is to document the risks inherent in existing applications that are to be replaced by the new creation. Developers should also note risks related to an application with which the new program will interact. The new application often faces all of the security issues that similar applications face, as well as new issues that arise from innovation.

Assessing the security risks of an application requires some diligence on the part of the application designers; if the designers have any level of security experience, the effort to assess risks quickly becomes smaller. The most basic research that identifies the security issues with related applications involves a search through the archives of vendor-specific support issues. The Web sites of these organizations generally have special areas and forums that announce the availability of patches to security problems in their applications. This research gives a sense of the common issues faced by the new application and the functionality it provides. Further research in security-specific forums provides more technical detail regarding the natures of the problems, as well as a broader sense of the security issues related to a specific application area.

As vulnerabilities in the pertinent technical areas are researched, it is important to document them. Creating a list of security risks and vulnerabilities helps establish a scope for the application under development, by determining which issues are likely to affect the new development.

The known vulnerabilities of an existing application can provide hints toward the presence or lack of a security architecture in its design. The vulnerabilities can often be categorized as implementation flaws, design flaws, and functional flaws. Implementation flaws relate to the actual code used to make the application; they provide only a small amount of insight to the security architecture. Implementation security is discussed in detail later in the chapter. Design and functional flaws reflect the thought and effort put into the design of the application. An application with a security architecture highlights and strengthens the functionality by making security awareness an inherent part of it. Shortcomings in design and function leave holes in the thoroughness of functionality, often creating security risks.

Consider the Functionality Not Provided

Strong designs recognize the functionality provided, as well as that which is not provided. The most basic level of functionality possible is defining what an application does. This is done under a completely positive view because it outlines only what an application does under the most pristine circumstances. It naively assumes that the world is perfect and that nothing bad will ever happen when the application is running. This means that all inputs will be completely understandable and fit the expected input "mold" for example, "All usernames will be alphanumeric values of a determined length and no user will, accidentally or otherwise, enter a character that is not either a number or letter." This view also assumes that all network connections would be from known clients, and that these clients would all communicate with the proper protocol for example, "All clients connecting to the application will adhere to the known messaging sequence required to perform the defined communication." Finally, it assumes that all interaction with the operating system occurs in a sterile environment that the application expects for example, "Each and every file that is modified always exists and is correctly formatted." Obviously, this is not necessarily the case and cannot be expected. Unfortunately, many applications rarely make it beyond this level of design. A comprehensive design takes into consideration the imperfections in the real world. A design of this nature recognizes that establishing rules and schemes provides reliability.

Considering both positive and negative scenarios for an application's operation is vital when creating an application. The negative view defines the reaction to unknown input, invalid syntax and communications, and anomalous conditions that might occur. The application needs to respond properly to events that are not expected or defined. Table 29.1 compares a basic design versus a more comprehensive design and the effects each has on user input, file access, and client connections.

Table 29.1. Effects of Basic Versus More Comprehensive Security Design on Application Functions

Effected Application Function

With Basic Design

With Comprehensive Design

User Input

Application receives invalid input and crashes because the non-alphanumeric value is misunderstood.

Application examines the input for invalid values and responds with an error message, indicating a non-alphanumeric value was found.

File Access

Application expects to find a database file in the proper format, ready and waiting for access. Instead, an ill-formatted file or a link to another file by that name is opened, data becomes corrupted, and the application crashes.

Application checks for the existence of a named file that is of the appropriate type, as well as the internal format of the file.

Client Connections

Application expects a client to connect with the first message being "hello." If a client connects and transmits any other value, the application waits indefinitely, disallowing any other client from connecting, and is no longer functioning.

Application validates the transmission and responds with a warning indicating that the received message was an unexpected value.

The degree to which designers and developers formulate answers to negative results plays a significant factor in the reliability and security of an application. While it is often difficult and infeasible to explicitly handle every known exception, general rules can easily be created to handle undesired events. These three examples present extremely simple scenarios that might seem unrealistic, but all have occurred more than once in many applications.

Come Here for Guaranteed Security

This discussion would be incomplete without mention of third-party organizations, whether commercial or public domain, that provide security software, development kits, and hardware to enhance the security of applications. Many of these commercial organizations present their products as providing "guaranteed" security.

However, there is no hardware or software substitute for a well-thought-out design. Often, managers, designers, and developers are led to believe that the addition of some complex and expensive security components offered by a commercial security organization provides "guaranteed" security. This is simply untrue; "guaranteed" security is a fallacy. This concept preys on victims who understand security as a feature or component that can be plugged in for immediate security satisfaction. If only one bit of information is gleaned from these pages, let it be the fact that security is a continuous process!

Few applications are devoid of security components, but it is not sufficient to simply incorporate the most commonly known components in order to render a design or implementation secure. The inclusion of security components without consideration for their use does not enhance the security of an application and can, in fact, hinder it. The products offered by security companies are very valuable and useful when used properly, but they cannot guarantee the security of an application. The inclusion of third-party security technologies should be examined for usefulness and value, given the security requirements that are established for the application.

Security Requirements

In conjunction with identifying related security risks known to a specific application or application genre, developers must assess the security requirements for their application. This analysis should arrive at a balanced measurement of the level of security required for an application. It does not have to ponder the extremes of the security spectrum. Given the understanding that true and guaranteed security is nonexistent, protecting against known risks and minimizing the number of successful attacks and their effects is generally an acceptable level of security. Those involved with the development cycle of the application should determine their own "acceptable" security level, by examining the known risks, the goals of the application, and the methods used to implement the desired security level.

To arrive at security requirements, managers and developers can find it useful to concentrate on the following, commonly known risk areas:

        User authentication and access control

        Data storage of confidential information

        Security in external network communications

        Security of entry points for external applications and the operating system

From these four general areas application designers and developers can identify a minimal set of important features to analyze. Depending on the functionality of the application, some risk areas are more pertinent than others.

To Secure or Not to Secure

The addition of security to an application affects an application in several ways. It immediately becomes more complex, as the code path takes a new turn to accommodate the security methods. The performance of an application might be hindered, especially with the addition of encryption operations. These operations are CPU intensive due to the complex algorithms involved. The efficiency of an application can also be sacrificed if security is applied in areas where it provides few benefits. This can occur if security methods are blindly applied to all components of an application without thought as to their requirements. The following sections provide a reasonable starting point to determine a basic level of security requirements.

Tip

It is a good practice to consider the security required for each module or component within an application. Resist the urge to apply blanket security methods across multiple modules or components. Instead, determine the most suitable level of security for each.

 

Assessing Authentication and Access Control Requirements

User authentication is often handled by the operating system on which the application runs, but several classes of application might need to deal with authentication on its own. Embedded applications, applications that function independently of the operating system, and distributed Web applications often need to accommodate some level of user authentication and access control. Common examples of applications that require these security methods are Internet commerce (e-commerce) applications, wherein users make purchases via a Web site, or customer database access. In both cases, the possibility for many different users or groups of users to use the system requires stringent control of accessible data. The applications need methods to allow separate users to access the systems via a login method; they also need restrictions regarding the individual users'respective financial data.

To determine the authentication and access control requirements, designers should examine the interactions within the application and with the world that surrounds it. This includes the methods by which users access the application sitting directly at a terminal or accessing from the network are two methods that might require different authentication schemes. An application that is accessed only while sitting at the desktop can effectively be secured via the authentication methods of the operating system on which it runs. Network applications that are accessed by multiple users simultaneously, or through which users access data from common databases, provide strong impetus for access control and authentication. The level of granularity and flexibility of the access control and authentication capabilities provided by the operating system help determine if proprietary methods should be developed. Standalone or embedded applications are often developed from scratch and therefore require their own specialized methods.

Requirements for Data Storage

Data storage reflects the method used to store private and sensitive information. This includes the correct use of the file protection methods of the underlying operating system (such as re stricted file permissions) and stronger methods of protecting individual data elements (such as encryption). In many cases, the use of operating system permission methods is sufficient to provide the required degree of security. Encryption can be used to protect extremely sensitive data, such as user credentials and credit information.

The level to which the application needs to store data, and the nature of that data drive the requirements for data storage security. The storage of sensitive information is often the gating factor in determining if high security encryption is needed. User credentials, such as passwords, addresses, phone numbers, and financial data, should be considered sensitive and also treated with high security. Configuration information can often be secured sufficiently with standard file permission methods.

When forming the requirements for data storage, designers might be tempted to standardize on one level of security. For instance, if a password is stored in a configuration file along with other non-sensitive information, the temptation might be strong to use encryption on the entire file as a blanket security method. To determine data storage requirements, examine the needs of all components involved. In this example, applying encryption to nonsensitive data might be considered inefficient due to the computational expense of the encryption operations, as well as the lack of granular control of the other information in the file. A single element cannot be accessed easily if the entire file is encrypted. The complexity involved to access other elements is then increased dramatically.

Network and Entry Point Security Requirements

Applications communicate to users, the operating system, and other applications through entry points. These might be on the same machine as the application or across a network; often, they are on both. The methods for providing entry points to the application and support for network communication are often one and the same; therefore, they are grouped together here.

Security of network communications is best addressed by examining the content of the messages being sent. Applications that utilize network communication for informational messaging or passing static data might not require any stronger reliability than the protocol supports. Again, as in the case of Internet commerce, an application that sends and receives sensitive user information likely warrants the addition of a higher security method. Entry points also determine security requirements for applications that communicate in a networked environment.

The entry points to an application require a high level of analysis because they encompass and provide the network communication functionality. Others also interact with the application through the entry points. These functional areas can be protected via a combination of available operating system methods and the defined access control and authentication schemes used within the application. Analysis of the interactions that an application has with the outside world allows the designers to determine the most suitable level of security needed. These interactions and subsequent entry points are categorized as

        Network interaction

        Interaction with other applications

        Interaction with its respective operating system

These categories are well-known functional areas, and designers probably already know if their application interacts in any of these manners. The next step is to consider each area's security.

Network, Application, and System Interactions

The popularity of the Internet gives rise to many new applications that interact with other components within local systems and with remote systems. Applications that function independently of other applications and interact only with themselves obviously do not require networking risk analysis. The majority of the applications targeted by this chapter do perform some level of external communication, though.

Network interaction can be present at several levels. An application can be completely client/ server oriented, for use on remote systems spread across the Internet. The need for security in these applications becomes considerably more complex than it is in standalone applications.

Several dependencies should be discerned. The level to which the developers wish to provide security mechanisms in the application should be considered. The designers might decide not to provide any internal security mechanisms. The security of the networked application then relies on the security of the networks on which it runs and communicates; the network topol ogy and firewall determine the maximum level of security that an application can experience. The designer might choose to eschew dependencies and provide the highest degree of security possible within the application. These are the two extremes most applications fall somewhere in the middle.

Applications can also use network facilities for localized communication that is not destined to go beyond the confines of the system on which it runs. Consideration should be given to the nature of this communication in order to determine if the implementation creates unnecessary risk.

The need for interaction between applications or with the operating system does not imply a requirement for network communication. Designers should investigate the implementation methods that provide the required functionality. Applications often use sockets-based communication methods to provide these entry points because they are fast and easily implemented. The use of sockets can provide more functionality than is needed, however. Applications that need to communicate only with other applications on the local system or with their own operating systems have many communication methods at their disposal, such as non-Internet sockets and IPC mechanisms.

Commonly, UNIX systems use socket communications because of their ease of use and plethora of documentation. UNIX supports several flavors of sockets-based communication, two of which are the popular IP socket and UNIX domain sockets.

IP sockets, as the name implies, use the IP protocol for communication, and support remote network communication, which allows local and remote processes to communicate with the application. Many applications that communicate only with processes on a local system and that do not require network communication capabilities use IP sockets as a standard interface. IP sockets are not ideal in this situation because they automatically provide access to local and remote clients.

Domain sockets use an internal UNIX protocol for communication and do not support network communication; they do provide a connection-oriented communications channel. Domain sockets have their share of risks if used improperly. UNIX domain sockets support the passing of file descriptors as well as informational data. This means that pointers or handles to other parts of the system can be passed from one application to another. This functionality is available only in UNIX domain sockets. If this capability is not desired or warranted, diversion from sockets-based communication to another IPC mechanism might be a better choice.

Considering the kind of information sent to and from the application helps define the requirements for its communication method. Designers should evaluate the interaction capabilities of the application before incorporating a standard function. In this example, the application using IP sockets is at risk because it allows remote systems to connect to the application when they should not be allowed. A better design documents the requirement for interaction with only local applications. This comprehensive requirement leads the developer to use something other than IP sockets.

Note

For in-depth information about programming with sockets and Inter-Process Communication, see the following well-known books on the subject:

UNIX Network Programming, Volume 1: Networking APIs Sockets and XTI by W. Richard Stevens. Prentice Hall. ISBN: 013490012X.

UNIX Network Programming, Volume 2: Interprocess Communications by W. Richard Stevens. Prentice Hall. ISBN: 0130810819.

 

Operating System Interactions

Interaction with the operating system often creates another level of security issues. Many levels of interaction can occur with an operating system network interaction, Inter-Process Commu nication, and the manipulation of files have already been mentioned. Two other types of interaction also require attention: The execution of external programs and the use of system and other externally defined calls are common sources of exploitation. Important issues such as permissions, authentication, access control, and input validation should be considered in conjunction with operating system interactions.

System calls and external applications present a high degree of risk when used improperly because of their natures. These functions often exist in libraries that are used by many applications simultaneously and that often provide direct access to operating system components and resources. Exploitation of a single application through these functions can affect several applications and the system.

System calls provide access to many general-purpose and system-specific functionalities. They allow an application to interact with specific hardware components as well as kernel-level functionality. The safety of the operating system and components needs to be considered when using system calls. If an application ties together user or network data with the operating system via system calls, designers must minimize exposure to dangerous, unexpected, and improper data.

The execution of external applications is another common, unsecured interaction. Developers often design an application to call upon other applications by various methods. There are sev eral reasons a developer might want to do this, such as to differentiate between functions or to establish environmental control. Calling other applications also allows developers to use existing functionality and to expedite the application's implementation.

There is an inherent danger in calling another program from within an application the external program can rarely be trusted. The application exists in a dynamic environment where it is possible to modify or replace any program. The problem lies in the methods used to call the application. UNIX-based systems often support functions called system() and exec(), which pass the supplied parameters as a string to the execution of the standard UNIX shell and subprocess, respectively. The system() call returns to the calling program when complete, but exec() terminates the running program and replaces it with the called program. Windows- based systems have the exec() call that allows the execution of other programs. Unlike UNIX, the Windows version of exec() runs the specified program in a subprocess, and the calling function does not terminate. Without input validation, attackers can put shell meta-characters into the input stream, forcing the shell to run possibly harmful commands and parameters. The use of these functions is generally frowned upon, and effort should be made to avoid their use because they allow the execution of untrusted and uncontrolled applications. Alternatively, designers can incorporate the required functionality directly into an application. The use of freely available, open-source software greatly decreases the effort needed to do so.

Many operating systems also allow functionality to run at different privilege levels. UNIX has its "root" privileges, Windows has "Administrator" and "SYSTEM" privileges. These special accounts can perform administrative tasks and have more interaction with the operating system and its services than other accounts do. Programs can be written to elevate privilege levels when specialized functionality is needed. An application and the operating system on which it runs can be compromised if privileges are not carefully controlled.

The model of least privilege suggests that an application should run with the minimum set of privileges needed to perform most functions. Functionality that requires higher privileges should be isolated into its own module, however that is defined as a process, a class, an application, or even another system. Even then, that set of functionality should also run at the least privileged level until those elevated privileges are required. At that time, higher privileges should be obtained and, upon completion of the functionality, privileges should be returned to the minimum level.

Note

Meta-characters are characters that take on special meaning in a given context. For example, within the standard UNIX shell, the semicolon (;) is a command separator, that is, many commands can be put on a single line when a semicolon is between them. The pipe (|) character sends terminal output to whatever follows the pipe, allowing the output from one program to be fed into another.

 

Throw Away That Security Blanket

Some application developers choose to forget or ignore security within their applications and put themselves at the mercy of the customer's network or operating system and its security features. Using this blanket security model shuns the responsibility to provide secure applications to the public. The security philosophy advocated in this chapter purports a strong and comprehensive security ideology that assumes nothing about the security of components external to the application. An application should always be as secure as it can be with respect to itself and the external components with which it interacts. The level of follow-through is left to the discretion of the designer or developer, however.

Identification of the Risk Areas

As stated, a good process for application development includes the definition of a strong security architecture. This provides a new level of comprehensiveness that fosters analysis and provides a documented overview of potential security problems, as well as the methods for resolving them. When creating a security architecture for an application, possible attacks can be classified in the following categories:

        Subversion of the application

        Subversion of the system and external applications

        Cessation of functionality

Often, an attacker's goal is to gain access to resources or assume control of an application or the system on which it runs. Short of this, an application can simply be forced to crash, which can also cause the entire system to crash.

Subversion of the application occurs when the attacker causes the application to do something it was not intended to do, or to execute some level of unintended functionality. These vulnerabilities are broad in nature and effect. Exploitation of buffer overflows, race conditions, and data input are examples. The effects are limited to the running application.

Subversion of the system and external applications occurs when the exploit affects other running applications or system resources. This can include execution of other applications, such as a shell in UNIX, or utilization of a connection to another application using the vulnerabilities outlined here. The effects of the attack are not limited to the running application, and that application is often used as a conduit to other systems, applications and the operating system.

An application can completely cease to function and crash due to attacks. This is a form of denial of service that targets the limitations of the application, rather than the network or oper ating system. Applications that are well designed and securely coded provide a high level of security and reliability that protects against denial of service.

Security Response

Once the risks and requirements are identified, the response to these issues is the logical next step. Identification of potential security issues is not very useful without a known path to protect against those risks. The defense methods become a natural part of the design in this phase of the development cycle.

Knowledge of the various vulnerabilities, interactions, and areas of analysis discussed previously form the basis of a security response. Knowing the relationship of existing vulnerabilities in the target environment and in similar applications establishes the minimum level of security required. Ideally, the new application provides more security than its predecessors and competitors, by addressing all of the known vulnerabilities and analyzing for new issues. The next steps involve a careful look at all of the interactions within and around the application. It's useful to start at the external view and progress inward through the application. To define areas that need higher security, analyze all methods by which the application communicates and interacts with the network, operating system, and other applications. Analysis of the information passed across these channels further clarifies the security requirements.


 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

Security-Aware Designs

The security of an application depends on the comprehensiveness of thought and analysis done during the application's design phase and prior to its actual implementation. The design phase ties together all the requirements and considerations outlined during the early gestational phases of the application idea, and provides an explicit implementation path. The concept of security in software development is often ignored during the initial stages and sometimes throughout the life span of a development effort or is viewed as a feature that can simply be added at a later point. Security is not a feature, but an integral part of all phases of a software development cycle. Good security arises from a combination of good processes, good practices, and continuous analysis. The earlier an organization introduces security analysis to the development cycle, the stronger the application will be. This section suggests a process by which the elements of secure application development can be implemented and enforced.

Design Phase Analysis

A comprehensive design provides solutions to the problem addressed by the product, and also takes into consideration the effects of the innovation. The security of an application is also created and enhanced by a comprehensive design.

Once an organization discards the idea of security as a feature, it becomes apparent that secu rity needs consideration early. While each organization might have different methods of designing a product or application, the following approach is useful to assure a high level of security in a design. The design phase analyzed from three viewpoints:

        Global

        Organizational

        Component

The global viewpoint is the highest view of the system; it identifies the needs addressed by the application and its feature set. The organizational viewpoint highlights the individual components that make up the application. The component viewpoint goes to the next level of granularity, by examining the explicit details of each component and its implementation. The incremental approach used here allows for a deeper and more comprehensive analysis that also provides an easily understood process flow. This helps managers and developers put the appropriate procedures into place to ensure the consideration of security in their application.

The Global Viewpoint

Many applications arise from an unfulfilled need or the inadequacy of current solutions. A good design identifies the following security concerns:

        Security issues related to existing solutions to the problem or the need being solved

        The application's response to those security issues

        The potential security vulnerabilities that exist in the innovations being made

A global analysis provides information that an attacker would otherwise find after the application is released. At this phase, a security architecture should be defined for the application. This architecture formalizes the level of security needed in an application. It helps establish the application's security scope by identifying the relationship of the application to its surroundings and the level of security provided by them. A security architecture also highlights the need for, and amount of, independent security that an application must provide, as well as the features required.

A global analysis and establishment of a security architecture are done via the incremental categorization of the modules, components, interfaces and methods used in the application. Their location and relationship to each other and their exposure to external applications, users and interfaces are important points.

The global viewpoint of an application initially presents the proverbial "black box" the only details known are those seen by outsiders. These external features and functional requirements are then separated into modules for security analysis.

Searching vendor advisories, newsgroups, mailing lists, and online forums for disclosed vulnerabilities in competing or similar applications is a good way to learn about some of the major security issues related to the application. It also helps point out ineffective solutions to vulnerabilities found in similar applications.

Case Study, Phase I

This hypothetical example is a case study for the development of an Internet commerce application. It will be used throughout the remainder of the chapter to exemplify the guidelines and information presented here. This service allows users to connect from their browsers to make online purchases.

The first level in the design of this application is the definition of its capabilities, independent of the implementation methods used. These features might include the following:

        The selection of an appropriate operating system to host the applications

        Database access for storage of private and public information

        Web serving

        Connectivity of these parts using custom-developed applications

        Connectivity to financial institutions for the transactions

From the global viewpoint, a designer discerns the security features required for each component of these functions.

The database stores private information on many different users, including passwords, credit card numbers, and contact information. In order to protect this information, the database should have security components that support restrictions to objects and perhaps their encryption.

The Web server should support secure communications with the Secure Sockets Layer (SSL) protocol, and some method of interfacing with external applications and the database. This could be via Java servlets and applets, or CGI programs.

Network communications are an inherent part of this system. The Web server and database server software probably run on separate machines, hence a means to secure the network communication between them is required.

The last component to be considered is the actual connection to the organization that authorizes the transactions. This could be a bank or credit card company that authorizes the expenditures by users. Connections are likely accomplished with a modem or other piece of telecommunications equipment that interacts with the financial organization and performs the validation. These connections require high security, in order to avoid access by unauthorized users.

These insights are gleaned from knowledge of the field, analysis of competing products, and familiarity with customer requirements. At this point, diligent research needs to be done to document past and current security vulnerabilities in related products. The resulting list should identify the risk areas that need addressing. Security features missing from the initial requirements list are often identified through this analysis, another of its benefits.

In our Internet commerce example, researchers found the following vulnerabilities in comparable products:

        Databases had no protection schemes, resulting in the exposure of private customer information.

        The Web server had holes that allowed the execution of arbitrary code on the system. The server's default configuration also allowed directory traversal, which allows external users to access many private files and directories on the system.

        The CGI applications used to communicate with the database had vulnerabilities that allowed remote attackers to impersonate other customers.

The research also indicates that data protection, buffer overflows, default configuration, and input validation issues need to be avoided because these vulnerabilities occurred frequently with similar applications. It also showed that vendors have added security features and patches that will protect against these problems. This makes authentication and access control important requirements, along with network security.

At this point, the designer knows of vulnerabilities associated with the application, the methods by which other organizations respond to them, and a set of important security features that form the basis of a security architecture. The security architecture develops further in the next phase of design.

The Organizational Viewpoint

Security is not an exact science; its needs are specific to each application and environment. Therefore, it is not sufficient to look at security from only the global view. Although an otherwise well-designed application might exist independently of all other applications on the system, the interaction of all components in that application might pose security threats. The organizational viewpoint identifies the individual elements or groups of elements that form the entire application, their functions, and their relationships with each other. An element could be a function, a class, a process, or a set of these elements that are grouped by their relationship to the application.

An application is typically made of several functional modules, such as the user interface, networking or communication components, or data storage and retrieval tools.

While a module might seem reasonably secure, its relationship with the other components might be executed insecurely. Similar to the analysis of the interactions of the application, organizational level analysis takes the functional requirements and determines the architecture used. The functional requirements allow the designer to identify a set of components and methods that provide the functionality; an analysis of the security between them as they interact yields the safest choices for inclusion with the application.

The incremental identification, organization, and categorization of each component in the application continues in this phase. A logical place to begin and particularly suspect in terms of security are the edges of a module. The edges are the entry or exit points where data crosses the boundaries between modules. For example, data crosses boundaries between modules during these functions:

        Passing of data as parameters

        Setting global variables

        Manipulating shared memory

        Writing data to files

        Sending data across a communication channel such as the network

        Receiving user input

Case Study, Phase II

In the organizational phase of designing the sample Internet commerce application, each application component the Web server, the database server and the associated applications is categorized into its functional module for further analysis.

The database server has a storage component, an authentication and access control component, and a communication component. The Web server has the Web serving component, an external programming model, and secure client communication and secure database communication components. Interactions between these components form a definable path, with several points where security must be strong. Following an interaction between the Web server and the customer, a client Web browser transmits sensitive data to the Web server. This data is then passed to the custom application that interacts with the database and the financial institution. Following the data path through the application, a developer can observe points of vulnerability in the client communication, the Web server application interaction, the communication from the Web server to the database, and the interaction between them.

The security requirements begin to take shape. The application must address these risk areas and provide security in these forms:

        Secure communication to the client Web browser in the form of encrypted network communication, authentication of users, and access control mechanisms on their information.

        Safe interaction between the Web browser and the e-commerce application to ensure the safety of the system

        Application integrity of the commerce program

        Secure communications with the database

        Security of database objects

From these requirements, an architecture begins to take shape. The use of SSL on the Web server protects the server-to-browser communications. The application will have its own authentication mechanism that allows clients to sign in safely. Data validation and protection mechanisms will also be implemented in an organized fashion that uses only the minimum required privilege to operate. Functionality will be incorporated into the application for object protection and encryption on the database server. The actual implementation details will be developed in the final component phase.

Pertinent security methods will rise to the surface when the following questions are asked in the organizational phase:

        How will we protect information passed?

        What are the effects if one module passes spurious data to another?

        Have the constraints of the data been defined?

        Is there a preferred method of passing data that lends itself to increased security?

        Which components, applications, and users need access to the data? What kind of access is required read-only or both read and write capabilities?

The Component Viewpoint

The final viewpoint from which design analysis occurs is the dissection of the individual components within an application. The smallest design and implementation details can introduce obvious and obscure security problems that are difficult to find post-release. Poor implementation can also undo the effort put into the security of a design.

Some of the precautions suggested here fall into what are considered good coding practices and are not necessarily security specific, but they do have an effect on the security of an application.

The security architecture that is defined for an application will mandate that there be a series of checks and balances to which the application must conform. These checks and balances will provide a high degree of assurance that an application acts in a uniform manner in the event of unexpected data or information.

The component view examines each piece that forms a module. (Modules, in turn, form the application.) These components should be analyzed for their individual security features and the interactions with other components within that module. Starting points when examining programmatic issues within a module are

        Return values

        Precedence and prerequisites

        Data validation

        Identified response and recovery

        Permissions and privilege

Return values are indicators of success or failure within a function. Components of an individual module are made of functions, which interoperate and have established relationships that allow the program to perform properly. Developers should also understand what it means when a function fails, and should react appropriately to that failure. A complete understanding of these relationships allows a developer to understand the dependencies between functionalities. Based on these dependencies, components within a module can be organized to enhance reliability and security.

Data is dynamic in any application it travels between functions and modules, and to separate applications, altering the execution of the initial application as it does so. With the identification of the modules that form an application, and the components that subsequently form those modules, the path of data through an entire application can be traced. The entry points between modules and functions are the pivotal elements that affect the success or failure of the application, therefore validating data at these points is vital. Working hand-in-hand with validation is a defined response to invalid data and anomalous conditions. Many applications fail to formulate a recovery mechanism in the event of unexpected events; this often results in unstable applications that crash at the earliest sign of imperfect data.

Knowledge of the permissions and privileges required for the components to function provides important information for the application designers. Following the model of least privilege is a good design practice; it recommends the isolation and limitation of privilege in a running application. In most cases, high privilege levels are needed very infrequently and only in isolated instances. Many applications that require elevated privileges for a small portion of functionality commit the entire application to that higher privilege level for its entire existence. This creates a hazardous environment wherein each vulnerability that is found exists in this privileged mode.

Privilege refers primarily to the several levels of authority on an operating system. At each higher level of privilege, authorized users have access to progressively more resources in the system, such as memory, other applications, hardware devices, and data. The root user in UNIX, the administrator, and the SYSTEM special account in Windows exemplify the highest level of privilege. There are often various degrees of access that provide granular access control, also. Granular access control allows definable access and rejection methods for the application. Files, network traffic, users, and objects can be bound to a set of explicit permissions that allow or disallow access to them.

Case Study, Phase III

The component phase analysis of our Internet commerce application looks at the individual components used in the application and their security. Entering this stage, designers should have a sense of the complete data path and the relationships between modules. The next granular step establishes the basis for the application's implementation. The goals of this analysis are to determine the privileges of the various components, a sense of organization that will be used during the implementation, and knowledge of how the implementation will occur. Here, previously determined requirements are translated into detailed implementation methods.

Designers choose a Web server and a database server at this point, based on the established requirements. In our example, an Apache Web server was chosen because it can be made to support SSL communications and has a well-documented method to interact with external applications. An Oracle database server was chosen because it provides the flexibility, scalability, and security required in the database, and also because it supports Java-based interaction. Java was chosen as the language and extension for implementing the actual Internet commerce software because it operates with the Web and database servers and supports a strong, configurable security model.

The Web server and database server are then designed by determining the most secure methods for the following components:

        Server default configuration

        Security configuration of the underlying operating system

        Privileges required to interoperate with the Java applications

        Access control components

The commerce software is organized to provide these components:

        Privilege requirements to perform its functions

        The Web server interaction and communication entry points

        User authentication and access control methods

        Session security methods to prevent impersonation of users

        The database server interaction and communication entry points

Based on this analysis and organization, it is determined that the only portion of the system that requires elevated operating system privileges is the commerce application. It also needs an internal set of privilege levels to enforce user access control and authentication. The default configurations enable only the functionality needed for operation of the system, and each operating system is security-hardened with the same minimalist approach.

The commerce application is where the majority of the security components exist. It has the responsibility of validating user credentials, setting permissions on database objects, keeping track of each user session, and having the actual intelligence required to keep the system functioning.

The commerce application needs elevated privileges only when controlling Web and database server startup and shutdown. The most secure method to perform these actions is determined to be a separate controller process that increases its privileges at the time it performs a startup or shutdown action; it then relinquishes its privileges until the next request. The controller process communicates only with the commerce application and uses authenticated messages to initiate the startup or shutdown of the system.

The commerce application will also encrypt private information before storing it in the database in order to protect user credentials and financial information. In the event of anomalous and error conditions such as invalid input data, user authentication failures, and failed communications, the design calls for a reporting system that can log this information and respond appropriately to the events.

System implementation rises naturally from the continuous analysis provided in the preceding global, organizational, and component analysis phases. The developers use the guides and procedures in place to help them write the code that maintains the high security standard established.


 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

Secure Coding Practices

This section delves into the more technical aspects of security in the code used to implement an application, and provides guidelines to develop an enforcement process for secure implementation. The potential for vulnerabilities in an application is reduced by a strong design, but the implementation of the application seals its fate. Hard work poured into a secure design becomes inconsequential if the implementation is poorly done. It is also important to understand that the inclusion of security-related technologies or design methods does not necessarily imply or guarantee any level of security within an application. The implementation of an application and any security technology used is one of the final components that brings a high level of reliability.

Analysis in the implementation phase is the responsibility of both developers and development managers. Developers are responsible for implementing the design well, whereas managers are responsible for setting forth the process that ensures a good implementation. This can be done via standardized procedures that include documented development and coding standards, design and code reviews, and developer training with regard to security in application development. These procedures benefit the developers and applications regardless of which languages are used or the type of application developed.

The most commonly used languages today are the C programming language, Java, and scripting languages such as Perl and the UNIX shells. Each of these languages and environments can be used improperly to compromise the security of an application and the system on which it runs. The following demonstrates security issues with these languages in relation to the vulnerabilities outlined earlier in the chapter. However, this section is not a checklist for developers to follow. Instead, the development of a security-focused thought process allows for an arguably stronger coding practice.

Pitfalls by the C

The C programming languages, which include C, C++, and object-C, are the most commonly used languages and can be dangerous in unsure hands. They provide the developer with the ability to manipulate and access many parts of the system, such as memory, files and devices. This is a great strength of the C languages, but danger arises when the developer makes mistakes. C provides a high level of access to the underlying operating system, and there are few checks and balances to protect the developer. If the developer mistakenly writes data to the wrong device or memory location, the C program will do whatever the developer writes, regardless of the data or destination.

The first vulnerable area often associated with C is the buffer overflow. The following sample code demonstrates a very basic overflow:

char string[10];
 
strcpy(string, "AAAAAAAAAAAAAAA");  /* 15 "A" characters */

Here, 15 "A" characters are copied into the memory area for a variable string, which is declared to be a static 10-character array. The strcpy() function does exactly as directed with no regard for the size of the data being copied or the location to which it goes. A buffer overflow occurs when the 11th element is copied into the memory location, immediately following the location of the 10th element of the variable string. Now apply this principle to any input data that comes from an external source, replacing the string of "A" characters. This allows attackers to control the effects of the overflow.

The strcpy() function is one of several functions in C that do not perform any bounds checking and allow arbitrarily sized buffers to be copied. Other functions to avoid are gets(), strcat(), sprintf(), and the scanf() family of functions. There are updated versions of some of these functions that allow the lengths copied to be specified. These are strncpy(), strncat(), snprintf(), and fgets(). These modified functions copy only up to the number of characters specified by the length parameter. At most, length characters are copied from source into destination:

strncpy(destination, source, length);

Tip

When using the "n" versions of the string manipulation functions strncpy(), strncat(), snprintf(), and fgets() be sure that the length is not larger than the destination string, rather than the source string. The buffer can be overrun if the length value is larger than the size of the destination buffer.

 

When using pointers to buffers, instead of statically declared buffers, you need to allocate enough memory to store the values being copied. Use the memory manipulation functions, which allow you to specify length.

Tip

When allocating memory for string data, do not forget to add 1 to the total length, in order to accommodate for the NULL terminating character. Without a NULL terminator, the data in memory directly after the last character of the string might be considered part of the string.

 

These functions are not the only places where buffer overflows occur. Be sure to check that information read, copied, or written to any memory location or assigned to a variable will fit, or that the destination allocated has enough storage space.

Tip

To avoid buffer overflows in your code, be sure to validate input. Check the size of the data and the storage location and use manipulation functions that generate developer-specified amounts of information instead of arbitrarily long chunks of data.

 

Race conditions add a level of complexity to using C code. Race conditions can be exploited in two aspects of C code creation sequencing and protection.

Sequencing refers to the order in which events occur in an application. Race conditions can result from sequencing variations between dependent events, when no checking is done between the events. This often signifies a shortcoming in any error-checking and validation routines used. If two functions normally run sequentially and the second function assumes that the results of the first are valid, then the possibility for a race condition exists. Elevated privileges, discussed in further detail in the "Operating System Interactions" section earlier in this chapter, are often targets of attack. Organization, combined with sequencing and error checking, minimizes the possibility for race conditions.

This is a bad implementation that creates a race condition:

increase_privs();
...
value = special_app_function(); /* requires privileges */
 
other_unreleted_function(); /* does not require privileges */
other_unreleated_function2(); /* ditto */
 
special_dependent_function(value); /* requires privileges */
...
exit();

Here, a couple of unsafe practices occur. The privileges of the application are elevated early in the application, but not used until later. They are also never relinquished, so most functionality executes with higher-than-needed privilege levels. Finally, the race condition is created through poor organization dependent functions do not occur near each other. An example that solves these problems is

increase_privs();
value = special_app_function; /* requires privileges */
if (!validate_function(value) /* assure the safety of the value */
{
  do_error_processing(value); /* do something intelligent with the error */
}
special_dependent_function(value); /* requires privileges */
decrease_privs(); /* no longer need privileges */
 
other_unreleted_function(); /* does not require privileges */
other_unreleated_function2(); /* ditto */

Note the special validation and error processing routines that are used before passing the value to another function.

Tip

Organize functionality and combine it with validation to ensure that expected information is not compromised between dependent events.

 

Many race conditions exist as the result of poor temporary file usage. When these files are created, they should be protected against external attack during operation. UNIX and Windows allow the developer to set the permission bits and operational flags when creating a file. Permissions should disallow access to anyone but the owner of the process. When creating the file using the open() call, set the O_EXCL and O_CREAT flags, which cause the function to return an error if the file you are attempting to create already exists. Because it is a temporary file, it should not exist prior to the need for it. If the file exists, this is a possible sign of attack. When using these methods, it is also important to check the return values of the functions and to clean up any files in the event of error conditions. The following example shows the syntax to open a file or create one, if it does not already exist with permissions that allow only the creator to read, write, and execute it using the S_IRWXU mode macro.

open("filename", O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);

The call will fail in the event that the file exists already because of the O_EXCL flag. Also, note the unsecure filename. The static naming convention used increases the risk of attack dramatically because one component of the attack is already provided.

Due to the increased presence of temporary file race conditions and the associated insecurities, several operating systems have specific functions to create temporary files in a secure manner. The mkstemp() and mktemp() functions have been written and rewritten to solve the protection issues and the predictability problem discussed in the "Temporary Storage" vulnerability section.

Developers can also use an operating system's built-in file-locking capabilities to control access to the files. These methods control access in the fundamental kernel components.

Tip

When using temporary files, randomize the filenames, set strong permissions, and organize the creation, use, and removal of the files to minimize the possibility of attack.

 

Another component that increases reliability and can influence the security of an application is the return value. While it might seem obvious, it is important to stress the necessity of validating return values of functions. Functions often execute serially and rely on the results or data from a previous function. By checking the return value of previous functions, the dependent function is protected from executing with invalid data. Even when the events are not attacks, recovering from anomalous conditions increases the robustness of the application.

This example demonstrates a poor implementation that fails to check return values:

n = do_string_check (string, valid_characters); /* function returns an int */
if (n == GOOD_RETURN)
{
  process_string(string);
}

Here, the implementation is weak because the negative case, a bad return, is never handled. A better implementation is

n = do_string_check(string, valid_characters); /* function returns an int */
if (n != GOOD_RETURN)
{
  special_error_processing_routine(n); /* bad value, do something */
}
 
process_string(string);

The negative return is handled by the-error processing routine, which can exit the program, request a new string, or convert the return value into a valid parameter. If the return value is good, it goes to the process routine.

Tip

The creation of reusable event and error routines provides a standard mechanism by which all applications react to various attacks and issues. Ideas for these routines include common validation methods for string and numeric values, wrapper functions to perform integrity checks, and protection mechanisms that validate variables and memory locations. Always check and process the return value of a function.

 

The next bit of detail involves the use of sensitive information within the application, including passwords, encryption, or any other private information. As mentioned previously, all program information exists in areas of the common pool of memory that can be subject to reading and modification by external procedures. It is beneficial to clear the memory when the information is no longer in use, in order to avoid revealing information during an attack. The most common and sufficient method to clear data, this is typically referred to as zeroing out memory. When stored information is no longer needed, the storage locations should be overwritten with zeros or random data to prevent an attacker from recovering the information via memory or core dumps. This procedure becomes particularly important when encryption is in use. The keys used to encrypt and decrypt messages are the most important pieces of a cryptographic system, and everything possible to protect them needs to be done.

These guidelines exemplify some of the common issues that arise in C-based applications. C is a very popular and powerful language that allows provides great flexibility to the developer, and care should be taken with its use.

A Perl of an Application

Perl is an interesting beast that combines many of the benefits of a structured programming language, like C, with the flexibility and integration of a UNIX shell. Perl allows the developer to create procedures or subroutines, define variables, and utilize applications and commands available with the operating system. These capabilities, and its strength with regular expressions and parsing, give Perl a strong presence in Web applications, system administration, and automation.

Perl programs are not generally susceptible to buffer overflows because of the weakly typed nature of its variables and declarations. Unlike C, wherein variables and memory need to be defined as a particular storage class and memory must be allocated for them, Perl does everything automatically and treats everything as string data. Take the following example:

#!/path/to/perl
 
$one = 1;
$one_s = "1";  # No different than $one
$two = $one + $one_s; # the result is 2, or "2", which are equivalent

In C, the variable $one would likely be declared an integer and $one_s, a string. The addition of the two elements would also result in an erroneous value. Perl does not differentiate between the different types, so $two is assigned 2, or "2" they are equivalent in Perl. The language also does not fall prey to the memory allocation requirements that other languages exhibit. The following example is completely acceptable in Perl:

#!/path/to/perl
$var1 = "AAAAA";
$var2 = "BBBB";
$var2 = $var1; # $var2 becomes "AAAAA";

$var2 takes on the new value of five "A" characters. All variables are dynamically allocated; there is no concept of preset storage space that could be overflowed.

Perl is susceptible, however, to race conditions and the vulnerabilities associated with the execution of external programs. Care should be given to the sequencing of functions. Input validation is equally important in Perl, in order to prevent the exploitation of external applications.

Perl supports the capability to open files, similarly to C and other languages, therefore the use of temporary files should incorporate appropriate permissions and creation flags.

The use of Perl in Web-based CGI programs is also extremely popular. The greatest risks associated with its use in this environment occur during input validation and execution of external system programs from within. To protect the application, several precautions can be taken. Using the taint-check mechanisms of Perl, any variable set outside of the program will not be passed to any program run by the application. Any variables set by the tainted variable become tainted. Taint-check mode is particularly useful for avoiding vulnerabilities, wherein unchecked user variables are passed surreptitiously to programs called from the Perl application. To initialize version 5 of Perl in taint-check mode, use the following script header:

#!/path/to/perl  T # Run in taint-check mode

The next precaution is to parse input values to remove meta-characters and unwanted values. This helps protect against attacks that exploit parameter-passing to shells and other applications. The following example shows a simple routine that scans an input string for any meta-characters that might be interpreted by a program:

$unclean_input = &get_HTML_forms_response();
if($unclean_input =~  tr/;|`!#$&*()[]{ } <>:'"//)
{
  # Print out some HTML here indicating failure
  &do_some_error_reporting();
}

In this case, the routine reports an error if a meta-character is found. Alternative methods replace meta-characters, or only continue if no meta-character is found.

A final precaution is the use of a shell to run other applications. As with the UNIX system() call and the Windows exec() call, the system() call in Perl allows the developer to run another application. The exec() call in Perl functions like that call in UNIX the running process is replaced by the program indicated. These functions can be particularly dangerous when used in an environments that allow user input, such as CGI programs or system utilities. If input validation does not occur, the application can be exploited to execute arbitrary programs that can affect the system. The following example demonstrates the insecurities of using system() with nonvalidated input. Assume the user supplied the string username ;/bin/rm rf / that became assigned to the variable $input:

system("ecommerce_app $input");

This effectively translates to /bin/sh ecommerce_app username ; /bin/rm rf /. Assuming that the program is running with privileges, the program will execute a shell to run the e-commerce application; hit the shell semicolon, which is the command separator in a shell; and then run rm rf, which erases the entire file system.

Mi Java Es Su Java

Java is a relatively recent invention in the world of distributed Internet computing. It brings to fruition the concept of platform-independent code. Java works by writing code and compiling it into a special format that is then run on Java Virtual Machines. The Virtual Machine (VM) is platform specific, but the code that runs on it is not. Java allows Web browsers and remote systems to run more complex and interactive applications. The Web browser accesses a Web site and receives a Java applet from the server. This applet then runs in the Web browser and can communicate with the originating Web server. When introduced, Java transformed static Web pages into dynamic and flowing applications. Since the early days, the use of Java has expanded into many different distributed application areas such as network management, embedded Internet appliances, and other utility functions.

Java is a fine example of a language whose developers considered security in the early design stages. The initial versions of Java had a well-documented security architecture, called the sandbox, that prevented the Java applet or application from accessing system resources. As use of Java began to expand, the need arose for access to system resources outside of the sandbox. The first version of the Java Development Kit provided the use of signed applets. The model describes an applet that is digitally signed to verify its creator. When the digital signature is verified, the applet is then trusted by the local system, which allows the applet access to other system resources. This digital signature method involves a fair amount of complex programming to work correctly. It is also important to note that this security model of a digitally signed Java applet is flawed. Anyone can sign an applet. An malicious applet can be signed by the attacker and downloaded by the Web browser. The Web browser effectively verifies that the malicious applet is indeed written by the attacker, and then happily executes it, to whatever result is programmed in it.

The current and second iteration of the Java security architecture is much more powerful and flexible than earlier versions. This allows Java to enter many areas of application development previously beyond its capabilities. The new Java security architecture uses easily definable security policies and access control methods that allow an applet or application to access specific resources to varying degrees. In relation to the guidelines presented here, Java designers analyzed the various interactions and vulnerabilities present with distributed Internet applications, and arrived at a model that provides high security with extreme flexibility. The use of Java security policies requires a fair amount of reading and understanding that is beyond the scope of this chapter. For complete documentation on Java and its APIs, see http://java.sun.com.

The Shell Game and UNIX

UNIX shells form the basis of user interaction with a UNIX system. Shells are command-line interpreters that support some level of automation and programming in the form of shell scripts. These scripts are often used to automate system tasks, perform repetitive operations, and run CGI Web applications. As with Perl, areas of potential risk are input validation, race conditions, interaction with external files and programs, and the organization of functionality.

In UNIX, privileged operations can be run by a privileged user, or they can be set to run as a privileged user. There are subtle differences between the two methods. All files in a UNIX system, including applications, have a set of attributes that include user and group ownership, and a set of permissions flags. Combined, they allow file access to be strictly controlled. Normal applications are owned by a user and, depending on the access permissions, they might be run only by the owner, by a group, or by anyone on the system. The applications inherit the privileges of the user who runs them. An application that requires root privileges can be run by a non-root user, but, at those points where higher privileges are required, it will fail. To overcome this and allow normal users to access certain privileged functions, UNIX provides the SetUID and SetGID flags. When enabled, they cause the application to run as the owner or group for that application they set the User ID (UID) of the application to whomever owns it.

Many CGI and system programs require access to system resources and are SetUID root. This applies to compiled programs, such as C programs, and scripts, including Perl and the UNIX shells. Experienced UNIX users and developers often warn about the dangers of SetUID shell scripts that provide root privileges. As discussed earlier, input validation and race conditions are easily exploited when the script is not protected properly. When running a script as a privileged user, there is no easier way to hand over the keys to the castle than a weak shell script. Such scripts are particularly dangerous when programmed without security measures because a shell is interactive by nature. Users supply input, and the shell performs a function. Perl has many built-in checks and balances that allow safer SetUID usage.

Internet Appliances

Internet Appliances are those systems and devices whose entire purpose is Internet computing. All the design guidelines, programming language considerations, vulnerabilities, and operating paradigms discussed in this chapter are directly relevant to Internet Appliances. Internet Appliances often use common operating systems, applications, and methods to accomplish their goals. If the application in development follows this path, pay special attention to all of the information presented here. Some Internet Appliances are developed from scratch, incorporating only newly developed designs and technologies. Assessing security risk for these systems requires extra diligence. It is especially important to integrate security into a design process when starting from scratch.


 

Section: Chapter 29.  Secure Application Development, Languages, and Extensions

Summary

Many applications are based on previously created applications and benefit from an established and exercised security architecture. New innovations have no such luxury, therefore it is vital that they begin life with strong security measures. Consideration for security should not end at the completion of the application, system, or product development cycle, however. It is a continuous process that should remain active throughout the lifespan of the application. With every new twist, turn, and feature that the application takes, sincere consideration for its effect on the remainder of the application and the environment in which it runs needs to be examined and understood. Diligence and comprehensiveness throughout the cycle is a necessity a well-thought design can bolster the security but be undone by poor implementation, and secure code cannot secure a poor design.

The technical details involved with security require a large amount of time, experience, and exposure to grasp. An awareness of security and the ability to analyze from a security perspective allow designers, developers, and their managers to formulate questions and responses that aid the creation of a well-designed, reliable, and secure application. Security is a process, not a feature, that requires a steady but not overwhelming effort. Strive to develop with the model of least privilege necessary to accomplish a given task. Finally, always analyze the ramifications of a design or implementation decision because there is always a reaction to any action.


 



Enterprises - Maximum Security
We Only Played Home Games: Wacky, Raunchy, Humorous Stories of Sports and Other Events in Michigans
ISBN: 0000053155
EAN: 2147483647
Year: 2001
Pages: 38

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net