Todos nós sabemos que, se algum código de aplicativo for mal escrito, qualquer um pode hackear as informações usando um pequeno truque como SQL Injection. Neste post, estou dando um exemplo para demonstrar como o SQL Injection pode ser vulnerável a um aplicativo e como você pode evitá-lo.
A demonstração é baseada na tabela EMP do esquema SCOTT. Para baixar o script de esquema SCOTT, clique no link a seguir Baixar Scott Schema Script.
Um exemplo para executar injeção de SQL
Nesta seção, estou dando um exemplo de um procedimento armazenado PL/SQL que aceitará um parâmetro de número de funcionário como (p_empno) para exibir o salário desse funcionário. No código, estou usando a concatenação desse valor de parâmetro (p_empno) na string da instrução SQL para REF CURSOR, o que não é recomendado e será a causa do sucesso da SQL Injection. Abaixo segue o procedimento:
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; /
Agora vamos testar o procedimento acima normalmente passando um número de funcionário.
Teste
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal ('7566'); END; /
Saída
JONES -- 27706.89 PL/SQL procedure successfully completed.
Até agora está tudo bem. Porque chamamos corretamente o procedimento. Agora veremos como podemos hackear o procedimento acima usando o truque de SQL Injection para buscar o salário de todos os funcionários. Talvez às vezes você também queira fazer isso. Brincadeira!
Teste usando injeção de SQL
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal ('X'' OR ''1''= ''1'); END; /
Saída de injeção de SQL bem-sucedida
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.
Uau, agora você pode ver o salário de todos os funcionários usando esse truque de injeção de SQL. Imagine que você tem um campo de texto em um aplicativo, seja baseado em navegador ou desktop, e está passando o valor imediatamente para o procedimento, e se você usar o truque acima, certamente isso acontecerá.
Um exemplo para evitar injeção de SQL
Agora vamos modificar o procedimento acima para usar a variável bind ao invés de concatenar o valor do parâmetro e desta forma nenhum truque de SQL Injection pode funcionar.
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; /
Teste o procedimento acima normalmente
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal_2 ('7566'); END; /
Saída
JONES -- 27706.89 PL/SQL procedure successfully completed.
Teste o procedimento acima usando SQL Injection
SET SERVEROUTPUT ON; BEGIN prc_get_emp_sal_2 ('1'' OR ''1''= ''1'); END; /
Falha na saída de injeção de SQL
Can not fetch any records for: 1' OR '1'= '1 PL/SQL procedure successfully completed.
Portanto, anote isso, se você estiver criando programas PL/SQL usando SQL dinâmico, use os métodos de ligação.