The following examples demonstrate a SQL query and an insert statement as a developer would use them in APEX: -- This query is used in a report region select first_name,last_name from e
Trang 1Kochhar - 17000
De Haan - 17000
Hunold - 9000
Ernst - 6000
Austin - 4800
Pataballa - 4800
Gietz - 8300
107 Rows Returned
Now we can see all 107 rows! How did this happen?
1 By including the single quote after Grant, the where predicate has the correct syntax.
2 Adding or 1 = 1 essentially negates the where predicate and returns every row since 1
will always equal 1
3 The at the end of the statement is the comment operator in Oracle SQL, which comments
out the trailing single quote that is in the original procedure Remember that we already closed the quote in step 1
The addition of this predicate completely changes the result set of the query Instead of simply passing different last names to the procedure, we are able to construct parameters that will modify the structure of the query
The more an attacker knows about a system, the more effectively he can plan an attack In the next example, we will pass a more sophisticated parameter to the same procedure to start investigating the data dictionary views
hr@aos> exec sql_injection(q'!ZZZ' union select null,null,
table_name last_name,null,null,null,null,null,null,null,null
from user_tables !');
COUNTRIES
DEPARTMENTS
EMPLOYEES
JOBS
JOB_HISTORY
LOCATIONS
REGIONS
-7 Rows Returned
Here’s the breakdown of this attack:
1 The first part of the parameter is ZZZ’ This simply returns no rows from the employees
table and closes the first quote This was intentional since we already have all of the rows
in the preceding example
2 Next, we union in our own query The syntax of a union operator is such that both queries
need to have the same number and type of columns, so an attacker would need to keep adding null columns until he received a result
3 Once again, we comment out the trailing single quote since we already closed it in step 1.
A variation on this attack might be to query the USER_TAB_COLUMNS table to find all the columns in the employees table We could then union in our own query of the employees table
Trang 2to select columns that we were not intended to see, such as Social Security Number or Government Identification Number
Example 2: The Right Way
The fatal flaw with the procedure in the preceding example is that instead of using bind variables,
it glues a query together based on values passed in by the user, allowing an attacker to change the structure and the semantics of the query Bind variables prevent SQL injection by allowing the values of predicates to change at runtime, yet the structure of the query cannot change
The following example is a modified version of the preceding example implemented using bind variables:
create or replace procedure sql_injection_prevented(
p_last_name in varchar2)
is
type employee_record is table of employees%ROWTYPE;
emp_rec employee_record := employee_record();
x varchar2(32767);
begin
x := q'!select *
from employees
where last_name = :last_name !';
execute immediate x bulk collect into emp_rec using p_last_name;
for i in emp_rec.first emp_rec.last loop
dbms_output.put(emp_rec(i).last_name||' - ');
dbms_output.put_line(emp_rec(i).salary);
end loop;
dbms_output.put_line(emp_rec.count||' Rows Returned');
end sql_injection_prevented;
/
Take a close look at the query string defined in the variable X Because it uses a bind variable, :last_name, the query is first parsed without the actual value of p_last_name substituted for the bind variable last_name The SQL parser knows during the parse phase that this query has only one predicate and that the only acceptable value for last_name is a character string that represents a value It is impossible to add any structures such as an OR predicate that changes
structure of the query To prove this, let’s use a few of the tests from our previous example This procedure works as expected when we call it with a last name:
hr@aos> exec sql_injection_prevented(q'!Grant!');
Grant - 2600
Grant - 7000
2 Rows Returned
Now let’s try one of our SQL injection techniques:
hr@aos> exec sql_injection_prevented(q'!Grant' or 1 = 1 !');
BEGIN sql_injection_prevented(q'!Grant' or 1 = 1 !'); END;
*
Trang 3ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "HR.SQL_INJECTION_PREVENTED", line 1
ORA-06512: at line 1
This time we receive a “numeric or value error” since the string we passed in is not a valid string when searching for LAST_NAME
Now that you fully understand the concept of SQL injection, let’s talk about how it applies to APEX Bind variables are remarkably easy to use in APEX since you do not have to write extra code to bind the values of those variables You can simply reference the APEX items using bind variable syntax The following examples demonstrate a SQL query and an insert statement as a developer would use them in APEX:
This query is used in a report region
select first_name,last_name
from employees
where instr(upper(last_name),upper(nvl(:P1_SEARCH,last_name))) > 0
This insert statement is used in a page process
insert into employees (first_name,last_name)
values (:P2_FIRST_NAME,:P2_LAST_NAME)
returning employee_id into :P2_NEW_EMPLOYEE_ID;
Note that in both examples, bind variable syntax is used to reference items It is possible to
reference APEX items using &ITEM_NAME syntax, which treats them as a string, not a bind variable For example, in the first query, we could have used &P1_SEARCH instead of :P1_SEARCH In that
case, a nefarious user could inject carefully constructed code, through either the URL or the search box, which would modify the semantics of the query at runtime The documentation, tutorials, and examples used throughout the APEX community encourage bind variable syntax However, it is still possible for a developer who does not understand the difference to use the wrong syntax Fortunately, the wrong syntax is substantially more complex than the right syntax when you add in the
concatenation operators, so the secure way is actually the easy way
Cross-site Scripting
Cross-site scripting (XSS) is a type of web exploit that affects every web development technology
by attempting to execute client-side code, typically JavaScript, in a user’s browser that was not included by the original developers of the application For example, suppose a JavaScript function was executed on the Accounts page of your online banking application that searched for your account numbers on that page, and then posted those account numbers to some other web server controlled by a hacker Obviously, the developers of the banking application would never create such a function, but if someone else were able to “inject” this function into your browser session
or the page itself, the consequences could be catastrophic
Of the several different types of XSS attacks, the most obvious one in the context of APEX is called “persistent.” The persistent attack takes advantage of applications that have a database backend and that allow end users to save text that includes JavaScript, that will then be displayed
by another user (as shown in Figure 12-5) Let’s look at another example in an effort to clarify the vulnerability
Imagine a timecard system or reporting application written in APEX that allowed users to enter the number of hours they worked along with some comments about those hours and a display of their year-to-date earnings A newly submitted timecard would be routed to a person’s manager
Trang 4for approval The manager could see all of her employees’ hours, comments, and salaries So far, the application works as intended Now imagine that in my comments for this week, I include JavaScript that sends all the data displayed on a page to a web server that I control When my manager views my timecard and my comments, she is unknowingly executing my JavaScript stored in the comments field, which sends the salary of everyone in my group to a server that I control If I knew that the business rules of the system were such that overtime of more than 20 hours goes up to our group vice president for approval, I could wait for a week when I had more than 20 hours of overtime to inject my code, thus gaining access to the salaries of everyone in the whole organization Who would have thought that a simple comments field could do so much damage?
Hopefully, you’re now asking yourself, What can I do to prevent this? Fortunately, APEX includes features to help developers mitigate the risk of XSS To summarize, the behavior we are trying to prevent is the ability for one user to submit JavaScript that will be displayed and therefore executed by other users The most common APEX objects that display data are regions
of type Report and APEX page items When a new Standard Report is created based on a SQL query
in APEX 3.2, the default column type for every column is called Standard Column Unfortunately, this type of column is vulnerable to XSS To prevent XSS, a developer must change this column type to Display as Text (escape special characters, does not save state) Future versions of APEX will likely change the default column type to the more secure version The new type of APEX reports, called Interactive Reports, use the secure column type by default
This same concept applies to APEX page items If a developer chooses an item of type Display
As Text, any JavaScript code that is stored in session state for that item will be executed when the page is displayed To prevent this, a developer should choose items of type Display As Text (escape special characters, does not save state), as shown in Figure 12-6 In the case of the Report Column type and the APEX page item type, the “escape special characters” aspect is key, because
it will send the HTML character codes for the less-than and greater-than signs—< and >,
respectively—to the browser, not the characters < and > The escaping of such markup-sensitive characters will cause the browser to render the actual characters, rather than interpret them as
HTML Using this technique will prevent data containing <script>…</script> from being
interpreted as executable JavaScript
One of the best ways for a developer to understand a security vulnerability is to intentionally create an example that is vulnerable, and then re-create the same example in a secure way To that end, the following is a simple two-page APEX application that demonstrates the insecure and secure report column types and page item types The following code, saved in a database table, simply writes a line of text onto a web page The message is written in Unicode character codes
so that it is unreadable unless the JavaScript function is executed:
<script type="text/JavaScript">
document.write(String.fromCharCode(88,83,83,32,86,117,108,110,101,114,97,98,108,101));
FIGURE 12-5 Persistent XSS
Trang 5The report on the page has two comments columns that display the previous JavaScript function The first column, Comments - Secure, is set to the secure type of Display as Text (escape special characters, does not save state) The second column, Comments - Insecure, is set to the type Standard Column As you can see in Figure 12-7, the first column shows the JavaScript function
in its entirety, but does not execute it, while the second column is actually executing the JavaScript
as you can read the phrase “XSS Vulnerable” in the comments The developer of this application anticipated that users would enter only comments about an employee However, it was trivial to add a bit of JavaScript that is now executed by any other user who views the report
URL Tampering
In the earliest days of the internet web pages were primarily, a static, read-only medium used to share information The most interaction a user had with a web application was clicking a link Today, modern web applications have evolved to the point that there is an almost continuous two-way stream of information between a user’s web browser and the application server I’d like to emphasize “almost continuous” because it’s important to understand the true nature of the HTTP Protocol as it relates to modern web applications HTTP is a stateless protocol as a persistent connection is not maintained between the client web browser and the HTTP server In contrast, traditional client-server applications maintain a persistent connection between the client and the server The actions of the HTTP protocol are often defined in terms of request, response I like to add a third term and describe it as request, response, disconnect The emphasis on “disconnect” helps developers understand the true nature of the technology they are working with Consider the impact of removing the network connection would have on a web application compared to
a client-server application The stateful client-server application would know immediately if the
FIGURE 12-6 Report column types
Trang 6connection were severed whereas the web application would have no knowledge of this action Obviously, the end user in the web scenario would know when they clicked a link or posted a form and received no response, but the HTTP Server would have no knowledge of this
Now that we have a better understanding of the stateless nature of a web application, let’s talk about how APEX maintains session state When a user requests a page from an APEX application, the APEX engine checks to see if this is the first request from this user in this browser session If it
is the first request, APEX assigns a new, unique number for this session For brevity, we’ll refer to this unique user session identifier as the Session ID When you are using an APEX application, including the APEX Development Environment, notice the third parameter in the colon delimited list in the URL is a long seemingly random number This number is also stored in a hidden form field on every page called pInstance APEX also passes variables, such as search criteria, as parameters in the URL when the operation is an HTTP GET One of the most convenient aspects
of APEX for developers is that this session state is automatically managed for them in database tables Once a value is set for an APEX Page or Application Item, the value of this item is maintained for the duration of the user’s session One simple, yet security-friendly benefit to this is realized when passing data between pages If you as a developer set the value of an item on page 1, you can refer to it on any other page in the application without the need to constantly pass it through the URL
FIGURE 12-7 XSS example report output
Trang 7A classic example of passing session state through the URL is the “Report and Form on a Table” generated by a wizard within APEX The wizard builds a report page that shows the rows
in a database table with an edit link next to each row When users click on the edit link they are taken to the form page where they can edit the data in that row, as shown in Figure 12-8 The primary key of the table is passed as a parameter in the URL The query on the form page uses that primary key as a query predicate to select the desired row
In our example, what would happen if a user simply changed the value of the primary key
in the URL (aka URL tampering)? If the user chose a value that matched another row in the table, that row would appear in the form This raises an obvious security concern as end users are able
to change the value used in the query predicate at will Imagine now that the developer in this example wanted to limit the rows a user could edit by simply limiting the rows shown on the report page If there is no row to click, how can a user edit that row? This false assumption by
a developer could easily allow a user to gain access to data he or she is not supposed to see
The first instinct of many developers is to make the request an HTTP POST instead of a GET,
as the value is not passed through the URL The reality is that this technique is simply “security through obscurity.” Many web developer tools are available that allow an end user to view and
manipulate post data or simply turn the POST into a GET To address these concerns, APEX offers
several new features that let developers easily protect their applications against such threats
Restricted Items
In many scenarios, certain application- or page-level items should never be set by an end user Suppose, for example, that the end user’s role, such as Administrator or Guest, is stored in an application-level item If an end user can change the value of this item, she could gain access
FIGURE 12-8 Primary Key Passed Through the URL
Trang 8to pages or data to which she is not entitled Each application or page item now has an attribute called Session State Protection Setting this attribute to Restricted - May Not Be Set From Browser will prevent end users from tampering with the value of the attribute The only way to change a Restricted item’s value is through constructs inside the application, such as computations or processes at the application or page level This technique enables the developer to store values
in items with the confidence that an end user cannot change their values
Checksum
Completely blocking the ability to set an item from a URL is the most secure option and works well for application-level roles or attributes that are typically static for the session However, in many scenarios, this technique simply won’t work, such as the “Report and Form on a Table” discussed earlier in this section
When a user clicks a row, we must have a way of determining which row was clicked to edit the proper row on the form page The most common technique for this in APEX, as well as many other popular technologies, is to pass the value in the URL Unfortunately, this gives the end user the opportunity to change the value In response to this potential security threat, the concept of a URL checksum was built into APEX When this feature is enabled, APEX uses a cryptographically strong Message Authentication Code or “MAC” to generate a checksum based on the parameters and their values in the URL APEX then appends this checksum to the URL This process occurs when APEX generates links to pages that require a checksum When a link is clicked that sets the value of one or more items, APEX runs the items and values through the same function Any change of a value in the URL will result in a different checksum when the request is received, thus allowing APEX to detect URL tampering
A great example of this is the Report and Form on a Table Once we require a checksum for the ID item on the form page, APEX automatically generates the checksum for each link on the report page Clicking a link works exactly as it did before, except you will now see a checksum appended to the URL If you try to change the value of ID in the URL, APEX will simply return an error If this checksum were a simple hash based on a well-known algorithm, it would be easy to spoof However, since the algorithm also takes in a key or “salt” that is known only internally to the APEX engine, a nefarious user would need to determine the algorithm or algorithms used as well as the key
Suggested Session State Protection Scenario
Session State Protection is extensively documented in the APEX Application Builder Users Guide Many permutations of settings are possible for Session State Protection Instead of dedicating a large portion of this chapter to all possible scenarios, I’ll provide some guidelines that cover the most common ones, which fall into two categories: items that should never be changed by a user, such as an application item storing the user’s role, and items that must be set through the URL; however, allowing the user to change item values is a security risk
Items That Should Never Change To ensure that the value of an item is never set by an end
user using URL parameters, set the item-level Session State Protection attribute to Restricted - May Not Be Set From Browser This is applicable to application- and page-level items
Sensitive Items Set Through the URL The scenario described earlier using the Report and
Form example is a perfect candidate for URL checksums To enable this feature, navigate to the Application Level Attributes | Security | Session State Protection Section, and click Page Select the target page of a URL that requires a checksum, in this case the form page Change the Page Access Protection attribute to Arguments Must Have Checksum You must also set the session
Trang 9state protection level for key items on the page, typically items that store the primary key of the row Checksum Required - Session Level is the most secure setting Application and User Level Checksums are used when sharing URLs or bookmarking is desirable It is not necessary to require checksums for items that are not passed through the URL, as the values of those items will
be overwritten by the process that returns the row into the page items Figure 12-9 shows an APEX URL in which Session State Protection is enabled The cs= parameter appended to the URL is the checksum
Password Items Items of type Password have always obscured the value from appearing on
the screen so that someone watching over a user’s shoulder cannot read the password However, password values are stored in the APEX session state table, which is accessible to anyone with a privileged database account Any login page generated by APEX clears this session state as soon
as the user logs in, but that value could persist in online redo log files or archive log files It is also easy for a developer inadvertently to alter the process that clears session state for the login, leaving the password in the session state table
To address these situations, two new item types were introduced in APEX 3.2: Password (does not save state) and Password (submits when ENTER pressed, does not save state) Both item types insure that a user’s password is never written to a persistent store Developers who upgrade applications or instances from previous versions of APEX to APEX 3.2 should verify that all password fields use these new item types
Encrypted Session State
In addition to protecting passwords, a developer may want to protect other sensitive data from system administrators or anyone else who has a privileged account or access to the online redo or archive log files Even if the data is stored in a table using programmatic or transparent encryption, any data element used in an APEX item is stored in the application session state table in clear text Starting with APEX 3.2, developers can set an item-level attribute to store session state encrypted
To illustrate this point, the following example in Figure 12-10 sets the Store Value Encrypted In Session State attribute of the P11_SALARY item to Yes and leaves it set to No for the P11_EMAIL item After submitting the page to make sure their values were stored in session state, I queried the WWV_FLOW_DATA table used to store APEX session state:
APEX_030200@AOS> select item_name,item_value
from wwv_flow_data
where flow_instance = 3574272250559947
and flow_id = 107
and item_name in ('P11_EMAIL','P11_SALARY');
ITEM_NAME ITEM_VALUE
-
-P11_EMAIL JWHALEN
P11_SALARY 948F90BDC554FBB74305B2AFA6E44102
FIGURE 12-9 Checksum added to URL
Trang 10As you can see, P11_EMAIL is in clear text, while the value of P11_SALARY that was a four-digit number on screen is now stored in the session state table as an encrypted value This value can be decrypted only internally by APEX and is thus protected from scenarios such as this It’s very important to note that this setting affects how the data is stored only in APEX session state—it has nothing to do with how the value is stored in application tables In this example, the value of the EMPLOYEES.SALARY column is not encrypted, only the value stored in the APEX item P11_ SALARY So enabling encryption on this item does not encrypt data in the EMPLOYEES table It is the developer’s responsibility to make sure that the application tables used to store sensitive data
do so using technologies such as DBMS_CRYPTO or Transparent Data Encryption at the column
or tablespace level
Leveraging Database Security Features
One key benefit to the APEX architecture is that every database feature is available to APEX developers In this section, we will explore the integration of VPD to implement row-level security We’ll then leverage fine-grained auditing (FGA) to provide silent alarm for our most sensitive data Both scenarios take advantage of the context information that APEX provides to better determine who the user is and where they are in a given application
FIGURE 12-10 Store Value Encrypted In Session State attribute