Joven hacker sonriendo

Hackeamos su software

cero falsos positivos

Inteligencia experta + automatización eficaz

Evitar Inyección SQL

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la programación segura en RPG al evitar ataques de inyección de código SQL. La validación de entradas de las aplicaciones es un proceso fundamental para evitar brechas de seguridad y fugas de información.

Necesidad

Evitar que un atacante controle las instrucciones SQL usadas en iSeries.

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación en RPG que contiene sentencias SQL.

  2. Debe validarse la entrada de información antes de ser usada.

  3. La aplicación requiere de una conexión a una base de datos relacional a través del lenguaje de consulta SQL.

  4. Se desea evitar vulnerabilidades de tipo inyección SQL.

Solución

  1. El término "SQL dinámico" se refiere a los comandos SQL que se interpretan en tiempo de ejecución. En esta situación, las sentencias SQL se cargan en una variable del host, luego se realiza un llamado PREPARE para decirle al procesador SQL que compile la sentencia en un comando ejecutable.

  2. La siguiente aplicación obtiene los campos nombre y edad de la tabla estudiantes según el nombre indicado en la variable sqlnombre.​

  3. Definimos las variables: sqlnombre que contiene el nombre a buscar en la tabla, sqlcommand el cual es la sentencia dinámica a ejecutar, que contiene el carácter que sirve para indicar la búsqueda de un campo string, datastructure contiene la estructura de datos donde se almacenará el retorno de la sentencia.

    sqlselect.rpgle
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    D sqlnombre       S             20A   INZ('fsg')
    
    D sqlcommand      S            256A   INZ('SELECT nombre, edad +
    D                                          FROM fluidattacks/ESTUDIANTES +
    D                                          WHERE nombre = ')
    D                                     VARYING
    
    D quote           S              1A   INZ('''')
    
    D datastructure   DS
    D  nombre                       30A   VARYING
    D  edad                         10S 0
    
  4. Asignamos el nombre a la sentencia dinámica: SELECT nombre, edad FROM estudiantes WHERE nombre = $nombre , como se muestra a continuación:

    1
    2
    C                   EVAL      sqlcommand = sqlcommand +
    C                                          quote + sqlnombre + quote
    
  5. Se definen las configuraciones para almacenar la información y cerrar los recursos:

    1
    2
    3
    4
    5
    C/exec sql
    C+   SET OPTION
    C+   COMMIT = *NONE,
    C+   CLOSQLCSR = *ENDMOD
    C/end-exec
    
  6. Preparamos la sentencia SQL que proviene de sqlcommand:

    1
    2
    3
    C/exec sql
    C+ PREPARE SQLSTATEMENT FROM :sqlcommand
    C/end-exec
    
  7. Definimos el cursos CURSORSQL, con el cual podremos recorrer las filas retornadas por la sentencia:

    1
    2
    3
    C/exec sql
    C+ DECLARE CURSORSQL CURSOR FOR SQLSTATEMENT
    C/end-exec
    
  8. Abrimos el cursor:

    1
    2
    3
    C/exec sql
    C+ OPEN CURSORSQL
    C/end-exec
    
  9. Iniciamos la ejecución de la sentencia:

    1
    2
    3
    C/exec sql
    C+ FETCH NEXT FROM CURSORSQL INTO :datastructure
    C/end-exec
    
  10. Iniciamos el ciclo que recorre cada fila solo si el estado de la sentencia es igual a 00000 (La sentencia fue exitosa y no hubo advertencias), el estado 02000 es usado cuando no se han encontrado filas. Imprimimos la estructura:

    1
    2
    C                   DOW       SQLSTT = '00000'
    C     datastructure DSPLY
    
  11. Se obtiene la siguiente fila y la insertamos en la estructura de datos:

    1
    2
    3
    C/exec sql
    C+ FETCH NEXT FROM CURSORSQL INTO :datastructure
    C/end-exec
    
  12. Finalizamos el ciclo y el programa

    1
    2
    3
    C                   ENDDO
    
    C                   RETURN
    
  13. Este tipo de instrucciones son de gran flexibilidad ya que se puede contener comandos SQL insertados y cargados por diferentes medios. Sin embargo, esta flexibilidad puede llevar a problemas de seguridad. En el ejemplo anterior si la variable sqlnombre contiene datos válidos, por ejemplo, el nombre fsg (INZ(fsg)), la sentencia se vería de la siguiente manera:

    1
    SELECT nombre, edad FROM estudiantes WHERE nombre = 'fsg'
    
  14. Sin embargo, si la variable contiene otro tipo de información por ejemplo fsg' OR 1=1-- (INZ(fsg' OR 1=1--')), la sentencia generada es:

    1
    SELECT nombre, edad FROM estudiantes WHERE nombre = 'fsg' OR 1=1--'
    
  15. En este caso, se recuperan todos los registros, ya que se ha escrito una tautología.

  16. Para evitar este comportamiento es deseable evitar el uso de SQL dinámico. Si de todas formas es necesario su uso, se deben utilizar sentencias parametrizadas para evitar la inyección.

  17. Se define la nueva sentencia, el carácter ? es reemplazado al momento de abrir el cursor:

    1
    2
    3
    4
    D sqlcommand      S            256A   INZ('SELECT nombre, edad +
    D                                          FROM fluidattacks/ESTUDIANTES +
    D                                          WHERE nombre = ? ')
    D                                     VARYING
    
  18. Se eliminan las líneas que contienen el siguiente código:

    1
    2
    3
    4
    D quote           S              1A   INZ('''')
    
    C                   EVAL      sqlcommand = sqlcommand +
    C                                          quote + sqlnombre + quote
    
  19. Y se modifica la línea que contiene la instrucción para abrir el cursor por:

    1
    2
    3
    C/exec sql
    C+ OPEN CURSORSQL USING :sqlnombre
    C/end-exec
    
  20. Cada carácter ? en la sentencia es reemplazado por la variable definida en USING.

  21. Si se usa más de una variable, por ejemplo para la sentencia

    SELECT nombre, edad FROM estudiantes WHERE nombre = '$nombre' AND edad = '$edad'

    el código correspondiente sería:

    1
    2
    3
    4
    D sqlcommand      S            256A   INZ('SELECT nombre, edad +
    D                                          FROM fluidattacks/ESTUDIANTES +
    D                                          WHERE nombre = ? AND edad = ? ')
    D                                     VARYING
    
  22. Y en el cursor:

    1
    2
    3
    C/exec sql
    C+ OPEN CURSORSQL USING :sqlnombre, :sqledad
    C/end-exec
    

Descargas

sqlselect1.rpgle y sqlselect2.rpgle contienen el código fuente anteriormente descrito, para evitar inyecciones SQL en RPG.




Haz un comentario

Estado de los servicios - Términos de Uso