I've been reading the docs for Oracle VPD (Virtual Private Database, a.k.a. fine-grained security, the basis of label-based security), and there's something I'm having a hard time grasping. How does VPD prevent a user from leaking information using a malicious function in the WHERE
clause?
Say you have a VPD policy that generates a static predicate like cust_no = SYS_CONTEXT('order_entry', 'cust_num');
(like in the Oracle VPD tutorial).
That results in queries being rewritten, so:
SELECT * FROM orders;
becomes:
SELECT * FROM orders
WHERE cust_no = SYS_CONTEXT('order_entry', 'cust_num');
Fine so far as it goes. But what if the user writes:
SELECT * FROM orders WHERE my_malicious_function(secret_column);
? Where my_malicious_function
inserts each value it sees into another table owned by the malicious user's control, so they can then see the secret data by selecting that table.
The VPD rewriter will produce, according to the docs, something like:
SELECT * FROM orders
WHERE cust_no = SYS_CONTEXT('order_entry', 'cust_num')
AND my_malicious_function(secret_column);
but Oracle is free to re-order sub-clauses in WHERE
. What stops it from just running my_malicious_function
first, if it thinks that'll be the cheaper or more selective predicate? (Unlikely when the security condition is a SYS_CONTEXT
lookup, but very likely if the condition is a subquery against another table, or is a UDF its self).
I've read the documentation and I'm not seeing where it specifies any ordering guarantee on execution of VPD predicates vs user-supplied predicates. Is there such a guarantee, or any other mechanism to protect against malicious predicate functions?
(I'm also curious about whether a malicious predicate function in a VPD policy could cause a privileged user to run user-supplied code they did not intend by generating a predicate that refers to the malicious function, but that's somewhat separate.)
The "malicious function" is run after the VPD policy is applied, so it cannot see the hidden data.
So, in your example, the following query:
SELECT * FROM orders WHERE my_malicious_function(secret_column);
Gets rewritten to:
SELECT * FROM (
SELECT * FROM orders orders
WHERE cust_no = SYS_CONTEXT('order_entry', 'cust_num')
)
WHERE my_malicious_function(secret_column);
Therefore, the function is only executed for rows that satisfy the VPD predicate.
Refer: http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_rls.htm#i1005326
When a table alias is required (for example, parent object is a type table) in the predicate, the name of the table or view itself must be used as the name of the alias. The server constructs the transient view as something like
select c1, c2, ... from tab tab where <predicate>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With