アプリケーションコードの記述が不十分な場合、SQLインジェクションなどのちょっとしたトリックを使用して、だれでも情報をハッキングできることは誰もが知っています。この投稿では、SQLインジェクションがアプリケーションに対してどのように脆弱である可能性があり、どのようにそれを防ぐことができるかを示す例を示しています。
デモンストレーションは、SCOTTスキーマのEMPテーブルに基づいています。 SCOTTスキーマスクリプトをダウンロードするには、次のリンクをクリックしてください。Scottスキーマスクリプトのダウンロード。
SQLインジェクションを実行する例
このセクションでは、パラメータの従業員番号を(p_empno)として受け入れ、その従業員の給与を表示するPL/SQLストアドプロシージャの例を示します。コードでは、REF CURSORのSQLステートメント文字列でそのパラメーター(p_empno)値の連結を使用しています。これは推奨されておらず、SQLインジェクションが成功する原因になります。手順は次のとおりです。
CREATE OR REPLACE PROCEDURE PRC_GET_EMP_SAL (p_empno VARCHAR2)
IS
--Declare a ref cursor and local variables--
TYPE C IS REF CURSOR;
CUR_EMP C;
L_ENAME VARCHAR2 (100);
L_SAL NUMBER;
L_STMT VARCHAR2 (4000);
BEGIN
--Open the ref cursor for a Dynamic SELECT statement--
L_STMT := 'SELECT ename, sal
FROM emp
WHERE empno = ''' || p_empno || '''';
OPEN CUR_EMP FOR L_STMT;
LOOP
--Fetch the result set and print the result set--
FETCH CUR_EMP
INTO L_ENAME, L_SAL;
EXIT WHEN CUR_EMP%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (L_ENAME || ' -- ' || L_SAL);
END LOOP;
CLOSE CUR_EMP;
END;
/ 次に、上記の手順を通常どおりテストします 従業員番号を渡すことによって。
テスト
SET SERVEROUTPUT ON;
BEGIN
prc_get_emp_sal ('7566');
END;
/ 出力
JONES -- 27706.89 PL/SQL procedure successfully completed.
今まですべてが順調です。プロシージャを正しく呼び出したからです。ここで、SQLインジェクションのトリックを使用してすべての従業員の給与を取得することにより、上記の手順をハックする方法を説明します。たぶんあなたもこれをしたいことがあります。冗談だよ!
SQLインジェクションを使用したテスト
SET SERVEROUTPUT ON;
BEGIN
prc_get_emp_sal ('X'' OR ''1''= ''1');
END;
/ 成功したSQLインジェクション出力
WARD -- 11641.56 JONES -- 27706.89 MARTIN -- 11641.56 BLAKE -- 26542.7 CLARK -- 22817.41 SCOTT -- 83819.06 KING -- 46566.18 TURNER -- 13969.85 ADAMS -- 10244.6 JAMES -- 8847.64 FORD -- 27939.74 MILLER -- 12107.2 PL/SQL procedure successfully completed.
これで、このSQLインジェクションのトリックを使用してすべての従業員の給与を確認できます。アプリケーションにブラウザベースかデスクトップかに関係なくテキストフィールドがあり、その値をプロシージャに直接渡すことを想像してみてください。上記のトリックを使用すると、確実にこれが発生します。
SQLインジェクションを防ぐための例
次に、パラメータ値を連結する代わりにバインド変数を使用するように上記の手順を変更します。これにより、SQLインジェクションのトリックは機能しなくなります。
CREATE OR REPLACE PROCEDURE PRC_GET_EMP_SAL_2 (p_empno VARCHAR2)
IS
--Declare a ref cursor and local variables--
TYPE C IS REF CURSOR;
CUR_EMP C;
L_ENAME VARCHAR2 (100);
L_SAL NUMBER;
L_STMT VARCHAR2 (4000);
BEGIN
--Open the ref cursor for a Dynamic SELECT statement--
L_STMT := 'SELECT ename, sal
FROM emp
WHERE empno = :p_bind_empno';
OPEN CUR_EMP FOR L_STMT USING p_EMPNO;
LOOP
--Fetch the result set and print the result set--
FETCH CUR_EMP
INTO L_ENAME, L_SAL;
EXIT WHEN CUR_EMP%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (L_ENAME || ' -- ' || L_SAL);
END LOOP;
CLOSE CUR_EMP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('Can not fetch any records for: ' || p_empno);
END;
/ 上記の手順を通常どおりテストします
SET SERVEROUTPUT ON;
BEGIN
prc_get_emp_sal_2 ('7566');
END;
/ 出力
JONES -- 27706.89 PL/SQL procedure successfully completed.
SQLインジェクションを使用して上記の手順をテストします
SET SERVEROUTPUT ON;
BEGIN
prc_get_emp_sal_2 ('1'' OR ''1''= ''1');
END;
/ 失敗したSQLインジェクション出力
Can not fetch any records for: 1' OR '1'= '1 PL/SQL procedure successfully completed.
したがって、動的SQLを使用してPL / SQLプログラムを作成する場合は、バインディング・メソッドを使用することに注意してください。