Tech Blog

Injection attacks - how to prevent with Liferay

A deep dive into the vulnerabilities and how to fix them

Simone Cinti
Software Architect
5 minutes read
liferay, security, xss, sql, injection, attack, and vulnerability
This article is also available in Italiano 🇮🇹

An Injection attack is the submission of malicious code or commands that could be interpreted and executed by the remote application.

Injection is actually first on OWASP Top Ten list, and includes a wide range of different subtypes depending on whether the nature of command or language.

Taken together, injection attacks are a huge percentage of the serious application security risk. Many organizations have poorly thought through security controls in place to prevent injection attacks. OWASP, about the injection attacks

In this article we will focus our attention on two particular types of injection:

  • SQL Injection (SQLi) – when the injection of SQL commands occours
  • Cross-Site Scripting (XSS) – when the injection of a browser-side script occours

then we will see how Liferay Portal solutions helps you to prevent injection attacks, keeping up the security level.

Liferay Portal Security

Liferay follows the OWASP Top 10 and CWE/SANS Top 25 lists to ensure the highest level of protection against several known attacks.

Here are some kind of attacks included in both OWASP Top Ten and CWE/SANS Top 25 and for which Liferay offers the highest level of protection against:

Liferay takes care about security, in both the community (CE) and the enterprise (DXP) editions, keeping always up-to-date the known vulnerabilities list, and also having their own security statement.

SQL Injection (SQLi)

SQL Injection is the injection of SQL commands (or queries) as untrusted user input data from client-side.

Depending on user privileges on target database, the attacker could:

  • insert, update or delete rows on existing tables
  • read sensitive data from tables (select)
  • drop tables
  • execute administration commands, such as perform the shutdown getting also a DoS attack

One of the benefits using Liferay is that the persistence layer generated by the Service Builder is built to prevent SQL Injection attacks (read more).

However, when the default solution provided by the Service Builder is not enough, Liferay helps you to maintain the highest level of protection against SQL Injection attacks even defining customized finders. Nevertheless, you should pay attention when implementing a custom Finder and follow the instructions provided by the official Liferay documentation.

In particular:

  • each custom query should have its own <custom-sql> element into custom-sql/default.xml and the sql command in a <![CDATA[...]]> section, without terminating semi-colon;

  • the query parameters should always set using QueryPos which performs escaping. Validation of untrusted data is mandatory as well as order-by column names from request parameters.

Strict validation of untrusted data coming from HTTP-request parameters is the key to prevent the SQL injection.

In the following example we will show why the order-by column names should never came directly from the request without prior validation, and how an injection attack on the order-by clause can significatively reduce the total attempts needed to guess a column value.

Suppose we have extended our model adding a new Vendor entity, introducing a vulnerability on the finder implementation:

sql = StringUtil.replace(sql, "[$ORDER_BY$]", " ORDER BY " + orderBy);  // unsafe

the safe version will make use of the BasePersistenceImpl.appendOrderByComparator():

StringBundler sb = new StringBundler();
if (orderByComparator != null) {
  appendOrderByComparator(sb, "Vendor.", orderByComparator);
}
sql = StringUtil.replace(sql, "[$ORDER_BY$]", sb.toString());

with such vulnerability, for instance, the attacker could try to use the SearchContainer's orderByCol parameter on the vendors search page, to inject the following sql command by the form submit action:

_portletName_INSTANCE_instanceId_orderByCol=

(CASE WHEN (
    SELECT substring(CONVERT(userId, CHAR),1,1)
    FROM user_
    WHERE emailAddress = 'test@liferay.com'
  ) = '2'
THEN name ELSE vendorId END)

In this way, the attacker can guess the i-th digit of the userId having test@liferay.com as e-mail address just verifying the results ordering in page. Each injection attempt will change the results order, whether the exact digit has guessed or not. Supposing userIds of five digits, the attacker can easily guess the userId in (5 * 9) - 1 = 44 attempts on the worst case, when a brute-force attack can require 9 * 10^4 attempts. The same attack could also be used to guess the screen-name or the encrypted password, but this will require more attempts.

Some useful tips to protect against SQL Injection attacks

  • Liferay ServiceBuilder helps you to keep up the security level, but pay attention providing custom Finder implementations: when using the OrderByComparator always perform a strict validation for column names in order-by clauses.

  • Keep in mind that vulnerability to SQL Injection is also an input validation problem: in order to prevent the injection attack, string concatenation should be avoided expecially on untrusted data from the request without prior validation.

  • Make use of query parameters, because the output escaping will neutralize the injection attack.

Cross-Site Scripting (XSS)

Cross-site scripting (XSS) is also known as “Improper neutralization of input during web page generation” and is one of the most common attacks according to OWASP Top 10 and CWE/SANS Top 25.

According to the OWASP reclassification, XSS attacks can be classified in two main types:

  • Server XSS: when untrusted user input data is included on server response

  • Client XSS: when untrusted user input data is added to DOM or evaluated through unsafe JavaScript call

Another possibile classification of XSS attacks is about data persistency:

  • Stored (Persistent or Type-I): when untrusted user input data is stored on the target server persistent storage like database

  • Reflected (Non-Persistent or Type-II): when untrusted user input data is returned in server response without being permanently stored.

Types of XSS
Types of XSS

Liferay Portal is built to prevent XSS attacks

When you are developing new portlet component, you should make use of the standard Liferay frontend taglib. The taglib elements (such as <aui: >, <liferay-ui: >, <clay: > ) are safe because they always perform output escaping where needed and in the right way, so they will neutralize almost any kind of XSS attack.

Liferay's HtmlUtil is the right way to safely perform output escaping.

Now let's show some examples of Server XSS attacks...

Reflected Server XSS Attack - example

  1. 💻 CLIENT: malicious code is submitted via the HTTP-request parameters or body
firstName=<script>alert('XSS!')</script>
  1. 🌍 SERVER: dynamically generated response includes untrusted data (because missing HTML-escaping):
<p> Hello, <%= firstName %> </p>
  1. 💻 CLIENT: the browser shows the response page executing the injected malicious code
<p>
  Hello,
  <script>
    alert("XSS!");
  </script>
</p>

Stored Server XSS Attack - example

  1. 💻 CLIENT: malicious code is submitted via the HTTP-request parameters or body
firstName=';alert(document.cookie);'
  1. 🌍 SERVER: the model entity is updated on persistence layer with untrusted data:
...
customer.setFirstName(firstName);
_customerService.updateCustomer(customer);
  1. 🌍 SERVER: dynamically generated response includes stored untrusted data, exposing the application to an XSS vulnerability because it misses the JS-escaping:
<aui:script>
 var firstName = '<%= c.getFirstName() %>';
...
</aui:script>
  1. 💻 CLIENT: the browser shows the response page executing the injected malicious code, which now is also stored, even after a page refresh.
<script>
 var firstName = '';alert(document.cookie);'';
...
<script>

XSS attacks could be neutralized performing output escaping:

  • every time you dynamically set an attribute value or content in a raw-HTML element from an untrusted source (HTTP-request parameters);

  • when using Java variables <%= ... %> on jsp inside <script> elements;

  • when defining customized taglibs.

Here are some examples of HtmlUtil usage:

<p> Hello, <%= HtmlUtil.escape(firstName) %> </p>
<div title="<%= HtmlUtil.escapeAttribute(title) %>">...</div>
<a href="<%= HtmlUtil.escapeHREF(detailsURL) %>">...</a>
<aui:script>
 var firstName = '<%= HtmlUtil.escapeJS(c.getFirstName())%>';
...
</aui:script>

Some useful tips to protect against XSS attacks

  • untrusted data should always be validated and sanitized before stored or processed for the output response. Enums or constants could be used for values mapping when the input parameters are strings;

  • use Liferay frontend taglibs, and HtmlUtil to perform output escaping. Escaping the output values in dynamic response page will neutralize injected code as well as <script> elements or JavaScript snippets, preventing the execution by the web-browser;

  • be aware of custom JavaScript code which directly modifies DOM nodes. Unsafe JavaScript code could reveal in a vulnerability to Client XSS Attacks.

Conclusion

Injection is one of the most common attack on web applications. Defense strategies against Injection attacks are:

  • prevention by validation of untrusted data before it could be stored or processed for the output response;

  • neutralization by output escaping or sanitization.

Liferay ensures the highest security level in both the community (CE) and the enterprise (DXP) editions, and supports the developer to keep the security level high even when providing new solutions.

written by
Simone Cinti
Software Architect
Software Architect for SMC, he's involved in the design and development of Liferay Portal based solutions.

You might also like…