Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Um exemplo para demonstrar a vulnerabilidade da injeção de SQL e sua prevenção no Oracle


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.