Friday 23 May 2014

APEX Security hiccups

Last coupe of days I spent playing with Apex security issues and wanted to share it with you.
Basically the story behind it was inspired by the awesome work by Recx guys and their Hands-On Oracle Application Express Security book.

After I read it - you know how it is, when you are almost quite confident that your apps are safe but again there is that thing that bothers you and leaves that nasty "What if?" question in your head. 
Could I have done something even better and what are the wholes that were potentially left behind?
So many different APEX version are used from project to project so many topics on this subject and understandably it is very hard to keep track. 

After all I thought at least what I can do is look back and see where some improvements can be made. Where are the most obvious security issues and how can I prevent it happen in the future. 

Cross-Site Scripting - XSS

One of the most common problems that all web applications are exposed to since JavaScript is a critical part of everyday application. No wonder it is commonly used by external parties for attacks on modern systems.
Cross-site scripting is an attack where JavaScript within the app is specified not by a developer but instead by a malicious user.  There are two types of XSS attacks: Reflective and Stored Cross-Site scripting. Difference being in last user submits 'dangerous' information to target site and then every other user that displays the data might be exposed to a malicious script information.
XSS issue arises because data provided by the user is included within the site content without any (or insufficient) validation or encoding to ensure that data being used is safe. Best way how we can protect against XSS is to validate and sanitize the received data. It most cases I would recommend doing both.
 

Within APEX there are two places where this inclusion of untrusted data causes most problems: in reports, and in PL/SQL blocks that output data. 

Report issues 

1. Report Column --> Display type
For display report problems with "Standard Report Column" we all heard of as many times we needed to display data in the reports in formatted way so we may opted for:
select  "ID",
'<b>' ||username || '</b>' "USERNAME",
"FIRSTNAME",
"SURNAME"
from "DEMO_USERS"

Plus in pre APEX 4.0 release display as "Standard Report Column"  was a default way report columns were created. This exposes our code to all sorts of XSS attacks because by simply having:
Monaco<script>alert('Hello there');</script>

would cause a pop-up Java alert to be shown.  This means that our data was inflected. Thankfully this can be easily prevented by the use of escape functions so a solution would as simple as
'<b>' || htf.escape_sc(username) || '</b>' "USERNAME"


In 4.2.x version we can also use apex_escape.html() function.

In order to prevent problems like this we can:
- Set the display type of each column to “Display as Text (escape special characters).” 
- Using a PL/SQL function to escape the column within the Region Source query if having to use display as "Standard Report Column"  
Same as you I was familiar with the story untill this part but few of these next  features actually caught me of guard. 


2. Report Column --> HTML Expressions
The Report Column definition includes a Column Formatting section which can be used to change the display format of the column data, and it is often used to style a column. Things that we can use here are template variables (#VALUE#) and substitution variables. These values are automatically stripped and escaped by APEX to counter a Cross-Site Scripting threat. Or are they? It really depends on a version you are using.

However, try putting this as your HTML Column expression in your report:
<a href ="javascript:alert('Name: #SURNAME#')"> #FIRSTNAME#</a>



The report now renders a link (in the Dummy column), and when you click on it a message box displays with the user’s full name JavaScript alert. This is what I was expecting given that I have not changed the default Display type of any columns. But what actually happens next when column surname gets changed?


If you modify a row for Branko making its surname be:  Hmhhh');alert(document.cookie);//

Now you actually get two pop-ups even though both columns are perfectly escaped and have not been modified from its default setting. How is this possible? I certainly wasn't expecting this.

It is all about the context in which template variable is used and variable not being escaped properly.

How to resolve this issue? The source query of the report needs modifying to contain an additional column that is escaped so that it can safely be used in JavaScript. 
 apex_escape.js_literal('Name: ' || surname) as htmltext

 and changed my HTML expression for Dummy column to:


<a href ="javascript:alert(#HTMLTEXT#)"> #FIRSTNAME#</a> 
     
        This is something definitely to be aware of.       
Try avoiding using the column template variables within tag attributes.
Where column templates need to be used, define the report with an additional column that makes the data safe using apex_escape.js_literal
Do not use item substitution values in APEX 4.2 or above.


3. Report Column --> Column link
In your reports when creating a link from your columns by using substitution variables to set item values within the Name/Value section of the Column Link settings also leads to a Cross-Site Scripting vulnerability. Because the substitution variables are not encoded properly!
To prove a point here create a page with a report and on the same page have a text item. Once created create a link on one of your columns setting the value of the same PX_TEXT item to its own value. Now set a value of PX_TEXT item to: 
:"><script>alert('Exposed Link');</script> 
     
    In 4.2.5 version this issues has been fix and you should get no errors, but I was definitely able to reproduce a problem on Application Express 4.0.0 version. 

It comes town to which version you are using and your system might be exposed. To prevent this:   Substitution variables should be protected and not based on untrusted data when used to set an item’s value. If using event attributes in link attributes with a column template variable, you’ll need to use a specifically escaped column in the report as in example 2.

4. Report Column --> List of values 
    
Even though not that common, applications that define a classic report column based on a List of Values (LOV) query are also prone to XSS attacks. 

Data being returned and displayed via the LOV is not encodedCreate a report, make one of the columns be defined as LOV.  As simple as:
select firstname, id from mytable
If you change you first name value to be  
Brian<script>alert('Hello there')</script>
Once the page is run you would get a pop up alert for every row in the table even if using APEX 4.2.5. 

Solution would be to always use apex_escape.html() function in your LOV query for first name. In prior version you can make use of htf.escape_sc() function.

Good news is that for LOV queries in other controls (select lists or pop-up boxes) display value is escaped automatically from APEX 4.0 onward so you do not need to worry about them.

 Direct output

When using htp package to directly output text to the page it can lead to XSS attack if data being used is untrusted. 
htp.p('DEMO -' || :PX_TEXT1 || ' followed by ' || :PX_TEXT2); 
What happens if you make your TEXT_1:   
<script>document.write('
and your TEXT_2:
 ');document.location=('http://www.google.com');</script>

When you click Submit JavaScript executes and you’re redirected to Google. In fact, you can’t go back to the APEX application because every time the page loads, the values from your session state for the two text boxes are displayed and interpreted by the browser as JavaScript instructions to navigate to Google!
To prevent it 'ensure' the data with the:  
  - use the apex_escape functions (from APEX 4.2.x)
  - use htf.escape_sc or apex_javascript.escape (in older versions of APEX)

Next time I will be looking at SQL injection issues and what we can do to prevent these. Plus does Hidden Item really means it is protected if you set Value Protected to 'Yes'?

Till then.

Take care.
 SLino