In previous post, we covered Validating on Input as the first "preferred" line of defense against XSS Vulnerabilities. In this post, we will discuss Escaping on Output mechanism which basically helps addressing malicious XSS scripts that already exist in your application (Ex. malicious scripts injected directly into backend database, or user input submitted prior to implementing some XSS input validation mechanism), by making sure that such scripts won't be executed when they are rendered in the application, but will rather be rendered as plain text.
As I explained before, while Validating on Input can be sufficient in most cases, it is a good practice to bundle that with some sort of Escaping on Output mechanism in order to implement a concrete XSS solution.
For example, if you have First Name text field in Employee form and you populate it with something like:
"<script>alert("Your application is hacked!!"); </script>"
Then you submit your input (assuming you don't have some sort of XSS input validation implemented yet). The First Name will be stored in Employees table successfully. Now, if you have a report (interactive report or classic report) on Employees table that displays First Name as one of its columns (and it does not escape special characters), the JavaScript will be automatically executed upon rendering the report. So in this case, you will get a "Your application is hacked!!" alert when you run that report.
Now, in real XSS attacks, attackers won’t just inject an alert that indicates that your application has been hacked (well, unless they just want to scare you and brag about it), but they will actually inject some malicious scripts that can cause several types of damage, such as: hijacking user's session, hacking user's account credentials, accessing and stealing sensitive data, and much more. So, you want your application to render such scripts as plain text without being executed at runtime to mitigate such threat, which can be accomplished by making sure that your APEX application escapes special characters where needed.
In APEX, there are mainly three types of components that usually render user’s input data and you need to make sure they escape on output:
Text Items
APEX naturally escapes special characters when rendering values of text items, so you don't need to worry about text items in this case.
Display Only Items
APEX offers a native property to escape special characters when displaying values of Display Only items. Setting the Escape special characters property to Yes ensures that the item's value will be displayed “as is” without being executed at run time. For example, if you have a Display Only item with source set to static text:
"<script>alert("Your application is hacked!!"); </script>"
However, if you set Escape Special Characters property to No, then you will get a “Your application is hacked!!” alert when the page that has that item is rendered.
Report Columns (Interactive and Classic reports)
Similar to Display Only items, APEX offers a native properties to escape special characters when displaying report columns. However, report columns can be a little tricky, as you might have certain columns that are meant to be clickable links or icons (Ex. Edit icon/link and download attachment icon/link). Such columns are expected to have some JavaScript that need to be executed. In such cases, you do not want not make these columns escape on output.
Classic Reports:
When you create a classic report, you can edit each column and set the Display As property as needed. In the context of XSS, we are mainly interested in two options:
o Standard Report Column: if you want underlying JavaScript/HTML to be executed when column is rendered (Ex. Edit, View, Download links/icons) when the column is rendered.
o Display as Text (escape special characters): if you want underlying JavaScript/HTML to be rendered as plain text (i.e. not executed).
Interactive Reports:
When you create an interactive report, you can edit each column and set the Display As property as needed. In the context of XSS, we are mainly interested in three options:
o Standard Report Column: if you want underlying JavaScript/HTML to be executed when column is rendered (Ex. Edit, View, Download links/icons) when the column is rendered.
o Display as Text (escape special characters): if you want underlying JavaScript/HTML to be rendered as plain text (i.e. not executed).
o Remove HTML and escape special characters: if you want to completely strip out JavaScript/HTML when column is rendered.
I will now provide some sample queries that you can build on to come up with a programmatic approach to identify and fix APEX items and columns that need to escape special characters. Again, these are just sample queries that you will likely need to adjust and tweak to meet your specific XSS requirements.
Display Only items
Step 1: Get a list of candidate items (i.e. items that currently do not escape HTML/JavaScript)
Step 2: Fix required items
Review this list of candidate items obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified items with a query like this:
P.S. Replace apex_050100 with the correct schema name based on your current APEX version (this was coded and tested in APEX 5.1)
Classic Reports
Step 1: Get a list of candidate columns (i.e. columns that currently do not escape HTML/JavaScript)
You can add more filters to narrow down the list of candidate columns by utilizing other metadata such as:
Step 2: Fix required columns
Review the list of candidate columns obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified columns with a query like this:
Interactive Reports
Step 1: Get a list of candidate columns (i.e. columns that currently do not escape HTML/JavaScript)
Step 2: Fix required columns
Review this list of candidate columns obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified columns with a query like this:
Note 1: The queries above deal with APEX metadata, which can cause serious issues to your application if not handled with extra precaution. I suggest that you take backups of your APEX application and updated tables in case anything goes wrong.
Note 2: Page Designer IDE (introduced in APEX 5.0 and become the default APEX IDE since then) has made some changes to Display As property values for Interactive and Classic Reports. “Standard Report Column” and “Display as Text (escape special characters)” options have been removed and replaced by “Plain Text” along with a “Escape Special Characters” property under Security section that you can set to Yes or No (similar to Display Only Items). However, this is just a difference in how these options and properties are displayed in Page Designer, so if you switch back to legacy component view, you should see old options.
Oracle has nicely documented property naming differences between Page Designer and Component View, which can be found here.
Also, steps needed to switch from Page Designer to legacy component view (not recommended) can be found here.
Note 3: Radio Groups and Checkboxes also have Escape Special Characters property. However, since these items' text (i.e. options) usually do not come from user’s input directly but rather come from lookup data created by developers, I have not cover them in this post.
As I explained before, while Validating on Input can be sufficient in most cases, it is a good practice to bundle that with some sort of Escaping on Output mechanism in order to implement a concrete XSS solution.
For example, if you have First Name text field in Employee form and you populate it with something like:
"<script>alert("Your application is hacked!!"); </script>"
Then you submit your input (assuming you don't have some sort of XSS input validation implemented yet). The First Name will be stored in Employees table successfully. Now, if you have a report (interactive report or classic report) on Employees table that displays First Name as one of its columns (and it does not escape special characters), the JavaScript will be automatically executed upon rendering the report. So in this case, you will get a "Your application is hacked!!" alert when you run that report.
Now, in real XSS attacks, attackers won’t just inject an alert that indicates that your application has been hacked (well, unless they just want to scare you and brag about it), but they will actually inject some malicious scripts that can cause several types of damage, such as: hijacking user's session, hacking user's account credentials, accessing and stealing sensitive data, and much more. So, you want your application to render such scripts as plain text without being executed at runtime to mitigate such threat, which can be accomplished by making sure that your APEX application escapes special characters where needed.
In APEX, there are mainly three types of components that usually render user’s input data and you need to make sure they escape on output:
Text Items
APEX naturally escapes special characters when rendering values of text items, so you don't need to worry about text items in this case.
Display Only Items
APEX offers a native property to escape special characters when displaying values of Display Only items. Setting the Escape special characters property to Yes ensures that the item's value will be displayed “as is” without being executed at run time. For example, if you have a Display Only item with source set to static text:
"<script>alert("Your application is hacked!!"); </script>"
and you set Escape Special Characters property to Yes, the item's value will be rendered as plain text and it won't be executed:
Report Columns (Interactive and Classic reports)
Similar to Display Only items, APEX offers a native properties to escape special characters when displaying report columns. However, report columns can be a little tricky, as you might have certain columns that are meant to be clickable links or icons (Ex. Edit icon/link and download attachment icon/link). Such columns are expected to have some JavaScript that need to be executed. In such cases, you do not want not make these columns escape on output.
Classic Reports:
When you create a classic report, you can edit each column and set the Display As property as needed. In the context of XSS, we are mainly interested in two options:
o Standard Report Column: if you want underlying JavaScript/HTML to be executed when column is rendered (Ex. Edit, View, Download links/icons) when the column is rendered.
o Display as Text (escape special characters): if you want underlying JavaScript/HTML to be rendered as plain text (i.e. not executed).
Interactive Reports:
When you create an interactive report, you can edit each column and set the Display As property as needed. In the context of XSS, we are mainly interested in three options:
o Standard Report Column: if you want underlying JavaScript/HTML to be executed when column is rendered (Ex. Edit, View, Download links/icons) when the column is rendered.
o Display as Text (escape special characters): if you want underlying JavaScript/HTML to be rendered as plain text (i.e. not executed).
o Remove HTML and escape special characters: if you want to completely strip out JavaScript/HTML when column is rendered.
Now, you know what properties should be utilized when you design and develop your APEX applications. I actually suggest that you add this to your code review checklist and best practices to make sure your applications stay XSS compliant. However, if you already have an APEX application in production, and you want to make sure that your application does escape HTML/JavaScript where required, you might want to consider writing some queries against APEX metadata to identify gaps. This should save you a lot of time and efforts compared to having to manually review all your APEX items and report columns, which can take forever especially for large APEX applications.
I will now provide some sample queries that you can build on to come up with a programmatic approach to identify and fix APEX items and columns that need to escape special characters. Again, these are just sample queries that you will likely need to adjust and tweak to meet your specific XSS requirements.
Display Only items
Step 1: Get a list of candidate items (i.e. items that currently do not escape HTML/JavaScript)
select to_char(item_id) item_id,application_id, page_id, page_name, region,item_name, label, display_as, item_source, item_source_type from APEX_APPLICATION_PAGE_ITEMS where application_id in (100) -- replace this with your desired application ID and display_as_code in ('NATIVE_DISPLAY_ONLY') -- display only items and nvl(ESCAPE_ON_HTTP_OUTPUT,' ') ='No' -- Escape Special Characters currently set to No and nvl(condition_type_code,' ') <> 'NEVER' -- you may want to exclude inactive/unused items order by 2,3,4,5,6,7
Step 2: Fix required items
Review this list of candidate items obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified items with a query like this:
update apex_050100.wwv_flow_step_items
set escape_on_http_output = 'Y' -- escape special characters
where id in (111,222,333.... ); -- list of comma separated item IDs from query above
Commit;
P.S. Replace apex_050100 with the correct schema name based on your current APEX version (this was coded and tested in APEX 5.1)
Classic Reports
Step 1: Get a list of candidate columns (i.e. columns that currently do not escape HTML/JavaScript)
select to_char(t1.region_report_column_id) id, t1.application_id, t1.page_id, t1.page_name, t1.region_name, t1.column_alias, heading,Regexp_replace(heading, '<.+?>') column_name, t1.COLUMN_LINK_URL, t1.COLUMN_LINK_TEXT
from APEX_APPLICATION_PAGE_RPT_COLS t1, APEX_APPLICATION_PAGE_REGIONS t2
where t1.application_id in (100) -- replace with your desired application ID
and display_as_code ='WITHOUT_MODIFICATION' -- currently does not escape special characters
and NVL(t1.column_is_hidden,'yes') = 'No' -- not hidden
and nvl(t1.condition_type,' ') <> 'NEVER' --only active columns
and t1.application_id = t2.application_id
and t1.page_id = t2.page_id
and t1.region_id = t2.region_id
and upper(nvl(t2.condition_type,' ') ) <> 'NEVER' -- only active regions/reports
order by 2,3,4,5,6,7
You can add more filters to narrow down the list of candidate columns by utilizing other metadata such as:
and COLUMN_LINK_TEXT is not null or UPPER(NVL(COLUMN_ALIAS,' ')) like '%LINK%' or UPPER(NVL(HEADING,' ')) like '%ACTION%' or UPPER(NVL(HEADING,' ')) like '%DELETE%' or UPPER(NVL(HEADING,' ')) like '%EDIT%' or UPPER(NVL(HEADING,' ')) like '%SELECT%'
Step 2: Fix required columns
Review the list of candidate columns obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified columns with a query like this:
update APEX_050100.WWV_FLOW_REGION_REPORT_COLUMN set display_as = 'ESCAPE_SC' -- escape special characters where id in (111,222,333.... ); -- list of comma separated column IDs from query above Commit;
Interactive Reports
Step 1: Get a list of candidate columns (i.e. columns that currently do not escape HTML/JavaScript)
select to_char(t1.column_id) id,t1.application_id, t1.page_id, t2.page_name, t1.region_name, t1.column_type, t1.column_alias,Regexp_replace(nvl(report_label,form_label), '<.+?>') column_name, column_link, column_linktext from APEX_APPLICATION_PAGE_IR_COL t1 , APEX_APPLICATION_PAGES t2 , APEX_APPLICATION_PAGE_REGIONS t3 where t1.application_id in (129,159,169,299) and display_text_as ='WITHOUT_MODIFICATION'-- currently does not escape special characters and nvl(display_condition_type_code,' ') <> 'NEVER' -- only active columns and t1.page_id = t2.page_id and t1.application_id = t2.application_id and t1.region_id = t3.region_id and t1.application_id = t3.application_id and t1.page_id = t3.page_id and upper(nvl(t3.condition_type,' ') ) != 'NEVER' -- only active regions/reports order by 2,3,4,5,6,7Similar to Classic Reports, you can add more filters to narrow down the list of candidate columns returned by the query.
Step 2: Fix required columns
Review this list of candidate columns obtained by the query above and identify those that should be updated (i.e. should escape special characters). You can then update identified columns with a query like this:
update APEX_050000.wwv_flow_worksheet_columns set display_text_as ='ESCAPE_SC' -- escape special characters where id in (111,222,333.... ); -- list of comma separated column IDs from query above Commit;
Note 1: The queries above deal with APEX metadata, which can cause serious issues to your application if not handled with extra precaution. I suggest that you take backups of your APEX application and updated tables in case anything goes wrong.
Note 2: Page Designer IDE (introduced in APEX 5.0 and become the default APEX IDE since then) has made some changes to Display As property values for Interactive and Classic Reports. “Standard Report Column” and “Display as Text (escape special characters)” options have been removed and replaced by “Plain Text” along with a “Escape Special Characters” property under Security section that you can set to Yes or No (similar to Display Only Items). However, this is just a difference in how these options and properties are displayed in Page Designer, so if you switch back to legacy component view, you should see old options.
Oracle has nicely documented property naming differences between Page Designer and Component View, which can be found here.
Also, steps needed to switch from Page Designer to legacy component view (not recommended) can be found here.
Note 3: Radio Groups and Checkboxes also have Escape Special Characters property. However, since these items' text (i.e. options) usually do not come from user’s input directly but rather come from lookup data created by developers, I have not cover them in this post.