Oracle PL/SQL 源代码加密实战
文章目錄
- PL/SQL 源代碼加密概述
- 加密的原則
- 加密局限性
- 使用 wrap 工具加密 PL/SQL 代碼
- 使用 DBMS_DDL 加密 PL/SQL 代碼
大家好,我是只談技術(shù)不剪發(fā)的 Tony 老師。
對 PL/SQL 源代碼進(jìn)行加密可以在交付應(yīng)用時隱藏源碼和實(shí)現(xiàn)細(xì)節(jié),同時也可以防止發(fā)布出去的代碼被篡改;Oracle 數(shù)據(jù)庫系統(tǒng)內(nèi)置的PL/SQL 程序包和類型的代碼絕大部分經(jīng)過了加密處理。Oracle 為我們提供了兩種加密 PL/SQL 源代碼的方法:wrap 實(shí)用工具和 DBMS_DDL 子程序。本文就給大家介紹一下如何利用這些方法提高 PL/SQL 代碼的安全性。
如果覺得文章有用,歡迎評論📝、點(diǎn)贊👍、推薦🎁
PL/SQL 源代碼加密概述
加密(wrap) PL/SQL 源代碼就是通過混淆隱藏 PL/SQL 內(nèi)容的過程。包含加密后內(nèi)容的文件被稱為加密文件(wrapped file),加密文件可以被 SQL*Plus 或者導(dǎo)入/導(dǎo)出工具移動、備份以及處理,但是內(nèi)容無法通過數(shù)據(jù)字典視圖 *_SOURCE 進(jìn)行查看。
以下 PL/SQL 對象的源代碼可以進(jìn)行加密:
- 程序包規(guī)范
- 程序包體
- 類型規(guī)范
- 類型體
- 函數(shù)
- 過程
PL/SQL 加密可以使用 wrap 實(shí)用工具或者 DBMS_DDL 子程序?qū)崿F(xiàn)。wrap 工具從命令行運(yùn)行,可以加密 SQL 腳本文件中的任何可加密的 PL/SQL 對象,例如一個 SQL*Plus 安裝腳本。DBMS_DDL 子程序可以加密單個動態(tài)生成的 PL/SQL 單元,例如 CREATE PROCEDURE 命令。
兩種加密方法都可以檢查標(biāo)記化錯誤(例如字符串超長),但是不會檢查語法或者語義錯誤(例如不存在的表或視圖)。
加密的原則
加密PL/SQL 源代碼時,建議遵循以下規(guī)則:
- 加密程序包或者對象類型時只加密包體,不加密包規(guī)范。這樣可以允許其他開發(fā)人員使用該程序包或者類型時查看需要的信息,而不能查看具體的實(shí)現(xiàn)。
- 只加密已經(jīng)開發(fā)完成的源代碼文件。加密文件不能進(jìn)行編輯,如果想要修改加密后的 PL/SQL 代碼,必須編輯未加密的原始文件并再次進(jìn)行加密。
- 發(fā)布加密文件之前使用文本編輯器查看并確認(rèn)所有重要的內(nèi)容都進(jìn)行了加密。
加密局限性
PL/SQL 源代碼的加密功能存在以下局限性,使用時需要注意:
- 加密文件不支持 Oracle 數(shù)據(jù)庫的向下兼容。例如,版本 n.1 的 PL/SQL 加密工具生成的文件無法導(dǎo)入版本 (n-1).2 的 Oracle 數(shù)據(jù)庫中,甚至版本 n.2 的 PL/SQL 加密工具生成的文件無法導(dǎo)入版本 n.1 的 Oracle 數(shù)據(jù)庫中。加密文件支持向上兼容,以及同一版本中的不同補(bǔ)丁包之間兼容。
- 加密 PL/SQL 源代碼不適合作為一個隱藏密碼或者表名的安全方法。對于更高級別的安全需求,可以考慮使用 Oracle Database Vault。
- 加密工具無法加密觸發(fā)器的源代碼。如果想要隱藏觸發(fā)器的實(shí)現(xiàn)細(xì)節(jié),可以將具體實(shí)現(xiàn)放入一個存儲程序,然后加密該程序,最后編寫一個調(diào)用加密程序的觸發(fā)器。
使用 wrap 工具加密 PL/SQL 代碼
wrap 工具接收一個 SQL 文件作為輸入,加密該文件中可加密的 PL/SQL 對象(不會加密匿名塊、觸發(fā)器或者非 PL/SQL 代碼),然后輸出一個對應(yīng)的加密文件。
wrap 工具位于 $ORACLE_HOME/bin/ 目錄下,在操作系統(tǒng)提示符中輸入以下命令:
wrap iname=input_file [ oname=output_file ] [ keep_comments=yes ]其中,input_file 是包含 SQL 語句的文件;如果忽略文件擴(kuò)展名,默認(rèn)使用 .sql。output_file 是創(chuàng)建的加密文件;oname 可選,輸出文件名默認(rèn)為輸入文件名加上擴(kuò)展名 .plb。wrap 工具默認(rèn)會刪除所有的注釋,除非指定了 keep_comments=yes;此時,加密文件中會保留源碼之外的所有注釋。注意,等號兩邊不能包含任何空格。
📝如果 input_file 是已經(jīng)加密的文件,不會進(jìn)行任何處理,output_file 文件的內(nèi)容和 input_file 完全相同。input_file 文件中不能包含任何使用 SQLPlus DEFINE 定義的替換變量,因?yàn)?output_file 通過 PL/SQL 編譯器進(jìn)行解析,而不是 SQLPlus。
例如,以下命令的效果等價:
wrap iname=/mydir/myfile wrap iname=/mydir/myfile.sql oname=/mydir/myfile.plb以下命令為 input_file 指定了非默認(rèn)的擴(kuò)展名,為 output_file 指定了非默認(rèn)的文件名,同時保留了注釋信息:
wrap iname=/mydir/myfile.src oname=/yourdir/yourfile.out keep_comments=yes加密之后的 output_file 文件可以通過 SQL*Plus 直接執(zhí)行,創(chuàng)建 PL/SQL 對象:
SQL> @myfile.plb;接下來看一個示例,假設(shè) wraptest2.sql 文件包含以下內(nèi)容:
-- The following statement will not change.SELECT COUNT(*) FROM EMPLOYEES //* The PL/SQL source text of the following two CREATE statements will be wrapped. */CREATE PROCEDURE wraptest AUTHID CURRENT_USER /* C style comment in procedure declaration */ ISTYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;all_emps emp_tab; BEGINSELECT * BULK COLLECT INTO all_emps FROM employees;FOR i IN 1..10 LOOP /* C style in pl/sql source */DBMS_OUTPUT.PUT_LINE('Emp Id: ' || all_emps(i).employee_id);END LOOP; END; /CREATE OR REPLACE FUNCTION fibonacci (n PLS_INTEGER ) RETURN PLS_INTEGER AUTHID CURRENT_USER -- PL/SQL style comment inside fibonacci function spec ISfib_1 PLS_INTEGER := 0;fib_2 PLS_INTEGER := 1; BEGINIF n = 1 THEN -- terminating conditionRETURN fib_1;ELSIF n = 2 THENRETURN fib_2; -- terminating conditionELSERETURN fibonacci(n-2) + fibonacci(n-1); -- recursive invocationsEND IF; END; /其中,wraptest 過程和 fibonacci 函數(shù)是可加密的 PL/SQL 單元;另外該文件中還包含了一些注釋以及一個 SELECT 語句。
從操作系統(tǒng)提示符中運(yùn)行以下命令進(jìn)行加密:
> wrap keep_comments=yes iname=wraptest2.sql執(zhí)行成功后輸出的信息如下:
Processing wraptest2.sql to wraptest2.plb加密后的 wraptest2.plb 文件內(nèi)容如下:
-- The following statement will not change. SELECT COUNT(*) FROM EMPLOYEES //* The PL/SQL source text of the following two CREATE statements will be wrapped. */ CREATE OR REPLACE PROCEDURE wraptest wrapped a000000 1 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 7 129 138 qf4HggDBeNMPlWAsPn6pGf+2LGwwg+nwJK5qZ3SVWE4+GayDZaL1bF7RwYm2/zr1qjZY3FrN 48M1bKc/MG5aY9YB+DrtT4SJN370Rpq7ck5D0sc1D5sKAwTyX13HYvRmjwkdXa0vEZ4q/mCU EQusX23UZbZjxha7CtlCDCx8guGw/M/oHZXc8wDHXL8V8OsqQMv/Hj7z68gINl7OstalRScr uSZ/l/W1YaaA9Lj8Fbx5/nJw96ZNy1SCY8VsB/G6O5f/65+EDxdThpnfU4e1vrrE9iB3/IpI +7fE1Tv29fwc+aZq3S7O/CREATE OR REPLACE FUNCTION fibonacci wrapped a000000 1 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 8 150 ff BFDvTL9OR04SJbx+qOy5H/h8IcwwgxDcAJnWZ3TNz51mjAmegdQcpNJfq8hUuQtv1Y5xg7Wd KqMH/HBANhnZ+E1mBWekavYjPxlqV9zIFqZAgB4SBqkqe42sai9Vb0cLEU02/ZCEyxDSfWf3 H1Lp6U9ztRXNy+oDZSNykWCUVLaZro0UmeFrNUBqzE6j9mI3AyRhPw1QbZX5oRMLgLOG3OtS SGJsz7M+bnhnp+xP4ww+SIlxx5LhDtnyPw==/加密文件中刪除了代碼內(nèi)部的注釋并加密了 wraptest 和 fibonacci 的源代碼,同時保留了加密對象外部的注釋。
然后,我們可以在 SQL*Plus 中運(yùn)行 wraptest2.plb 文件創(chuàng)建對象、查看子程序的內(nèi)容并且調(diào)用子程序:
SQL> -- Run wrapped file: SQL> SQL> @wraptest2.plb SQL> -- The following statement will not change. SQL> SQL> SELECT COUNT(*) FROM EMPLOYEES2 /COUNT(*) ----------1071 row selected.SQL> /* The PL/SQL source text of the following two CREATE statements will be wrapped. */SQL> CREATE PROCEDURE wraptest wrapped2 a0000003 14 abcd5 abcd6 abcd7 abcd8 abcd9 abcd10 abcd11 abcd12 abcd13 abcd14 abcd15 abcd16 abcd17 abcd18 abcd19 720 129 13821 qf4HggDBeNMPlWAsPn6pGf+2LGwwg+nwJK5qZ3SVWE4+GayDZaL1bF7RwYm2/zr1qjZY3FrN22 48M1bKc/MG5aY9YB+DrtT4SJN370Rpq7ck5D0sc1D5sKAwTyX13HYvRmjwkdXa0vEZ4q/mCU23 EQusX23UZbZjxha7CtlCDCx8guGw/M/oHZXc8wDHXL8V8OsqQMv/Hj7z68gINl7OstalRScr24 uSZ/l/W1YaaA9Lj8Fbx5/nJw96ZNy1SCY8VsB/G6O5f/65+EDxdThpnfU4e1vrrE9iB3/IpI25 +7fE1Tv29fwc+aZq3S7O26 27 /Procedure created.SQL> CREATE OR REPLACE FUNCTION fibonacci wrapped2 a0000003 14 abcd5 abcd6 abcd7 abcd8 abcd9 abcd10 abcd11 abcd12 abcd13 abcd14 abcd15 abcd16 abcd17 abcd18 abcd19 820 150 ff21 BFDvTL9OR04SJbx+qOy5H/h8IcwwgxDcAJnWZ3TNz51mjAmegdQcpNJfq8hUuQtv1Y5xg7Wd22 KqMH/HBANhnZ+E1mBWekavYjPxlqV9zIFqZAgB4SBqkqe42sai9Vb0cLEU02/ZCEyxDSfWf323 H1Lp6U9ztRXNy+oDZSNykWCUVLaZro0UmeFrNUBqzE6j9mI3AyRhPw1QbZX5oRMLgLOG3OtS24 SGJsz7M+bnhnp+xP4ww+SIlxx5LhDtnyPw==25 26 /Function created.SQL> SQL> -- Try to display procedure source text: SQL> SQL> SELECT text FROM USER_SOURCE WHERE name='WRAPTEST';TEXT -------------------------------------------------------------------------------- PROCEDURE wraptest wrapped a000000 1 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 7 129 138 qf4HggDBeNMPlWAsPn6pGf+2LGwwg+nwJK5qZ3SVWE4+GayDZaL1bF7RwYm2/zr1qjZY3FrN 48M1bKc/MG5aY9YB+DrtT4SJN370Rpq7ck5D0sc1D5sKAwTyX13HYvRmjwkdXa0vEZ4q/mCU EQusX23UZbZjxha7CtlCDCx8guGw/M/oHZXc8wDHXL8V8OsqQMv/Hj7z68gINl7OstalRScr uSZ/l/W1YaaA9Lj8Fbx5/nJw96ZNy1SCY8VsB/G6O5f/65+EDxdThpnfU4e1vrrE9iB3/IpI +7fE1Tv29fwc+aZq3S7O1 row selected.SQL> SQL> -- Try to display function source text: SQL> SQL> SELECT text FROM USER_SOURCE WHERE name='FIBONACCI';TEXT -------------------------------------------------------------------------------- FUNCTION fibonacci wrapped a000000 1 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 8 150 ff BFDvTL9OR04SJbx+qOy5H/h8IcwwgxDcAJnWZ3TNz51mjAmegdQcpNJfq8hUuQtv1Y5xg7Wd KqMH/HBANhnZ+E1mBWekavYjPxlqV9zIFqZAgB4SBqkqe42sai9Vb0cLEU02/ZCEyxDSfWf3 H1Lp6U9ztRXNy+oDZSNykWCUVLaZro0UmeFrNUBqzE6j9mI3AyRhPw1QbZX5oRMLgLOG3OtS SGJsz7M+bnhnp+xP4ww+SIlxx5LhDtnyPw==1 row selected.SQL> SQL> BEGIN2 wraptest; -- invoke procedure3 DBMS_OUTPUT.PUT_LINE('fibonacci(5) = ' || fibonacci(5));4 END;5 / Emp Id: 100 Emp Id: 101 Emp Id: 102 Emp Id: 103 Emp Id: 104 Emp Id: 105 Emp Id: 106 Emp Id: 107 Emp Id: 108 Emp Id: 109 fibonacci(5) = 3PL/SQL procedure successfully completed.SQL>使用 DBMS_DDL 加密 PL/SQL 代碼
DBMS_DDL 程序包提供了 WRAP 函數(shù)和 CREATE_WRAPPED 過程,可以用于加密單個動態(tài)創(chuàng)建的可加密 PL/SQL 對象。同時還提供了一個依次 MALFORMED_WRAP_INPUT(ORA-24230),當(dāng)輸入?yún)?shù)不是一個可加密的 PL/SQL 對象 DDL 語句時拋出。
WRAP 函數(shù)接收一個 CREATE 語句作為輸入,返回一個加密后的 CREATE 語句;CREATE_WRAPPED 過程相當(dāng)于執(zhí)行 WRAP 函數(shù)后再執(zhí)行返回的 CREATE 語句創(chuàng)建相應(yīng)的 PL/SQL 對象。輸入?yún)?shù)中的 CREATE 語句具有調(diào)用者權(quán)限執(zhí)行。
以下示例通過 EXECUTE IMMEDIATE 語句動態(tài)創(chuàng)建了一個程序包規(guī)范和加密的程序包體(通過 CREATE_WRAPPED 語句):
DECLAREpackage_text VARCHAR2(32767); -- text for creating package spec and bodyFUNCTION generate_spec (pkgname VARCHAR2) RETURN VARCHAR2 ASBEGINRETURN 'CREATE PACKAGE ' || pkgname || ' AUTHID CURRENT_USER ASPROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);PROCEDURE fire_employee (emp_id NUMBER);END ' || pkgname || ';';END generate_spec;FUNCTION generate_body (pkgname VARCHAR2) RETURN VARCHAR2 ASBEGINRETURN 'CREATE PACKAGE BODY ' || pkgname || ' ASPROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) ISBEGINUPDATE employeesSET salary = salary + amount WHERE employee_id = emp_id;END raise_salary;PROCEDURE fire_employee (emp_id NUMBER) ISBEGINDELETE FROM employees WHERE employee_id = emp_id;END fire_employee;END ' || pkgname || ';';END generate_body;BEGINpackage_text := generate_spec('emp_actions'); -- Generate package specEXECUTE IMMEDIATE package_text; -- Create package specpackage_text := generate_body('emp_actions'); -- Generate package bodySYS.DBMS_DDL.CREATE_WRAPPED(package_text); -- Create wrapped package body END; /以下語句查看程序包 emp_actions 的內(nèi)容:
SELECT text FROM USER_SOURCE WHERE name = 'EMP_ACTIONS';TEXT ------------------------------------------------------------------------PACKAGE emp_actions AUTHID CURRENT_USER ASPROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);PROCEDURE fire_employee (emp_id NUMBER);END emp_actions; PACKAGE BODY emp_actions wrapped a000000 1f abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd b 180 113 1fOVodewm7j9dBOmBsiEQz0BKCgwg/BKoZ4VZy/pTBIYo8Uj1sjpbEz08Ck3HMjYq/Mf0XZn u9D0Kd+i89g9ZO61I6vZYjw2AuBidnLESyR63LHZpFD/7lyDTfF1eDY5vmNwLTXrFaxGy243 0lHKAzmOlwwfBWylkZZNi2UnpmSIe6z/BU2nhbwfpqd224p69FwYVXmFX2H5IMsdZ2/vWsK9 cDMCD1KEqOnPpbU2yXdpW3GIbGD8JFIbKAfpJLkoLfVxoRPXQfj0h1k=如果程序包的規(guī)范也進(jìn)行了加密,就無法讀取調(diào)用子程序的接口信息。執(zhí)行以下命令調(diào)用 emp_actions.raise_salary 過程:
DECLAREs employees.salary%TYPE; BEGINSELECT salary INTO s FROM employees WHERE employee_id=130;DBMS_OUTPUT.PUT_LINE('Old salary: ' || s);emp_actions.raise_salary(130, 100);SELECT salary INTO s FROM employees WHERE employee_id=130;DBMS_OUTPUT.PUT_LINE('New salary: ' || s); END; /Old salary: 3557.4 New salary: 3657.4PL/SQL procedure successfully completed.??如果將 DBMS_DDL.WRAP 返回的語句作為 statement 參數(shù)(VARCHAR2A)傳給 DBMS_SQL.PARSE 過程,必須將DBMS_SQL.PARSE 的參數(shù) lfflg 設(shè)置為 FALSE;否則,DBMS_SQL.PARSE 將會增加一些內(nèi)容到加密后的 PL/SQL 對象中,從而破壞對象的定義。
總結(jié)
以上是生活随笔為你收集整理的Oracle PL/SQL 源代码加密实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .properties文件加载失败
- 下一篇: 区块链之旅(四)双花攻击、博弈论、Has