Locations of visitors to this page

Wednesday, April 1, 2009

how to protect your plsql code 保护存储过程

how to protect your plsql code
如何保护你的存储过程


概述:

两种方法:
a.使用包PACKAGE封装(encapsulate), 让用户拥有执行权限,但看不到SQL代码
如果想让用户能查看代码,不能执行,请参考Procedure vs Package内的方法
they would have to
a) be granted access to DBA_SOURCE which doesn't have any filters on what can be seen
b) have create/debug ANY procedure (very bad idea, avoid the ANY privileges)
c) have a view created for them against DBA_SOURCE that provided this access
d) user B provide them a stored procedure that shows this information.
here is a demonstration of (d)
...

b.用WRAP命令行程序或DBMS_DDL.WRAP打包,
这并不安全,可以被解包,获得源码,参考后面的资料和unwrapping plsql code in 10g



正文:

1. 创建PACKAGE封装SQL代码
用户即使拥有包的可执行权限, 也不能看到包体的SQL源码

用户A建一个包, 授权可执行权限给用户B
conn a/a
create or replace package pkg1
is
  procedure hello;
end;
/
create or replace package body pkg1
is
  procedure hello
  is
  begin
    dbms_output.put_line('Hello World!');
  end;
end;
/
grant execute on pkg1 to b;
用户B下能看到包的定义, 看不到包体中的SQL源码, 但可以运行
conn b/b
set pages 50000 line 120
set serveroutput on size unlimited
select text from all_source where owner='A' and name='PKG1' order by line;
exec a.pkg1.hello
desc a.pkg1
SQL> select text from all_source where owner='A' and name='PKG1' order by line;

TEXT
------------------------------------------------------------------------------------------------------------------------
package pkg1
is
  procedure hello;
end;

SQL> exec a.pkg1.hello
Hello World!

PL/SQL procedure successfully completed.

SQL> desc a.pkg1
PROCEDURE HELLO

SQL>

如果是使用单独的存储过程或函数, 则无法隐藏代码
conn a/a
create or replace procedure prc1
is
begin
  dbms_output.put_line('it''s prc1');
end;
/
grant execute on prc1 to b;
create or replace function fun1
  return varchar2
is
begin
  return 'it''s fun1';
end;
/
grant execute on fun1 to b;
conn b/b
set pages 50000 line 120
set serveroutput on size unlimited
desc a.prc1
select text from all_source where owner='A' and name='PRC1' order by line;
exec a.prc1
desc a.fun1
select text from all_source where owner='A' and name='FUN1' order by line;
select a.fun1 from dual;
SQL> exec a.prc1
SQLv select text from all_source where owner='A' and name='PRC1' order by line;

TEXT
------------------------------------------------------------------------------------------------------------------------
procedure prc1
is
begin
  dbms_output.put_line('it''s prc1');
end;

SQL> select text from all_source where owner='A' and name='FUN1' order by line;

TEXT
------------------------------------------------------------------------------------------------------------------------
function fun1
  return varchar2
is
begin
  return 'it''s fun1';
end;

6 rows selected.

SQL>
因此建议使用包, 还有更多好处尽在: Procedure vs Package
because packages

o break the dependency chain (no cascading invalidations when you install a new package body -- if you have procedures that call procedures -- compiling one will invalidate your database)
o support encapsulation -- I will be allowed to write MODULAR, easy to understand code -- rather then MONOLITHIC, non-understandable procedures
o increase my namespace measurably.  package names have to be unique in a schema, but I can have many procedures across packages with the same name without colliding
o support overloading
o support session variables when you need them
o promote overall good coding techniques, stuff that lets you write code that is modular, 
understandable, logically grouped together....

If you are a programmer - you would see the benefits of packages over a proliferation of standalone procedures in a heartbeat. 



2. 用WRAP程序打包加密

用法:
wrap iname=input_file [oname=output_file]

比如加密一个包:
set pages 50000 line 130
set serveroutput on size unlimited
create or replace package pkg1
is
  procedure hello;
  function hello2(n in varchar2) return varchar2;
end;
/
save pkg1.sql replace
create or replace package body pkg1
is
  procedure hello
  is
  begin
    dbms_output.put_line('Hello World!');
  end;
  function hello2(n in varchar2) return varchar2
  is
  begin
    dbms_output.put_line('Hello, '||n||'!');
  end;
end;
/
save pkg1.sql append
!cat pkg1.sql
!wrap iname=pkg1.sql
!cat pkg1.plb
@pkg1.plb
select text from user_source where name='PKG1' and type='PACKAGE' order by line;
select text from user_source where name='PKG1' and type='PACKAGE BODY' order by line;
desc pkg1
SQL> create or replace package pkg1
is
  procedure hello;
  function hello2(n in varchar2) return varchar2;
end;
/
  2    3    4    5    6
Package created.

SQL> save pkg1.sql replace
Wrote file pkg1.sql
SQL> create or replace package body pkg1
is
  procedure hello
  is
  begin
    dbms_output.put_line('Hello World!');
  end;
  function hello2(n in varchar2) return varchar2
  is
  begin
    dbms_output.put_line('Hello, '||n||'!');
  end;
end;
/
  2    3    4    5    6    7    8    9   10   11   12   13   14
Package body created.

SQL> save pkg1.sql append
Appended file to pkg1.sql
SQL> !cat pkg1.sql
create or replace package pkg1
is
  procedure hello;
  function hello2(n in varchar2) return varchar2;
end;
/
create or replace package body pkg1
is
  procedure hello
  is
  begin
    dbms_output.put_line('Hello World!');
  end;
  function hello2(n in varchar2) return varchar2
  is
  begin
    dbms_output.put_line('Hello, '||n||'!');
  end;
end;
/

SQL> !wrap iname=pkg1.sql

PL/SQL Wrapper: Release 10.2.0.4.0- 64bit Production on Wed Apr 01 06:39:23 2009

Copyright (c) 1993, 2004, Oracle.  All rights reserved.

Processing pkg1.sql to pkg1.plb

SQL> !cat pkg1.plb
create or replace package pkg1 wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
9
5a 8d
8JaRjQCpdPKaOi05fKryJOQXYfgwg5m49TOf9b9cFoXceF2bdIsJaefnm7+fMr2ywFyluIEI
CMQ5nr4Ckq0HMJL4NaJfIeSOenOOFXbWbgB21l8hlKxxhObWL+E8ceI/0Tx0pquab+0=

/
create or replace package body pkg1 wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
b
dc e3
5AxGCR+k0mFGtl3AL13kbu0X6Ccwg43wf5kVfI5GkPjVQV6jL6/i3N6bfqwIbBr0BnfpUnxp
Uv/QeD016geY1tCG/1ItwQDmF0OIHP4F6CWERI80lBaMgGkRX2Jd8NqKKo02yFU4BHl00UAy
sxEhONd3KmO93OuCTKfYMacb8W4LH6E2eSmCsTw27I+F7WloxLIwdLOqrw+/1XCtXv+K/lJh
lZmIFHgt

/

SQL> @pkg1.plb

Package created.


Package body created.

SQL> select text from user_source where name='PKG1' and type='PACKAGE' order by line;

TEXT
----------------------------------------------------------------------------------------------------------------------------------
package pkg1 wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
9
5a 8d
8JaRjQCpdPKaOi05fKryJOQXYfgwg5m49TOf9b9cFoXceF2bdIsJaefnm7+fMr2ywFyluIEI
CMQ5nr4Ckq0HMJL4NaJfIeSOenOOFXbWbgB21l8hlKxxhObWL+E8ceI/0Tx0pquab+0=


SQL> select text from user_source where name='PKG1' and type='PACKAGE BODY' order by line;

TEXT
----------------------------------------------------------------------------------------------------------------------------------
package body pkg1 wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
b
dc e3
5AxGCR+k0mFGtl3AL13kbu0X6Ccwg43wf5kVfI5GkPjVQV6jL6/i3N6bfqwIbBr0BnfpUnxp
Uv/QeD016geY1tCG/1ItwQDmF0OIHP4F6CWERI80lBaMgGkRX2Jd8NqKKo02yFU4BHl00UAy
sxEhONd3KmO93OuCTKfYMacb8W4LH6E2eSmCsTw27I+F7WloxLIwdLOqrw+/1XCtXv+K/lJh
lZmIFHgt


SQL> desc pkg1
PROCEDURE HELLO
FUNCTION HELLO2 RETURNS VARCHAR2
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 N                              VARCHAR2                IN

SQL>
包定义和包体都被加密了, 加密后的代码第一行存储过程名以wrapped结尾.
通常, 包定义不需要加密, 应该暴露给使用者

有时wrap不支持SQL语法的新写法, wrap命令后再加上edebug=wrap_new_sql参数即可, 见How to Wrap New SQL Using Wrap Utility


外部链接:
Obfuscating PL/SQL Source Code>
Wrapping PL/SQL Source Code
The WRAP Utility and the DBMS_DDL Package
Protecting PL/SQL Code
FAQ: Wrapping PL/SQL Source Code
Wrapping PL/SQL Code Primer - Oracle Database 11g PL/SQL Programming By Michael McLaughlin


3. 用DBMS_DDL.WRAP动态加密(Dynamic wrapping)
10gR2提供了DBMS_DDL.WRAP和DBMS_DDL.CREATE_WRAPPED为存储过程加密
WRAP返回加密后的代码, CREATE_WRAPPED加密代码并创建

WRAP用法:
DBMS_DDL.WRAP(
   ddl      VARCHAR2) 
  RETURN VARCHAR2;
DBMS_DDL.WRAP(
   ddl      DBMS_SQL.VARCHAR2S, 
   lb       PLS_INTEGER, 
   ub       PLS_INTEGER) 
  RETURN DBMS_SQL.VARCHAR2S;
DBMS_DDL.WRAP(
   ddl      DBMS_SQL.VARCHAR2A, 
   lb       PLS_INTEGER, 
   ub       PLS_INTEGER) 
  RETURN DBMS_SQL.VARCHAR2A;
VARCHAR2大小限制是32767字节, DBMS_SQL.VARCHAR2S是TABLE OF VARCHAR2(256), DBMS_SQL.VARCHAR2A是TABLE OF VARCHAR2(32767)
也就是说后两个函数可以处理SQL过程大于32k的情况, lb,ub分别是字符串数组的上下标

如:
select dbms_ddl.wrap(q'{create or replace procedure -
prc1 is begin dbms_output.put_line('it''s prc1'); end;}')
 from dual;
SQL> select dbms_ddl.wrap(q'{create or replace procedure -
prc1 is begin dbms_output.put_line('it''s prc1'); end;}')
 from dual;
>   2
DBMS_DDL.WRAP(Q'{CREATEORREPLACEPROCEDUREPRC1ISBEGINDBMS_OUTPUT.PUT_LINE('IT''SPRC1');END;}')
------------------------------------------------------------------------------------------------------------------------
create or replace procedure  prc1 wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
7
41 79
tCj/ToEfSjDvOgaZlsfca7W1grUwg5nnm7+fMr2ywFwWFvLfw6V0K7jAMv7SXrhSm7JK/iiy
veeysx0GMCyuJOqygUfTMoHgS/IyLvZE6iQf9jmmyeHTRA==


SQL>

参数是字符串数组, 举例:
set serveroutput on size unlimited
declare
  plain_sql   dbms_sql.varchar2a;
  wrapped_sql dbms_sql.varchar2a;
begin
  plain_sql(1) := 'create or replace procedure prc1 ';
  plain_sql(2) := 'is ';
  plain_sql(3) := 'begin ';
  plain_sql(4) := 'dbms_output.put_line(''it''''s prc1''); ';
  plain_sql(5) := 'end;';
  wrapped_sql := dbms_ddl.wrap(plain_sql,1,plain_sql.count);
  for i in 1..wrapped_sql.count loop
    dbms_output.put_line(wrapped_sql(i));
  end loop;
end;
/
SQL> declare
  2    plain_sql   dbms_sql.varchar2a;
  3    wrapped_sql dbms_sql.varchar2a;
  4  begin
  5    plain_sql(1) := 'create or replace procedure prc1 ';
  6    plain_sql(2) := 'is ';
  7    plain_sql(3) := 'begin ';
  8    plain_sql(4) := 'dbms_output.put_line(''it''''s prc1''); ';
  9    plain_sql(5) := 'end;';
 10    wrapped_sql := dbms_ddl.wrap(plain_sql,1,plain_sql.count);
 11    for i in 1..wrapped_sql.count loop
 12      dbms_output.put_line(wrapped_sql(i));
 13    end loop;
 14  end;
 15  /
create or replace procedure prc1 wrapped

a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
7
41
79
tCj/ToEfSjDvOgaZlsfca7W1grUwg5nnm7+fMr2ywFwWFvLfw6V0K7jAMv7SXrhSm7JK/iiy
veeysx0GMCyuJOqygUfTMoHgS/IyLvZE6iQf9jmmyeHTRA==



PL/SQL procedure successfully completed.

SQL>
注意数组每行最后要加上空格或回车符


CREATE_WRAPPED举例:
drop procedure prc1;
exec dbms_ddl.create_wrapped(q'{create or replace procedure prc1 is begin dbms_output.put_line('it''s prc1'); end;}')
desc prc1
SQL> drop procedure prc1;

Procedure dropped.

SQL> exec dbms_ddl.create_wrapped(q'{create or replace procedure prc1 is begin dbms_output.put_line('it''s prc1'); end;}')

PL/SQL procedure successfully completed.

SQL> desc prc1
PROCEDURE prc1

SQL>


外部链接:
Dynamic Wrap
DBMS_DDL
DBMS_DDL



4. 解包unwrap

列出一些资料, 以后再说
How to unwrap PL/SQL
Pete Finnigan Podcast about PL/SQL wrapping
2006_BlackHat_Vegas-V8-Finnigan-How_to_Unwrap_Oracle

Unwrapping 10G wrapped PL/SQL by Anton Scheffer
Wrapped PL/SQL - The Oracle Hacker's Handbook By David Litchfield
AUTOMATIC DETECTION OF VULNERABILITIES IN WRAPPED PACKAGES IN ORACLE

Oracle PL/SQL REWRAP Unwrap
UnwrapOracle




-fin-

No comments:

Website Analytics

Followers