Categorías
Python

Tipo de Python Comprobación

 

Tabla de Contenidos

  • Entendiendo inyección Python SQL
  • Configuración de una DatabaseCreating un DatabaseCreating una mesa con DataSetting Hasta un EnvironmentConnecting virtual de Python a la DatabaseExecuting una consulta
  • Creación de una base de datos
  • Creación de una tabla con datos
  • Configuración de una Python Entorno virtual
  • Conexión a la base de datos
  • ejecución de una consulta
  • el uso de parámetros de consulta en
  • parámetros de consulta Explotando
  • SQL parámetros de consulta con Python SQL InjectionCrafting segura de consulta ParametersPassing segura

  • Elaboración de seguridad de consulta Parámetros
  • adelantamiento con seguridad de consulta Parámetros
  • utilizando Composición SQL
  • Conclusión
  • Creación de una base de datos
  • Creación de una tabla con datos
  • Configuración de un entorno virtual de Python
  • Conexión a la base de datos
  • ejecución de una consulta
  • Crafting Parámetros de la consulta segura
  • adelantamiento con seguridad Parámetros de consulta

Cada pocos años, el Proyecto de seguridad de aplicaciones web abierta (OWASP) ocupa el mayor número de riesgos de seguridad de aplicaciones web crítico. Desde el primer informe, los riesgos de inyección han sido siempre en la parte superior. Entre todos los tipos de inyección, inyección de SQL es uno de los más comunes de ataque, y posiblemente el más peligroso. Como Python es uno de los más populares lenguajes de programación en el mundo, saber cómo proteger contra la inyección de SQL Python es crítica.

En este tutorial, vamos a aprender: la inyección

  • Lo Python SQL es y cómo prevenirlo
  • Cómo componer consultas con ambos literales e identificadores como parámetros
  • cómo ejecutar con seguridad las consultas en una base de datos de

Este tutorial es adecuado para usuarios de todos los motores de bases de datos . Los ejemplos aquí utilizan PostgreSQL, pero los resultados pueden ser reproducidos en otros sistemas de gestión de bases de datos (como SQLite, MySQL, Microsoft SQL Server, Oracle, etc.). Bono

gratuito: 5 pensamientos sobre Python Maestría, un curso gratuito para los desarrolladores de Python que muestra la hoja de ruta y la mentalidad que necesita para tomar sus habilidades de Python al siguiente nivel.

La comprensión de inyección SQL Python ataques de inyección SQL

son una vulnerabilidad de seguridad tan común que el legendario xkcd webcomic dedicó un cómic a la misma:

Generar y ejecutar consultas SQL es una tarea común. Sin embargo, las empresas de todo el mundo a menudo cometen errores terribles en lo que respecta a la composición de las sentencias SQL. Mientras que la capa ORM suele componer consultas SQL, a veces hay que escribir el suyo propio.

Al utilizar Python para ejecutar estas consultas directamente en una base de datos, hay una posibilidad de que podría cometer errores que pudieran comprometer su sistema. En este tutorial, aprenderá cómo implementar con éxito las funciones que SQL dinámico de composición consulta sin de poner el sistema en riesgo de inyección SQL Python.

Configuración de una base de datos

Para empezar, va a establecer una base de datos PostgreSQL fresco y rellenarlo con los datos. A lo largo del tutorial, vamos a usar esta base de datos a los testigos de primera mano cómo funciona la inyección de SQL Python.

Creación de una base de datos

En primer lugar, abra su concha y crear una nueva base de datos PostgreSQL propiedad del usuario postgres:

$ createdb -O postgres psycopgtest

Aquí utiliza la opción de línea de comandos -O para establecer el propietario de la base de datos para el usuario postgres. También ha especificado el nombre de la base de datos, que es psycopgtest.

Nota: postgres es un usuario especial , que se suele reservar para las tareas administrativas, pero para este tutorial, que está bien postgres uso. En un sistema real, sin embargo, se debe crear un usuario independiente a ser el propietario de la base de datos.

Su nueva base de datos está listo para ir! Puede conectarse a él utilizando psql:

$ psql -U postgres -d psycopgtest
psql (11.2, server 10.5)
Type "help" for help.

que estás conectado a la base de datos como psycopgtest el usuario postgres. Este usuario es también el propietario de la base, por lo que tendrá permisos de lectura en todas las mesas en la base de datos.

Creación de una tabla con datos

A continuación, debe crear una tabla con información de usuario y añadir datos a la misma:

psycopgtest=# CREATE TABLE users (
username varchar(30),
admin boolean
);
CREATE TABLE

psycopgtest=# INSERT INTO users
(username, admin)
VALUES
('ran', true),
('haki', false);
INSERT 0 2

psycopgtest=# SELECT * FROM users;
username | admin
----------+-------
ran | t
haki | f
(2 rows)

La tabla tiene dos columnas: nombre de usuario y admin. La columna de administración indica si un usuario tiene privilegios administrativos. Su objetivo es apuntar el campo de administración y tratar de abusar de ella.

Configuración de un entorno virtual de Python

Ahora que tiene una base de datos, que es hora de configurar el entorno de Python. Para obtener instrucciones paso a paso sobre cómo hacer esto, echa un vistazo a Python entornos virtuales: Una cartilla.

Crear su entorno virtual en un nuevo directorio:

(~/src) $ mkdir psycopgtest
(~/src) $ cd psycopgtest
(~/src/psycopgtest) $ python3 -m venv venv

Después de ejecutar este comando, se creará un nuevo directorio llamado Venv. Este directorio almacenará todos los paquetes se instalan dentro del entorno virtual.

Conexión a la base de datos

Para conectarse a una base de datos en Python, se necesita una base de datos adaptador . adaptador de base de datos más s seguimiento versión 2.0 de la API de base de datos de Python Especificación PEP 249. Cada motor de base de datos principal tiene un adaptador que lleva:

Para conectarse a una base de datos PostgreSQL, necesitará instalar psycopg, que es el adaptador más popular para PostgreSQL en Python. ORM de Django usa por defecto, y también es apoyado por SQLAlchemy.

En su terminal, activar el entorno virtual y el uso de pepita de instalar psycopg:

(~/src/psycopgtest) $ source venv/bin/activate
(~/src/psycopgtest) $ python -m pip install psycopg2>=2.8.0
Collecting psycopg2
Using cached https://....
psycopg2-2.8.2.tar.gz
Installing collected packages: psycopg2
Running setup.py install for psycopg2 ... done
Successfully installed psycopg2-2.8.2

Ahora ya está listo para crear una conexión con la base de datos. Aquí está el comienzo de la secuencia de comandos de Python:

import psycopg2

connection = psycopg2.connect(
host="localhost",
database="psycopgtest",
user="postgres",
password=None,
)
connection.set_session(autocommit=True)

Utilizó psycopg2.connect () para crear la conexión. Esta función acepta los siguientes argumentos:

  • host es la dirección IP o el DNS del servidor donde se encuentra la base de datos. En este caso, el anfitrión es su máquina local o localhost. base de datos de
  • es el nombre de la base de datos para conectarse a. Que desea conectarse a la base que ha creado anteriormente, psycopgtest.
  • usuario es un usuario con permisos para la base de datos. En este caso, se desea conectar a la base de datos como el propietario, por lo que se pasa el usuario postgres.
  • contraseña es la contraseña para el que ha especificado en el usuario. , En la mayoría de entornos de desarrollo, los usuarios pueden conectarse a la base de datos local sin una contraseña.

anfitrión es la dirección IP o el DNS del servidor donde se encuentra la base de datos. En este caso, el anfitrión es su máquina local o local anfitrión . base de datos de

es el nombre de la base de datos conectarse. Que desea conectarse a la base de datos creó anteriormente, psycopgtest. usuario

es un usuario con permisos para la base de datos. En este caso, se desea conectar a la base de datos como el propietario, por lo que se pasa el usuario postgres.

contraseña es la contraseña para quien especificó en el usuario. En la mayoría de los entornos de desarrollo, los usuarios pueden conectarse a la base de datos local sin una contraseña .

Después de configurar la conexión, se configura la sesión con el compromiso automático = verdad. La activación de los medios de confirmación automática que no tendrá que gestionar manualmente las transacciones mediante la emisión de un commit o rollback. Este es el más ORM defaultbehaviorin. Se utiliza este comportamiento aquí también para que pueda centrarse en la composición de las consultas SQL en lugar de la gestión de transacciones.

Nota: Los usuarios de Django pueden obtener la instancia de la conexión utilizada por el ORM de django.db.connection:

from django.db import connection

ejecución de una consulta

Ahora que tiene una conexión con la base de datos, ya está listo para ejecutar una consulta:

>>> with connection.cursor() as cursor:
... cursor.execute('SELECT COUNT(*) FROM users')
... result = cursor.fetchone()
... print(result)
(2,)

ha utilizado el objeto de conexión para crear un cursor. Al igual que un archivo en Python, el cursor se implementa como un gestor de contexto. Cuando se crea el contexto, se abra un cursor para que pueda utilizar para enviar comandos a la base de datos. Cuando se sale del contexto, el cursor se cierra y ya no puede utilizarlo.

Nota: Para obtener más información sobre gestores de contexto, echa un vistazo a gestores de contexto Python y el “con” Declaración.

Mientras que dentro del contexto, se utiliza el cursor para ejecutar una consulta y obtener los resultados. En este caso, ha emitido una consulta para contar las filas de la tabla de usuarios. Para recoger el resultado de la consulta, que ejecutó cursor.fetchone () y recibió una tupla. Dado que la consulta sólo puede devolver un resultado, que utilizó fetchone (). Si la consulta fuera a devolver más de un resultado, entonces que había necesidad de Iterar sobre el cursor o el uso uno de los otros métodos de captación *.

El uso de parámetros de consulta en SQL

En la sección anterior, se creó una base de datos, establecido una conexión con él, y ejecuta una consulta. La consulta que utilizó era estática . En otras palabras, no tenía ningún parámetro . Ahora usted comenzará a utilizar parámetros en sus consultas.

En primer lugar, vamos a poner en práctica una función que comprueba si un usuario es un administrador. is_admin () acepta un nombre de usuario y devuelve esa condición de administrador del usuario:

# BAD EXAMPLE. DON'T DO THIS!
def is_admin(username: str) -> bool:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
admin
FROM
users
WHERE
username = '%s'
""" % username)
result = cursor.fetchone()
admin, = result
return admin

Esta función ejecuta una consulta para buscar el valor de la columna administrador de un nombre de usuario dado. Que utilizó fetchone () para devolver una tupla con un solo resultado. A continuación, se ha descomprimido esta tupla en el administrador variable. Para probar la función, comprobar algunos nombres de usuario:

>>> is_admin('haki')
False
>>> is_admin('ran')
True

Hasta aquí todo bien. La función devuelve el resultado esperado tanto para los usuarios. Pero ¿qué hay de usuario no existente? Echar un vistazo a este rastreo de Python:

>>> is_admin('foo')
Traceback (most recent call last):
File "", line 1, in
File "", line 12, in is_admin
TypeError: cannot unpack non-iterable NoneType object

Cuando no existe el usuario, se eleva un TypeError. Esto se debe a .fetchone () devuelve Ninguno cuando no se encuentran resultados, y desembalaje Ninguno plantea una TypeError. El único lugar donde puede desempaquetar una tupla es donde se rellenan de administrador del resultado. usuarios

para manejar no existentes, crean un caso especial para cuando el resultado es Ninguno:

# BAD EXAMPLE. DON'T DO THIS!
def is_admin(username: str) -> bool:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
admin
FROM
users
WHERE
username = '%s'
""" % username)
result = cursor.fetchone()

if result is None:
# User does not exist
return False

admin, = result
return admin

Aquí, usted ha agregado un caso especial para el manejo Ninguno. Si usuario no existe, entonces la función debe devolver Falso. Una vez más, pruebe las funciones de algunos usuarios:

>>> is_admin('haki')
False
>>> is_admin('ran')
True
>>> is_admin('foo')
False

Gran! La función puede ahora manejar nombres de usuario no existentes así. Inyección de parámetros de consulta

Explotando Con Python SQL

En el ejemplo anterior, se utiliza la interpolación de cadenas para generar una consulta. A continuación, se ejecuta la consulta y se envía la cadena resultante directamente a la base de datos. Sin embargo, hay algo que puede haber pasado por alto durante este proceso.

Piense de nuevo al argumento de nombre de usuario que ha pasado a is_admin (). ¿Qué es exactamente qué representa esta variable? Usted puede asumir ese nombre de usuario es sólo una cadena que representa el nombre de un usuario real. Como estás a punto de ver, sin embargo, un intruso puede fácilmente explotar este tipo de supervisión y causar daños importantes al realizar la inyección de SQL Python.

tratar de comprobar si el siguiente usuario es un administrador o no:

>>> is_admin("'; select true; --")
True

Espere … ¿Qué ha pasado?

vamos a echar otro vistazo a la aplicación. Imprimir el bienestar consulta real ejecutado en la base de datos:

>>> print("select admin from users where username = '%s'" % "'; select true; --")
select admin from users where username = ''; select true; --'

El texto resultante contiene tres declaraciones. Para entender exactamente cómo funciona la inyección de Python SQL, hay que fijarse en cada parte individual. La primera declaración es el siguiente:

select admin from users where username = '';

Esta es la consulta prevista. El punto y coma (;) termina la consulta, por lo que el resultado de esta consulta no importa. El siguiente paso es la segunda declaración:

select true;

Esta declaración fue construido por el intruso. Está diseñado para volver siempre es cierto.

Por último, se ve este breve fragmento de código:

--'

este fragmento desactiva todo lo que viene después de ella. El intruso se añadió el símbolo de comentario (-) para convertir todo lo que podría haber puesto después del último marcador de posición en un comentario.

Al ejecutar la función con este argumento, será siempre devolverá True . Si, por ejemplo, se utiliza esta función en la página de inicio de sesión, un intruso podría iniciar sesión con el nombre de usuario ‘; seleccione true; -, y serán de acceso concedidos.

Si usted piensa que esto es malo, podría empeorar! Intrusos con conocimiento de su estructura de la tabla se pueden utilizar Python inyección SQL para causar daño permanente. Por ejemplo, el intruso puede inyectar una instrucción de actualización para alterar la información contenida en la base de datos: la ruptura de

>>> is_admin('haki')
False
>>> is_admin("'; update users set admin = 'true' where username = 'haki'; select true; --")
True
>>> is_admin('haki')
True

lo bajó de nuevo:

';

Este fragmento termina la consulta, al igual que en la inyección anterior. La siguiente declaración es el siguiente:

update users set admin = 'true' where username = 'haki';

administrador Esta sección cambios a cierto para Haki usuario.

Por último, hay un fragmento de código:

select true; --

Como en el ejemplo anterior, esta pieza devuelve verdadero y comentarios fuera todo lo que le sigue.

¿Por qué es peor? Bueno, si el intruso se las arregla para ejecutar la función de dicha entrada, a continuación, Haki usuario se convertirá en un administrador:

psycopgtest=# select * from users;
username | admin
----------+-------
ran | t
haki | t
(2 rows)

El intruso ya no tiene que utilizar el hack. Sólo puede iniciar sesión con el nombre de usuario Haki. (Si el intruso realmente querían causar daño, entonces se podría incluso emitir un comando DROP DATABASE.)

Antes se le olvida, restaurar Haki de nuevo a su estado original:

psycopgtest=# update users set admin = false where username = 'haki';
UPDATE 1

Así que, ¿por qué sucede esto? Pues bien, ¿qué es lo que sabe sobre el argumento de nombre de usuario? Usted sabe que debe ser una cadena que representa el nombre de usuario, pero que en realidad no cheque o hacer cumplir esta afirmación. Esto puede ser peligroso! Es exactamente lo que los atacantes están buscando cuando tratan de piratear el sistema.

Elaboración de seguridad Parámetros de consulta

En la sección anterior, se vio como un intruso puede explotar su sistema de administración y el aumento de permisos mediante el uso de una cadena cuidadosamente elaborado. El problema fue que permitió que el valor que se pasa desde el cliente al ser ejecutado directamente a la base de datos, sin realizar ningún tipo de comprobación o validación. inyecciones SQL dependen de este tipo de vulnerabilidad.

En cualquier momento en la entrada del usuario se utiliza en una consulta de base de datos, hay una posible vulnerabilidad de inyección SQL. La clave para prevenir la inyección de SQL Python es asegurarse de que se está utilizando el valor que el desarrollador pretende. En el ejemplo anterior, destinados a nombre de usuario para ser utilizado como una cadena. En realidad, fue utilizado como una instrucción SQL en bruto.

Para hacer valores seguros se utilizan a medida que se pretende, es necesario escapar el valor. Por ejemplo, para evitar que intrusos inyección SQL prima en el lugar de un argumento de cadena, puede escapar comillas :

>>> # BAD EXAMPLE. DON'T DO THIS!
>>> username = username.replace("'", "''")

Este es sólo un ejemplo. Hay una gran cantidad de personajes y escenarios especiales que pensar cuando se trata de evitar la inyección de SQL Python. Por suerte para ti, modernas adaptadores de base de datos, vienen con una función de herramientas para la prevención de la inyección de SQL Python utilizando parámetros de consulta . Estos se utilizan en lugar de la interpolación de cadenas sin formato para componer una consulta con parámetros.

Nota: adaptadores diferentes, bases de datos y lenguajes de programación se refieren a parámetros de consulta con diferentes nombres. Los nombres comunes incluyen unen las variables , variables de sustitución y variables de sustitución .

Ahora que tiene una mejor comprensión de la vulnerabilidad, ya está listo para volver a escribir la función con parámetros de consulta en lugar de la interpolación de cadenas:

1 def is_admin(username: str) -> bool:
2 with connection.cursor() as cursor:
3 cursor.execute("""
4 SELECT
5 admin
6 FROM
7 users
8 WHERE
9 username = %(username)s
10 """, {
11 'username': username
12 })
13 result = cursor.fetchone()
14
15 if result is None:
16 # User does not exist
17 return False
18
19 admin, = result
20 return admin

Aquí está lo que es diferente en este ejemplo:

  • En la línea 9, que utiliza un nombre parámetro de nombre de usuario para indicar que el nombre de usuario debe ir. Nótese como el nombre de usuario de parámetros ya no está rodeado por comillas simples.
  • En la línea 11, que pasa el valor de nombre de usuario como el segundo argumento de cursor.execute (). La conexión utilizará el tipo y el valor del nombre de usuario cuando se ejecuta la consulta en la base de datos.

En la línea 9, que utiliza un nombre de usuario parámetro llamado para indicar que el nombre de usuario debe ir. Nótese como el nombre de usuario de parámetros ya no está rodeado por comillas simples.

En la línea 11, que pasa el valor de nombre de usuario como el segundo argumento de cursor.execute (). La conexión utilizará el tipo y el valor del nombre de usuario cuando se ejecuta la consulta en la base de datos.

Para probar esta función, pruebe algunos valores válidos y no válidos, incluyendo la cadena peligrosa de delante:

>>> is_admin('haki')
False
>>> is_admin('ran')
True
>>> is_admin('foo')
False
>>> is_admin("'; select true; --")
False

increíble! La función devuelve el resultado esperado para todos los valores. Lo que es más, la cadena peligrosa ya no funciona. Para entender por qué, puede inspeccionar la consulta generada por ejecutar ():

>>> with connection.cursor() as cursor:
... cursor.execute("""
... SELECT
... admin
... FROM
... users
... WHERE
... username = %(username)s
... """, {
... 'username': "'; select true; --"
... })
... print(cursor.query.decode('utf-8'))
SELECT
admin
FROM
users
WHERE
username = '''; select true; --'

La conexión tratado el valor de nombre de usuario como una cadena y escapó ningún carácter que pudiera terminar la cadena e introducir la inyección de SQL Python.

adelantamiento con seguridad Parámetros de consulta

adaptadores de base de datos por lo general ofrecen varias formas de pasar los parámetros de consulta. nombrado marcadores de posición son generalmente el mejor para facilitar la lectura, pero algunas implementaciones podría beneficiarse del uso de otras opciones.

Echemos un rápido vistazo a algunas de las formas correctas e incorrectas de usar consulta parámetros. A continuación se muestra bloques de código de los tipos de preguntas que querrá evitar:

# BAD EXAMPLES. DON'T DO THIS!
cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

Cada una de estas declaraciones pases nombre de usuario del cliente directamente a la base de datos, sin realizar ningún tipo de comprobación o validación. Este tipo de código está maduro para invitar a la inyección de SQL Python.

Por el contrario, este tipo de consultas debe ser seguro para que usted ejecute:

# SAFE EXAMPLES. DO THIS!
cursor.execute("SELECT admin FROM users WHERE username = %s'", (username, ));
cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username});

En estas declaraciones, nombre de usuario se pasa como un parámetro llamado. Ahora, la base de datos utilizará el tipo especificado y el valor del nombre de usuario cuando se ejecuta la consulta, que ofrece protección contra la inyección de SQL Python.

Composición Uso de SQL

Hasta ahora hemos parámetros utilizados para literales. literales son valores tales como números, cadenas y fechas. Pero lo que si usted tiene un caso de uso que requiere componer una consulta de uno donde el parámetro es otra cosa, como un nombre de tabla o columna diferente?

Inspirado en el ejemplo anterior, vamos a implementar una función que acepta el nombre de una tabla y devuelve el número de filas de esa tabla:

# BAD EXAMPLE. DON'T DO THIS!
def count_rows(table_name: str) -> int:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
count(*)
FROM
%(table_name)s
""", {
'table_name': table_name,
})
result = cursor.fetchone()

rowcount, = result
return rowcount

intenta ejecutar la función en su tabla de usuarios:

Traceback (most recent call last):
File "", line 1, in
File "", line 9, in count_rows
psycopg2.errors.SyntaxError: syntax error at or near "'users'"
LINE 5: 'users'
^

falló El comando para generar el SQL. Como hemos visto ya, el adaptador de la base de datos trata la variable como una cadena o un literal. Un nombre de tabla, sin embargo, no es una simple cadena. Aquí es donde entra en juego la composición de SQL.

Usted ya sabe que no es seguro de usar interpolación de cadenas a SQL componer. Por suerte, psycopg proporciona un módulo llamado psycopg.sql para ayudarle a consultas SQL de forma segura componer. Vamos a reescribir la función usando psycopg.sql.SQL ():

from psycopg2 import sql

def count_rows(table_name: str) -> int:
with connection.cursor() as cursor:
stmt = sql.SQL("""
SELECT
count(*)
FROM
{table_name}
""").format(
table_name = sql.Identifier(table_name),
)
cursor.execute(stmt)
result = cursor.fetchone()

rowcount, = result
return rowcount

Hay dos diferencias en esta aplicación. En primer lugar, que utilizó sql.SQL () para componer la consulta. Entonces, que utilizó sql.Identifier () para anotar el valor del argumento nombre_tabla. (Un identificador es un nombre de columna o tabla.)

Nota: Los usuarios del popular paquete django-debug-barra de herramientas puede ser que consiga un error en el panel SQL para consultas compuestas con psycopg.sql.SQL (). Se espera que una solución para la liberación en la versión 2.0.

Ahora, intente ejecutar la función en la tabla de usuarios:

>>> count_rows('users')
2

Gran! A continuación, vamos a ver lo que sucede cuando no existe la tabla:

>>> count_rows('foo')
Traceback (most recent call last):
File "", line 1, in
File "", line 11, in count_rows
psycopg2.errors.UndefinedTable: relation "foo" does not exist
LINE 5: "foo"
^

La función inicia la excepción UndefinedTable. En los siguientes pasos, va a utilizar esta excepción como una indicación de que su función está a salvo de un ataque de inyección SQL Python.

Nota: La excepción UndefinedTable se añadió en la versión 2.8 psycopg2. Si está trabajando con una versión anterior de psycopg, entonces obtendrá una excepción diferente.

Para ponerlo todo junto, añadir una opción para contar filas de la tabla hasta un cierto límite. Esta característica podría ser útil para tablas muy grandes. Para implementar esto, añadir una cláusula LIMIT para la consulta, junto con los parámetros de consulta para el valor del límite:

from psycopg2 import sql

def count_rows(table_name: str, limit: int) -> int:
with connection.cursor() as cursor:
stmt = sql.SQL("""
SELECT
COUNT(*)
FROM (
SELECT
1
FROM
{table_name}
LIMIT
{limit}
) AS limit_query
""").format(
table_name = sql.Identifier(table_name),
limit = sql.Literal(limit),
)
cursor.execute(stmt)
result = cursor.fetchone()

rowcount, = result
return rowcount

En este bloque de código, anotada límite usando sql.Literal (). Al igual que en el ejemplo anterior, psycopg se unirá todos los parámetros de la consulta como literales cuando se utiliza el enfoque simple. Sin embargo, cuando se utiliza sql.SQL (), necesita anotar explícitamente cada parámetro utilizando sql.Identifier () o sql.Literal ().

Nota: Desafortunadamente, la especificación API de Python no se ocupa de la unión de identificadores únicos, literales. Psycopg es el adaptador sólo es popular que añade la capacidad de SQL de forma segura componer con las dos literales e identificadores. Este hecho hace que sea aún más importante prestar mucha atención al enlazar los identificadores.

ejecutar la función para asegurarse de que funciona:

>>> count_rows('users', 1)
1
>>> count_rows('users', 10)
2

Ahora que ha visto la función está funcionando, asegúrese de que es también seguro:

>>> count_rows("(select 1) as foo; update users set admin = true where name = 'haki'; --", 1)
Traceback (most recent call last):
File "", line 1, in
File "", line 18, in count_rows
psycopg2.errors.UndefinedTable: relation "(select 1) as foo; update users set admin = true where name = '" does not exist
LINE 8: "(select 1) as foo; update users set adm...
^

Este rastreo muestra que psycopg escapó el valor, y la base de datos tratado como una tabla nombre. Dado que no existe una tabla con este nombre, una excepción UndefinedTable fue levantado y no se cortó!

Conclusión

ha implementado con éxito una función que compone SQL dinámico sin de poner el sistema en riesgo de inyección SQL Python! Que ha utilizado tanto literales e identificadores de la consulta sin comprometer la seguridad .

Usted ha aprendido: la inyección

  • Lo Python SQL es y cómo se puede explotar
  • Cómo evitar la inyección de SQL Python usando los parámetros de consulta
  • Cómo componer con seguridad las sentencias SQL que literales de uso e identificadores como parámetros

Usted es ahora capaz de crear programas que puedan resistir los ataques desde el exterior. Id y frustrar los hackers!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *