Archive for February, 2011

What a great week!

February 26th, 2011

It was a really good week. To start the week off I was able to attend an Exadata workshop at Oracle. From a technical point of view the Exadata machine really is quite amazing. The storage subsystem and the integration with the Oracle database is very impressive. As far as database I/O is concerned it is a real game changer. We know have to think in terms of row level I/O when the Smart Scan is involved.

I was also able to attend the NoCOUG conference this week and hear Cary Millsap speak about performance. Cary is one of those people that, in the words of another expert in his field, really does inform, instruct and inspire. I always take away something when I hear Cary speak.

And if that weren’t enough I was able to arrange to visit with Cary and get him a tour of the National Ignition Facility. I’m always awed by the NIF and I think Cary enjoyed it as well. It really was a great week.

Input Validation

February 24th, 2011

Tom Kyte posted a blog entry that included a test illustrating why not using bind variables can make your code susceptible to SQL Injection. I’ve heard Tom and others talk about this subject before, but to be honest I just accepted their premise and never spent much time experimenting with it. So after reading the post and Googling for SQL Injection, I found a nice example by David Litchfield and came up with this as a way to attack the code included in the test.

create or replace procedure inj( p_date in date )
as
l_rec   all_users%rowtype;
c       sys_refcursor;
l_query long;
begin
l_query := '
select *
from all_users
where created = ''' ||p_date ||'''';

dbms_output.put_line( l_query );
open c for l_query;

for i in 1 .. 5
loop
fetch c into l_rec;
exit when c%notfound;
dbms_output.put_line( l_rec.username || '.....' );
end loop;
close c;
end;
/

SQL> ALTER SESSION SET NLS_DATE_FORMAT = '" '' or ''a'' = ''a"';

Session altered.

SQL> exec inj(''' or ''a'' = ''a');

select *
from all_users
where created = ' ' or 'a' = 'a'
ILO.....
ILMDS.....
BI.....
PM.....
SH.....

PL/SQL procedure successfully completed.

SQL>

Tom subsequently posted a followup entry and my example wasn’t too far off and did illustrate the point.

I thought question 3: “how to best protect against that attack” was also interesting. Here’s my attempt at creating a “safer” procedure:

create or replace procedure inj1( p_date in date )
as
  l_ctr pls_integer := 1;
begin
  for l_rec in
    ( select * from all_users
      where created = p_date )
  loop
    if l_ctr <= 5 then
      dbms_output.put_line( l_rec.username || '.....' );
    else
      exit;
    end if;
    --
    l_ctr := l_ctr + 1;
  end loop;
end;
/

The big difference I think is that there was no reason to use dynamic SQL in the original example. Now I understand it was for illustration, but realistically in that case dynamic SQL just added to the ease of attacking the code. I still use a bind variable but I’ve eliminated the dynamic SQL from the code. The results?

SQL> ALTER SESSION SET NLS_DATE_FORMAT = '" '' or ''a'' = ''a"';

Session altered.

SQL> set serveroutput on
SQL> exec inj1(''' or ''a'' = ''a');

PL/SQL procedure successfully completed.

SQL> select username,
to_char(created,'MM/DD/YYYY HH24:MI:SS')
from all_users where trunc(created) =
to_date('10/14/2007','MM/DD/YYYY');

USERNAME                       TO_CHAR(CREATED,'MM
------------------------------ -------------------
SYS                            10/14/2007 15:05:30
SYSTEM                         10/14/2007 15:05:31
OUTLN                          10/14/2007 15:05:32
DIP                            10/14/2007 15:07:32
XDB                            10/14/2007 15:31:47
DBSNMP                         10/14/2007 15:20:35
WMSYS                          10/14/2007 15:23:01
EXFSYS                         10/14/2007 15:30:19
CTXSYS                         10/14/2007 15:30:45
ANONYMOUS                      10/14/2007 15:31:47
ORDSYS                         10/14/2007 15:36:15
ORDPLUGINS                     10/14/2007 15:36:16
SI_INFORMTN_SCHEMA             10/14/2007 15:36:16
MDSYS                          10/14/2007 15:36:16
OLAPSYS                        10/14/2007 15:46:49
MDDATA                         10/14/2007 15:47:54
SYSMAN                         10/14/2007 15:54:03
MGMT_VIEW                      10/14/2007 15:58:29
FLOWS_FILES                    10/14/2007 15:59:24
APEX_PUBLIC_USER               10/14/2007 15:59:24
FLOWS_030000                   10/14/2007 15:59:24
OWBSYS                         10/14/2007 16:10:50
SCOTT                          10/14/2007 16:13:15

23 rows selected.

SQL> exec inj(to_date('10/14/2007','MM/DD/YYYY'));

select *
from all_users
where created = '10/14/2007'

PL/SQL procedure successfully completed.

SQL> exec inj1(to_date('10/14/2007 16:13:15','MM/DD/YYYY HH24:MI:SS'));
SCOTT.....

PL/SQL procedure successfully completed.

SQL> exec inj(to_date('10/14/2007 16:13:15','MM/DD/YYYY HH24:MI:SS'));

select *
from all_users
where created = '10/14/2007'

PL/SQL procedure successfully completed.

SQL>

The attack didn’t work, but there are other problems. Because of the date usage the query isn’t really practical as you can see in the example. Unless you format or trunc the “created” column it’s not very easy to make this procedure, or the original for that matter, work as intended.

My point though is not to take issue with a simple example, but to raise the issue of input validation. In playing around with this I was reminded of how hard it can be to really code the checks needed to insure that input is properly validated. Most of the time we don’t and we just throw an exception when bad input causes a failure at run time. This practice though, can also lead to SQL Injection if you’re not careful. Even Tom and several commenters to his thread point out, that another way to protect the code is to validate the input. Whether by using format masks as Tom mentions, which is probably the easiest way, or by actually decoding and testing the input. The problem of course is that actually validating input can be hard and leads to more coding and possibly errors. Of course getting your database hacked isn’t that great of an alternative either.