1. Trang chủ
  2. » Công Nghệ Thông Tin

Applied Oracle Security: Developing Secure Database and Middleware Environments- P50 doc

10 262 1
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 298,62 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

create or replace package custom_apex_auth as procedure create_user p_username in varchar2, p_password in varchar2; function validate_user p_username in varchar2, p_password in var

Trang 1

For this example, we’ll use DBMS_CRYPTO.RANDOMBYTES to generate a 16 byte key: SYSTEM@AOS> grant execute on dbms_crypto to sec_admin;

Grant succeeded.

SEC_ADMIN@AOS> select DBMS_CRYPTO.RANDOMBYTES(16) salt from dual;

SALT

231F8E440E65B5C180FA184F94F55B71

Now we’ll use following table to store usernames and passwords The user SEC_ADMIN will own this table and related packages

create table application_users(

id raw(16) default sys_guid(),

user_name varchar2(255),

verification raw(128),

constraint app_users_pk primary key (id),

constraint app_users_uq unique(user_name)

)

/

The following package will be used to create and authenticate users Note the use of

EXECUTE IMMEDIATE for any queries or DML against the APPLICATION_USERS table In the

event that someone does gain access to our table, he cannot simply query one of the dictionary views such as DBA_DEPENDENCIES to determine the package used to set the password This is certainly not a foolproof technique, but does make it more challenging to dissect the logic associated with password hashes

create or replace package custom_apex_auth

as

procedure create_user(

p_username in varchar2,

p_password in varchar2);

function validate_user(

p_username in varchar2,

p_password in varchar2)

return boolean;

end custom_apex_auth;

/

create or replace package body custom_apex_auth

as

key from dbms_crypto.randombytes

g_salt raw(256) := '231F8E440E65B5C180FA184F94F55B71';

function get_mac(

Trang 2

return raw

is

begin

return dbms_crypto.mac(

src => utl_raw.cast_to_raw(p_password), typ => dbms_crypto.hmac_sh1,

key => utl_raw.cast_to_raw(g_salt));

end get_mac;

procedure create_user(

p_username in varchar2,

p_password in varchar2)

is

l_mac raw(128);

begin

l_mac := get_mac(p_password);

execute immediate 'insert into application_users

(user_name,verification) values (:a,:b)'

using upper(p_username),l_mac; end create_user;

function validate_user(

p_username in varchar2,

p_password in varchar2)

return boolean

is

l_mac raw(128);

l_user_name varchar2(255) := upper(p_username);

l_count pls_integer := 0;

begin

l_mac := get_mac(p_password);

execute immediate

'select count(*)

from application_users

where user_name = :username

and verification = :mac ' into l_count using l_user_ name,l_mac;

if l_count = 1 then

return true;

else

return false;

end if;

end validate_user;

end custom_apex_auth;

Trang 3

Since we are storing the key inside the package, we must note that this code is accessible to anyone with a privileged account that can query data dictionary views such as DBA_SOURCE

To prevent this, we will use the PL/SQL “wrap” utility included with the Oracle Database This utility obfuscates the code so that it is still functional, yet is not readable by an attacker Here’s the procedure for wrapping this package:

1 Save the package body in a file named custom_apex_auth.pkb.

2 Copy this file to a computer that has the Oracle database installed You should check for

the existence of the wrap executable in $ORACLE_HOME/bin

3 Make sure $ORACLE_HOME/bin is in your path variable.

4 Execute the following from the command line, where iname is the name of the input file and oname is the name of the output file:

$ wrap iname=custom_apex_auth.pkb oname=custom_apex_auth.plb

If you open the output file in a text editor, you can see that the contents are completely obfuscated:

create or replace package body custom_auth wrapped

a000000

1

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

b

49b 2fa

tEQWVnLxdhO2QpYe8q3ImRMGA2UwgzsJMUiDZ47NCjoY+Mlxa55aWhbzjdSGbS0GLgMhQ95d CYA14bY3oT+dgofd882EY0pWQou5wW4T05JazzZ4CCtLIqTZc9wBsJtEI0aEcpuUSWtLBEL8 0Em/y0eLcJoG1+pl7ZBFucjL+pHyucbrlX3UpPAHubK+mMQs9VH5b2XoZlrgpcxN41C8YZMm 8r3Brr1O2MpAu0azbDgLxlMEnvrgUO3S1XxVTNIyUJVDvvPqiTsJ98/emfxqiET2+TteElAw 28UNX7ATU3dYGJaAeUfv4ll0IVSkggDUh9oyHRsBvemuZTaXyOfD8e/2L1gKGKFGq/E95qtx jA1FuNWpKxGjpsM20NTr5TqIMs13icQ2h5et11Rv+WfFROYv6X1EI3xLeJV/JIlLPpcAkWRk Bdd71Xj45pCgOrSp37AgdOWFnzqPYiR+QRNXwXabp3muOvMOJNk5A09KshfQXTWK1mzrw7dQ qN2IRmIXQBXLXNc0kA1QfkY3/iRNfrFqLvEvoc/puVufDYElGjtRnBIJYv4qURsHG2VvIxjI

Now create a user to test the code and verify that the password is not stored in clear text, and then verify that the function works as expected:

Trang 4

custom_apex_auth.create_user('tyler','welcome');

end;

/

SQL> select user_name,verification from application_users;

USER_NAME VERIFICATION

-

-TYLER 02F4BB94F2C10F05F51E01B6E8A8A82928E243A8

SEC_ADMIN@AOS> set serveroutput on

declare

l_result boolean := false;

begin

l_result := custom_apex_auth.validate_user('tyler','welcome');

if l_result then

dbms_output.put_line('User Authenticated');

else

dbms_output.put_line('Authentication Failed');

end if;

end;

/

User Authenticated

PL/SQL procedure successfully completed.

declare

l_result boolean := false;

begin

l_result := custom_apex_auth.validate_user('tyler','hello');

if l_result then

dbms_output.put_line('User Authenticated');

else

dbms_output.put_line('Authentication Failed');

end if;

end;

/

Authentication Failed

PL/SQL procedure successfully completed.

Before testing this code in APEX, we need to grant execute on this package from the SEC_ ADMIN schema to the SEC_USER schema SEC_ADMIN will own the package, but our APEX application will parse as SEC_USER

SEC_ADMIN@AOS> grant execute on custom_apex_auth to sec_user;

Trang 5

Now that all of the code is in place, the final step is to create a new authentication scheme in APEX that leverages this code To create a new authentication scheme in an application, navigate

to Shared Components | Security | Authentication Schemes Create a new authentication scheme,

select From Scratch, and name it Custom Table-Based Edit the newly created authentication

scheme and scroll down to the Login Processing section and enter the following code in the Authentication Function attribute (as shown in Figure 12-1):

return custom_apex_auth.validate_user

The default login page for a new application is page 101 The next two attributes assume that your login page is also 101 If it is not, be sure to substitute the correct value of your login page Select page 101 for the Session Not Valid attribute and make sure the Session Not Valid URL attribute is blank Set the Logout URL attribute to the following string:

wwv_flow_custom_auth_std.logout?p_this_flow=&APP_ID.&p_next_flow_page_sess=&APP_ ID.:101:&APP_SESSION.

Save this authentication scheme To activate this authentication scheme, click the Change Current tab on the Authentication Schemes page, and then select Custom Table-Based To test the

new authentication scheme, simply run the application and log in with a username of tyler and password of welcome—or any valid username and password that you created with the CUSTOM_

APEX_AUTH package

Authorization Schemes

APEX authorization schemes offer a powerful and flexible solution to allow or deny access selectively to any component of an application Their design encourages developers to define the logic for authorization at the application level and then apply that logic where it is needed The more this code is centralized and reused, the easier it is to review, audit, and change in the event that security requirements change

To define an authorization scheme, navigate to the Shared Components section of an application Authorization Schemes are listed in the Security section, just under Authentication Schemes A developer can create authorization schemes based on values of APEX items, SQL queries, or PL/SQL functions Once you create an authorization scheme and give it a unique name, this name will appear in the authorization scheme select list attribute of every APEX object that supports this feature

A partial list of APEX objects that support authorization schemes includes applications, pages, regions, report columns, list entries, and page items

FIGURE 12-1 Custom authentication scheme

Trang 6

When an authorization scheme fails for a given user, the effect differs, depending on the type

of object to which it is applied For an application or page, APEX will return an error to any user that violates the authorization scheme All other objects, such as report columns, regions, items, and buttons, will simply not appear on the page for users that do not pass the authorization scheme This allows you to hide navigational elements or actions that a user cannot perform Note that simply hiding a list item or tab that links to an administrative page is not enough, however, as the user could simply enter the page in the URL The authorization scheme should also be applied to the page If a nefarious user tries to access a page for which she is not

authorized, the APEX will display the error message associated with the authorization scheme

In the following example, we create an Authorization scheme based on a PL/SQL function This function uses the APEX_LDAP package to query an LDAP directory and return the

Organizational Unit of a person Based on the value of this attribute, we conditionally allow access to certain parts of the application Two users are involved in this scenario: Kathy Evans (IT Security) and Robert Smith (Human Resources) Here is the code for the authorization scheme (also shown in Figure 12-2):

declare

l_attributes wwv_flow_global.vc_arr2;

l_attribute_values wwv_flow_global.vc_arr2;

begin

"ou" is the attribute we are interested in.

l_attributes(1) := 'ou';

apex_ldap.get_user_attributes(

p_username => :APP_USER, APEX User

p_pass => null,

p_auth_base => 'ou=people,dc=example,dc=com',

p_host => '127.0.0.1',

p_port => 389,

p_attributes => l_attributes,

p_attribute_values => l_attribute_values);

if l_attribute_values(1) = 'Human Resources' then

return true;

else

return false;

end if;

end;

Here’s how to implement this code as an authorization scheme:

1 Navigate to the Shared Components section of an application.

2 Select Authorization Schemes.

3 Create a new authorization scheme with the name In HR Org.

4 Under Scheme Type, select PL/SQL Function Body Returning a Boolean.

5 Enter the code in the preceding listing in the Expression 1 attribute.

6 Provide an error message.

Trang 7

Now that we have created an authorization scheme named In HR Org, that name will appear

in the select list of the authorization scheme attribute of almost every APEX object Figure 12-3 shows this attribute for an APEX button Note that APEX will also include the opposite of our authorization scheme by including “(Not In HR Org),” as this a common requirement and eliminates the need to implement it in code

FIGURE 12-2 In HR Org authorization scheme

FIGURE 12-3 Authorization scheme attribute of a button

Trang 8

Let’s use the report on the EMPLOYEES table as an example Figure 12-4 shows the same page

as it appears for two different users The user Robert Smith passes the authorization scheme, while user Kathy Evans does not The authorization scheme was applied to the Salary and edit link columns of the report, the Create button, and the Search text box As you can see, those elements simply do not appear for Kathy Evans

There are several scenarios in which reusing the logic of an authorization scheme is desirable, yet the declarative attribute is not present For example, let’s say we want to have sensitive items show up as read-only for anyone who is not an administrator This simply isn’t possible with the authorization scheme attribute of an item, as that attribute would completely hide the item for non-administrators, not simply show it as read-only If the logic for who is considered an administrator is already defined in an authorization scheme, we can leverage that logic by calling the APEX_UTIL.PUBLIC_CHECK_AUTHORIZATION API This API accepts the case-sensitive name

of an authorization scheme and returns a Boolean, true, if the current user passes the authorization scheme, and false if he or she fails the authorization scheme So, if we have an authorization scheme named IsAdmin, we can set the read-only attribute of one or more items to PL/SQL Function Body Returning a Boolean and enter the following code in the Expression 1 attribute: return apex_util.public_check_authorization('IsAdmin');

You can also use this API for certain circumstances in which you want to check more than one authorization scheme If you need to check more than one authorization scheme in multiple places, it might just be easier and more efficient to code an additional authorization scheme that encompasses the logic of several other schemes

FIGURE 12-4 Impact of authorization scheme for Robert and Kathy

Trang 9

SQL Injection

Database-driven web applications are more prevalent than ever They are no longer relegated to the one-off internal systems of corporations, but have made their way to almost every mainstream web application on the Internet From photo-sharing sites; to web-based e-mail applications, online auctions, and store-fronts; to content management systems—all have a database behind the scenes New static HTML sites are now the exception, not the norm After all, why would anyone choose the management nightmare of a bunch of loosely coupled HTML, CSS, JavaScript, and image files when a content management system can separate the content from the formatting and move the ability of editing content from a few select technologists down to the people actually responsible for the content? A database brings so much power and flexibility to the Web, but it also introduces vulnerabilities

SQL injection has been around for several years, but few developers know what it is and even fewer know how to prevent it A SQL injection attack attempts to change the meaning of

a predefined SQL query in an application This type of attack can also change or add Data Manipulation Language (DML) statements, which are any inserts, updates, or deletes Data Definition Language (DDL) could also be injected

Most queries that are used inside an application consist of the SQL query and one or more parameters that can be changed at runtime For example, imagine a web page that lists all employees for a particular department As a user changes a select list on the web page, the page is submitted and the following query is executed:

select first_name,last_name,phone_number

from employees

where department_id = || X

The only thing that changes between page views is the value of X in the predicate The developer

of this application expects that only the value for DEPARTMENT_ID will ever be a number However,

a hacker might have very different plans for this predicate, such as returning all rows, or perhaps returning metadata about the database schema to plan an attack

The key to preventing SQL injection attacks is the use of bind variables If the structure or semantics of a query can change at runtime, then it is potentially vulnerable to SQL injection Most of the time, the vulnerability is introduced by concatenating variables within the body of

the query The example in the preceding paragraph shows the variable X concatenated with the

rest of the query The query cannot be parsed before this concatenation occurs, and therefore the concatenation can change the structure of the query every time it is run

For our purposes, we can simplify the Oracle SQL parser a bit and assume that it goes through three phases to run a SQL query: parse, bind, and execute In the parse phase, the SQL parser checks that the query is syntactically valid and that all the objects that it references are valid, and then it locks in the structure of the query During the bind phase, the actual values of bind variables are substituted for their placeholders in the query Again, the bind variables can change the value of the placeholder variables, but they cannot change the structure of the query since that was already established during the parse phase The final step is for the SQL parser to execute the query Queries that concatenate, not bind variables, essentially reverse the bind and parse phases The string is concatenated together, including the values of the variables, and then it is parsed This reversal

of phases allows an attacker to change the semantics of a query completely at runtime

Trang 10

Example 1: The Wrong Way

To help you better understand the concept of SQL injection, let’s construct a procedure that is

vulnerable to SQL injection The following procedure takes in a parameter of p_last_name, then

outputs all employees that match the parameter (Note that I am using the “Q quote mechanism”

introduced in Oracle Database 10g R2 to make the examples easier to read.) The key is that the

string is enclosed in the following syntax: q’! some string!’ This allows strings to contain single

quotes without the need to escape those single quotes

create or replace procedure sql_injection(

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 = '!'||p_last_name||q'!'!';

execute immediate x bulk collect into emp_rec;

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;

/

At first glance, this procedure seems valid, and it would probably run just fine in a production environment Let’s take a look at the first two examples with this procedure They are semantically

the same, though the second procedure uses the q quote mechanism.

hr@aos> set serveroutput on

hr@aos> exec sql_injection('Grant');

Grant - 2600

Grant - 7000

2 Rows Returned

hr@aos> exec sql_injection(q'!Grant!');

Grant - 2600

Grant - 7000

2 Rows Returned

As you can see, the procedure displays both employees with the last name Grant

Now suppose we want to see all employees, even though the developer of this procedure never intended this functionality:

hr@aos> exec sql_injection(q'!Grant' or 1 = 1 !');

King - 24000

Ngày đăng: 06/07/2014, 23:20

TỪ KHÓA LIÊN QUAN