Vi vet alla att om någon applikationskod är dåligt skriven, så kan vem som helst hacka informationen genom att använda ett litet trick som SQL Injection. I det här inlägget ger jag ett exempel för att visa hur SQL Injection kan vara sårbart för en applikation och hur du kan förhindra det.
Demonstrationen är baserad på SCOTT-schemats EMP-tabell. För att ladda ner SCOTT-schemaskriptet klicka på följande länk Ladda ner Scott Schema Script.
Ett exempel för att utföra SQL-injektion
I det här avsnittet ger jag ett exempel på en PL/SQL lagrad procedur som accepterar en parameter anställds nummer som (p_empno) för att visa lönen för den anställde. I koden använder jag sammanlänkningen av det parametervärdet (p_empno) i SQL-satssträngen för REF CURSOR, vilket inte rekommenderas och kommer att vara orsaken till framgångsrik SQL-injektion. Nedan är proceduren:
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; /
Nu ska vi testa proceduren ovan normalt genom att skicka ett anställdsnummer.
Testa
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal ('7566'); END; /
Utdata
JONES -- 27706.89 PL/SQL procedure successfully completed.
Tills nu är allt bra. Eftersom vi korrekt kallade proceduren. Nu ska vi se hur vi kan hacka ovanstående procedur genom att använda SQL Injection-tricket för att hämta lönen för alla anställda. Kanske vill du ibland också göra det här. Skämt!
Testa med SQL Injection
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal ('X'' OR ''1''= ''1'); END; /
Lyckad SQL-injektion
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.
Wow, nu kan du se alla anställdas löner med detta SQL Injection-trick. Föreställ dig bara att du har ett textfält i en applikation oavsett om den är webbläsarbaserad eller stationär och du skickar värdet direkt till proceduren, och om du använder tricket ovan kommer detta säkert att hända.
Ett exempel för att förhindra SQL-injektion
Nu kommer vi att modifiera proceduren ovan för att använda bindningsvariabel istället för att sammanfoga parametervärdet och på så sätt kan inget SQL Injection-trick fungera.
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; /
Testa proceduren ovan normalt
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal_2 ('7566'); END; /
Utdata
JONES -- 27706.89 PL/SQL procedure successfully completed.
Testa proceduren ovan med SQL Injection
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal_2 ('1'' OR ''1''= ''1'); END; /
Misslyckad SQL Injection Output
Can not fetch any records for: 1' OR '1'= '1 PL/SQL procedure successfully completed.
Så notera det, om du skapar PL/SQL-program med dynamisk SQL, använd bindningsmetoderna.