Software security for developers

Secure software development means consideration in every phase. Here are 9 key software security principles plus practical advice from a developer's point of view.

1 2 3 Page 2
Page 2 of 3

For example, it's a terrible idea to rely solely on a firewall to provide security for an internal-use-only application, since firewalls can be circumvented by a determined and skilled attacker. Other security mechanisms should be added to complement the protection that a firewall affords (intrusion-detection devices, security awareness training for personnel, etc.) to address different attack vectors, including the human factor.

The principle of defense in depth does not relate to a particular control or subset of controls. It is a design principle to guide the selection of controls for an application to ensure its resilience against different forms of attack, and to reduce the probability of a single point of failure in the security of the system.

Principle 2: Use a positive security model (Whitelisting)

The positive security model, often called whitelisting, defines what is allowable and rejects everything that fails to meet the criteria. This positive model should be contrasted with a "negative" (or "blacklist") security model, which defines what is disallowed, while implicitly allowing everything else.

One of the more common mistakes in application software development is the urge to "enumerate badness" or begin using a blacklist. Like antivirus (AV) programs, signatures of known bad code (malware) are collected and maintained by AV program developers and redistributed whenever there's an update (which is rather often); this can cause massive disruption of operations and personnel while signature files are updated and rescans of the system are run to detect anything that matches a new signature.

Whitelisting, on the other hand, uses an effort focused on "enumerating goodness," which is a far easier and achievable task. Programmers can employ a finite list of what values a variable may contain and reject anything that fails to appear on the list. For example, a common vulnerability in Web applications is a failure to check for executable code or HTML tags when input is entered onto a form field. If only alphabetic and numeric characters are expected in a field on the form, the programmer can write code that will cycle through the input character by character to determine if only letters and numbers are present. If there's any input other that numbers and letters, the program should reject the input and force a reentry of the data.

Principle 3: Fail securely

Handling errors securely is a key aspect of secure and resilient applications. Two major types of errors require special attention:

  • Exceptions that occur in the processing of a security control itself
  • Exceptions in code that are not "security-relevant"

It is important that these exceptions do not enable behavior that a software countermeasure would normally not allow. As a developer, you should consider that there are generally three possible outcomes from a security mechanism:

  • Allow the operation
  • Disallow the operation
  • Exception

In general, you should design your security mechanism so that a failure will follow the same execution path as disallowing the operation. For example, security methods such as "isAuthorized" or "isAuthenticated" should all return false if there is an exception during processing. If security controls can throw exceptions, they must be very clear about exactly what that condition means.

Principle 4: Run with least privilege

The principle of least privilege recommends that user accounts have the least amount of privilege required to perform their basic business processes. This encompasses user rights and resource permissions such as:

  • CPU limits
  • Memory
  • Network permissions
  • File system permissions

The principle of least privilege is widely recognized as an important design consideration in enhancing the protection of data and functionality from faults (i.e., fault tolerance) and malicious behavior (i.e., computer security).

The principle of least privilege is also known as the principle of least authority (POLA).

Principle 5: Avoid security by obscurity

Security by obscurity, as its name implies, describes an attempt to maintain the security of a system or application based on the difficulty in finding or understanding the security mechanisms within it. Security by obscurity relies on the secrecy of the implementation of a system or controls to keep it secure. It is considered a weak security control, and it nearly always fails when it is the only control.

A system that relies on security through obscurity may have theoretical or actual security vulnerabilities, but its owners or designers believe that the flaws are not known, and that attackers are unlikely to find them. The technique stands in contrast with security by design.

An example of security by obscurity is a cryptographic system in which the developers wish to keep the algorithm that implements the cryptographic functions a secret rather than keeping the keys a secret and publishing the algorithm so that security researchers can determine if it is bullet-proof enough for common security uses.

Principle 6: Detect intrusions

Detecting intrusions in application software requires three elements:

  • Capability to log security-relevant events
  • Procedures to ensure that logs are monitored regularly
  • Procedures to respond properly to an intrusion once it has been detected

Principle 7: Don't trust infrastructure

You'll never know exactly what hardware or operating environment your applications will run on. Relying on a security process or function that may or may not be present is a sure way to have security problems. Make sure that your application's security requirements are explicitly provided though application code or through explicit invocation of reusable security functions provided to application developers to use for the enterprise.

Principle 8: Don't trust services

Services can refer to any external system. Many organizations use the processing capabilities of third-party partners who likely have different security policies and postures, and it's unlikely that you can influence or control any external third parties, whether they are home users or major suppliers or partners. Therefore, implicit trust of externally run systems is not warranted. All external systems should be treated in a similar fashion.

For example, a loyalty program provider provides data that is used by Internet banking, providing the number of reward points and a small list of potential redemption items. Within your program that obtains this data, you should check the results to ensure that it is safe to display to end users (does not contain malicious code or actions), and that the reward points are a positive number and not improbably large (data reasonableness).

Principle 9: Establish secure defaults

Every application should be delivered secure by default out of the box! You should leave it up to users to decide if they can reduce their security if your application allows it. Secure by default means that the default configuration settings are the most secure settings possible—not necessarily the most user-friendly. For example, password aging and complexity should be enabled by default. Users may be allowed to turn these two features off to simplify their use of the application and increase their risk based on their own risk analysis and policies, but doesn't force them into an insecure state by default.

Next: programming best practices

Programming Best Practices

Beyond the principles above, programmers have a special duty to assure that the design specifications are implemented using Defensive Programming, which like a defensive driving, is intended to insulate an application from negligent or willfully damaging activity while it's in use.

Input validation and handling

input validation approaches

[Figure: Input Validation Approaches]

Improper input handling is one of the most common weaknesses identified across applications today. Poorly handled input is a leading cause of critical vulnerabilities that exist in systems and applications.

"Validation can include checks for type safety (integer, floating point, text, etc.) and syntax correctness. String input should be checked for length (minimum and maximum number of characters) and "character set" validation, while numeric input types such as integers and decimals can be validated against acceptable upper and lower bound of values. When combining input from multiple sources, validation should be performed on the concatenated result and not against the individual data elements alone. This practice helps avoid situations in which input validation may succeed when performed on individual data items but fail when done on a concatenated string from all the sources." [ref2]

There are several techniques for validating input data. Each has varying levels of security, with the better ones following the practice of using a positive security model, and is illustrated in the Input Validation Approaches figure.

Avoiding Cross-Site Scripting attacks

In cross-site scripting (XSS), the attacker attempts to inject client-side script code on the browser of another user of the application. The injected code submitted will pass through the application and be delivered to the victim user.

The following techniques are used in conjunction with one another to protect an application from XSS attacks:

  • Output filtering

    —Encode fields to escape HTML in output.

    —Most languages provide functions for HTML encoding.

    —Example of HTML entities:

    The " > " character is encoded to > or >

    —Force a "charset" encoding in the HTTP response.

    Content-Type: text/html; charset=[encoding]

    meta http-equiv="Content-Type" (...) charset=[encoding]/

  • Cookie security —Enable the following cookie flags:

    HttpOnly

    Secure

Preventing injection attacks

There are several types of injection attacks: SQL injection, LDAP injection, mail command injection, null byte injection, SSI injection, XPath injection, XML injection, XQuery injection, etc. Here we will examine the techniques to prevent the most pernicious of all content injection attacks—SQL injection.

  • Validate all input parameters accepted by the application.
  • Use a secure way to create SQL queries—"PreparedStatement" or "CallableStatement."
  • Parameterized queries are not vulnerable to SQL injection attacks even in the absence of input validation.

    —They automatically limit the scope of user input to data, and the input can never be interpreted as part of the SQL query itself.

    —They can perform data type checking on parameter values that are passed to the query object.
  • If you are not using parameterized queries, consider filtering all potentially dangerous characters:

    —Single Quotes

    —Pattern matching characters in LIKE clauses (%,?,[,_)

Authentication and Session Management

There are three well-accepted methods for identifying an individual to a computer system. You can use:

  • Something you know—your password
  • Something you have—a security token device or digital certificate
  • Something you are—your fingerprint or retina scan

Applications that handle very sensitive data should consider using more than one authentication method ("multifactor authentication")—for example, requiring a security token and a password or PIN number (commonly used in corporate VPN connections from remote sites).

Establishing the user's identity is key for enforcing privileges and access controls. At various points, the application will require the user to provide some proof of identity:

  • Log-in
  • Password reset
  • Before performing sensitive transactions

An attacker can target each of these in different ways in an attempt to impersonate a legitimate application user. The attacker wants to gain access to the data that a user can access while using the application.

Defensive techniques to counter attacks on log-in functions include:

  • Develop generic "failed log-in" messages that do not indicate whether the username or the password was incorrect.
  • Enforce account lock-out after a predetermined number of failed log-in attempts.
  • Account lock-out should trigger a notification sent to appropriate personnel and should require manual reset (via the Help Desk).
  • Implement server-side enforcement of password syntax and strength (length, character complexity requirements, etc.)

Defenses to counter password reset attacks include:

  • Consider requiring manual password reset. Automated password reset mechanisms can greatly reduce administrative overhead, but they are susceptible to being used for an attack.
  • Require users to answer an open-ended security question to initiate a password reset.
  • Consider using multiple security questions instead of just one.
  • Generate a strong and unique new password once the reset has been performed, or allow to the user to choose one based on the complexity requirements.
  • Force users to change their password as the only function they can access once their password is reset and they log in using it.

Access Control

Access control authorization is the process whereby a system determines whether a specific user has access rights to a particular resource. To decide whether a specific user has or does not have access to a resource, the application needs to know the identity of the user. Many applications use an "all or nothing" approach, meaning that once they are authenticated, all users have equal privilege rights. There are several strategies to implement access privileges and permissions. A common method is to define roles, assign permissions to the roles, and place users in those roles.

1 2 3 Page 2
Page 2 of 3
Make your voice heard. Share your experience in CSO's Security Priorities Study.