2009-07-11 18 views
6

¿Hay fallos de seguridad que podrían ocurrir en este escenario:Seguridad de Python 'eval' Para Lista Deserialización

eval(repr(unsanitized_user_input), {"__builtins__": None}, {"True":True, "False":False}) 

donde unsanitized_user_input es un objeto str. La cadena es generada por el usuario y podría ser desagradable. Asumiendo que nuestro framework web no nos ha fallado, es una instancia real honesta a Dios de los builtins de Python.

Si esto es peligroso, ¿podemos hacer algo al respecto para que sea seguro?

Definitivamente no queremos ejecutar cualquier cosa contenida en la cadena.

Consulte también:

El contexto más amplio que es (creo) no es esencial para la cuestión es que tenemos miles de estos:

repr([unsanitized_user_input_1, 
     unsanitized_user_input_2, 
     unsanitized_user_input_3, 
     unsanitized_user_input_4, 
     ...]) 

en algunos casos anidados:

repr([[unsanitized_user_input_1, 
     unsanitized_user_input_2], 
     [unsanitized_user_input_3, 
     unsanitized_user_input_4], 
     ...]) 

que son ellos mismos convertidos en cadenas con repr(), puesto en almacenamiento persistente, y con el tiempo de lectura de retorno en la memoria con eval.

Evaluar deserializar las cadenas de almacenamiento persistente mucho más rápido que pickle y simplejson. El intérprete es Python 2.5, por lo que json y ast no están disponibles. No se permiten módulos C y cPickle no está permitido.

+0

"La razón para hacer esto tendría mucho más sentido si presentara el contexto más amplio" ¿Podría explicar el problema por favor? Actualmente, el comando parece completamente inútil, al igual que no hacer nada con 'unsunitized_user_input' .. – dbr

+0

" tenemos miles de estos "Eso no tiene sentido. ¿Por qué almacenaría la entrada de esa manera? No tiene sentido volver a escribir una cadena para fines de almacenamiento. – Miles

+0

¿Por qué no usa pickle o algo más simple? –

Respuesta

19

De hecho, es peligroso y la alternativa más segura es ast.literal_eval (consulte el módulo ast en la biblioteca estándar). Por supuesto, puede crear y modificar un ast para proporcionar, p. evaluación de variables y similares antes de evaluar el AST resultante (cuando se trata de literales).

La posible explotación de eval comienza con cualquier objeto que puede tener en sus manos (por ejemplo True aquí) y pasando a través .__ class_ a su objeto tipo, etc. hasta object, después consigue sus subclases ... básicamente lo que pueda llegar a CUALQUIER tipo de objeto y causar estragos. Puedo ser más específico, pero preferiría no hacerlo en un foro público (el exploit es bien conocido, pero teniendo en cuenta cuántas personas aún lo ignoran, revelarlo a los aspirantes a script kiddies podría empeorar las cosas ... solo evite eval en la entrada del usuario sin esterilizar y vivir felices para siempre! -).

3

En general, usted nunca debe permitir para enviar el código.

Los llamados "programadores profesionales pagados" tienen un tiempo suficientemente difícil para escribir código que realmente funciona.

La aceptación del código del público anónimo, sin el beneficio del control de calidad formal, es el peor de todos los escenarios posibles.

Los programadores profesionales, sin un buen y sólido QA formal, harán un hash de casi cualquier sitio web.De hecho, estoy realizando ingeniería inversa de un código increíblemente malo de profesionales pagos.

La idea de permitir que un código postal no profesional (no comprometido por QA) sea realmente aterrador.

8

Si puede probar más allá de toda duda que unsanitized_user_input es una instancia str de los integradores de Python sin nada manipulado, esto siempre es seguro. De hecho, será seguro incluso sin todos los argumentos adicionales desde eval(repr(astr)) = astr para todos esos objetos de cadena. Pones una cuerda, sacas una cuerda. Todo lo que hiciste fue escapar y escapar de él.

Todo esto me lleva a pensar que eval(repr(x)) no es lo que desea - ningún código se ejecutará jamás a menos que alguien le dé un objeto unsanitized_user_input que se parece a una cadena pero no lo es, pero esa es una pregunta diferente-- a menos que intente copiar una instancia de cadena de la manera más lenta posible: D.

+1

Eso es exactamente correcto; Definitivamente no quiero que se ejecute nada en la cadena. La razón para hacer esto tendría mucho más sentido si presentara el contexto más amplio, pero traté de simplificar el escenario para la pregunta. – gravitation

4

Con todo lo que usted describe, es técnicamente segura a eval cadenas repred, sin embargo, yo evitaría haciendo de todos modos, ya que está pidiendo problemas:

  • Podría haber algún rincón caso raro donde su suposición de que solo se almacenan las cadenas reprelacionadas (por ejemplo, una ruta de error/diferente en el almacenamiento que no se reprime instantáneamente).

  • Incluso si todo está bien ahora, suposiciones puede cambiar en algún momento, y los datos no analizados pueden ser almacenados en ese campo por alguien ware del código de evaluación.

  • Su código puede ser reutilizado (o peor, copiar + pegar) en una situación que no consideró.

Como Alex Martelli señaló, en python2.6 y superior, no es ast.literal_eval que manejar con seguridad las dos cadenas y otros tipos de datos simples, como tuplas. Esta es probablemente la solución más segura y completa.

Otra posibilidad, sin embargo, es utilizar el códec string-escape. Esto es mucho más rápido que eval (alrededor de 10 veces de acuerdo con timeit), disponible en versiones anteriores a literal_eval, y debe hacer lo que quiere:

>>> s = 'he\nllo\' wo"rld\0\x03\r\n\tabc' 
>>> repr(s)[1:-1].decode('string-escape') == s 
True 

(El [1: -1] es despojar a las cotizaciones externas repr añade.)

1
repr([unsanitized_user_input_1, 
     unsanitized_user_input_2, 
     ... 

... unsanitized_user_input es un objeto str

Usted no debería tener que serializar cadenas para almacenarlos en un da tabase ..

Si estas son todas las cadenas, como mencionaste, ¿por qué no puedes simplemente almacenar las cadenas en un db.StringListProperty?

Las entradas anidadas pueden ser un poco más complicadas, pero ¿por qué es así? Cuando tiene que recurrir a eval para obtener datos de la base de datos, probablemente esté haciendo algo mal ...

¿No pudo almacenar cada unsanitized_user_input_x como su propia fila db.StringProperty y agruparlos por un campo de referencia?

Cualquiera de estos puede no ser aplicable, ya que no tengo idea de lo que está tratando de lograr, pero mi punto es - ¿no puede estructurar los datos de una manera en la que no tiene que depender de eval (y también confíe en que no es un problema de seguridad)?

+0

Una razón es que las cadenas deben comprimirse para ajustarse al límite de entidad de 1MB de App Engine, y pensé que la sobrecarga de comprimir miles de cadenas individualmente sería probablemente mucho mayor que serializarlas, comprimirlas todas juntas y ponerlas en un gota. El ahorro de espacio probablemente sea menor también. Pero es un buen punto ... Definitivamente estoy buscando formas de evitar eval sin aumentar demasiado el costo de ejecución. – gravitation

Cuestiones relacionadas