tutorial - Código Vulnerable de inyección Python SQLite3 SQL
sqlite3 python install (3)
Cada vez que construyes sentencias SQL utilizando operaciones de cadena y datos proporcionados por un usuario externo, te abres a dos tipos de vulnerabilidad:
- Intento malicioso, en el cual el usuario ingresa un valor usando una combinación de caracteres separadores de línea, caracteres de comentario y instrucciones SQL UPDATE o DELETE que tendrán un efecto negativo en su base de datos (vea la respuesta de mhawke).
- Inocente en el que el usuario escribe texto legítimo pero contiene caracteres que su programa no espera. Un ejemplo sería alguien con el nombre de usuario
O''Reilly
. Cuando eso se interpola en la cadena, el apóstrofo de repuesto hará que el SQL resultante no sea válido.
Sé que los fragmentos de código a continuación son vulnerables a la inyección SQL debido al formato .format, pero no sé por qué. ¿Alguien entiende por qué este código es vulnerable y dónde comenzaría a solucionarlo? Soy consciente de que estos fragmentos de código dejan los campos de entrada abiertos para ejecutar otros comandos maliciosos a través de SQL Injection, pero no sé por qué
cursor.execute("insert into user(username, password)"
" values(''{0}'', ''{1}'')".format(username, password))
handle[0].execute("insert into auditlog(userid, event)"
" values({0}, ‘{1}'')".format(handle[2],event))
audit((cursor, connection, 0),
"registeration error for {0}”.format(username))
sql="""insert into activitylog(userid, activity, start, stop)
values({0}, ''{1}'', ''{2}'', ''{3}'')
""".format(handle[2], activity, start, stop)
De los documentos :
Por lo general, sus operaciones de SQL necesitarán usar valores de las variables de Python. No debe armar su consulta utilizando las operaciones de cadena de Python porque hacerlo no es seguro; hace que su programa sea vulnerable a un ataque de inyección SQL (vea http://xkcd.com/327/ para un ejemplo cómico de lo que puede salir mal).
En su lugar, use la sustitución de parámetros de DB-API. Poner ? como marcador de posición donde quiera usar un valor, y luego proporcione una tupla de valores como el segundo argumento para el método de ejecución del cursor (). (Otros módulos de base de datos pueden usar un marcador de posición diferente, como% s o: 1). Por ejemplo:
# Never do this -- insecure! symbol = ''RHAT'' c.execute("SELECT * FROM stocks WHERE symbol = ''%s''" % symbol) # Do this instead t = (''RHAT'',) c.execute(''SELECT * FROM stocks WHERE symbol=?'', t) print c.fetchone()
Un ejemplo de inyección SQL usando su primera instrucción SQL:
cursor.execute("insert into user(username, password) values(''{0}'', ''{1}'')".format(username, password))
Si el username
y la password
son "blah"
la declaración SQL resultante es:
insert into user(username, password) values(''blah'', ''blah'')
y no hay problema con esta declaración particular.
Sin embargo, si un usuario puede ingresar un valor para la password
, quizás de un formulario HTML, de:
blah''); drop table user; --
la declaración SQL resultante será:
insert into user(username, password) values(''blah'', ''blah''); drop table user; --
que en realidad son 3 declaraciones separadas por un punto y coma: una inserción, una tabla desplegable y luego un comentario. Algunas bases de datos, por ejemplo, Postgres, ejecutarán todas estas declaraciones, lo que dará como resultado la eliminación de la tabla de usuarios. Experimentando con SQLite, sin embargo, parece que SQLite no permitirá que se ejecuten varias declaraciones a la vez. Sin embargo, podría haber otras formas de inyectar SQL. OWASP tiene una buena referencia sobre el tema.
Reparar esto es fácil, use consultas parametrizadas como esta:
cursor.execute("insert into user(username, password) values(?, ?)", (username, password))
Los marcadores de posición se agregan a la consulta usando ?
y el motor de db escapará correctamente de estos valores para evitar inyecciones de SQL. La consulta resultante será:
insert into user(username, password) values(''blah'', ''blah''''); drop table users; --'')
donde la terminación ''
in ''blah/'''
se ha escapado correctamente. El valor
blah''); drop table users; --
estará presente en el campo de contraseña para el registro insertado.