2010-06-08 9 views
7

Esto es realmente solo una pregunta conceptual para mí en este momento.Lisp seguridad/validación de datos

En Lisp, los programas son datos y los datos son programas. El REPL hace exactamente eso: lee y luego evalúa.

Entonces, ¿cómo se puede obtener información del usuario de forma segura? Obviamente es posible, me refiero a viaweb, ahora Yahoo! Stores es bastante seguro, entonces, ¿cómo se hace?

Respuesta

16

El REPL significa Read Eval Print Loop.

(loop (print (eval (read)))) 

anterior es sólo conceptual, el código REPL real es mucho más complicado (con control de errores, depuración, ...).

Puede leer todo tipo de datos en Lisp sin evaluarlo. La evaluación es un paso separado, independiente de la lectura de datos.

Hay todo tipo de funciones de IO en Lisp. La más compleja de las funciones proporcionadas suele ser LEER, que lee s-expresiones. Hay una opción en Common Lisp que permite la evaluación durante READ, pero puede y debe apagarse cuando se leen datos.

Por lo tanto, los datos en Lisp no son necesariamente un programa e incluso si los datos son un programa, entonces Lisp puede leer el programa como datos, sin evaluación. Un REPL solo debe ser utilizado por un desarrollador y no debe exponerse a usuarios arbitrarios. Para obtener datos de los usuarios, uno usa las funciones normales de IO, incluidas funciones como READ, que puede leer expresiones S, pero no las evalúa.

Aquí hay algunas cosas que uno no debe hacer:

  • uso LEER para leer datos arbitrarios. LEER para obtener ejemplos permite leer datos realmente grandes: no hay límite.

  • evaluar durante READ ('read eval'). Esto debe ser apagado.

  • símbolos de lectura de E/S y llamar a sus funciones de símbolos

  • leer estructuras de datos cíclicos con leer, cuando sus funciones esperan listas de civil. Recorrer una lista cíclica puede mantener ocupado su programa por un tiempo.

  • no manejan los errores de sintaxis durante la lectura de los datos.

+0

+1. – rook

2

Esta es una pregunta muy importante y pensé lo mismo cuando estaba leyendo sobre Lisp. Aunque no he hecho nada significativo en LISP, mi respuesta es muy limitada.

Lo que puedo decir es que eval()is nasty. Hay un dicho que me gusta "Si eval es la respuesta, entonces estás haciendo una pregunta incorrecta". --Desconocido.

Si el atacante puede controlar los datos que luego se evalúan, entonces tiene una vulnerabilidad de ejecución remota de código muy grave. Esto puede ser mitigado, y yo voy a mostrar un ejemplo con PHP, porque eso es lo que sé:

$id=addslashes($_GET['id']); 
eval('$test="$id";'); 

Si no estuviera haciendo un complemento barras inclinadas a continuación, un atacante podría obtener la ejecución remota de código al hacer esto :

http://localhost?evil_eval.php?id="; phpinfo();/* 

Pero las barras add su vez, la " en un \", manteniendo así el atacante de "romper" de los "datos" y ser capaz de ejecutar código. Que es muy similar a la inyección sql.

+0

+1 para la cotización. Eso probablemente se aplica a casi cualquier idioma. Y, por supuesto, cada vez que uso php, la entrada siempre está envuelta en una respuesta positiva htmlspecialchars() –

6

lo haces de la manera en que todos los demás lo hacen. Lees una cadena de datos de la secuencia, la analizas para tus comandos y parámetros, validas los comandos y parámetros e interpretas los comandos y parámetros.

No hay magia aquí.

En pocas palabras, lo que NO hace, es no exponer su oyente Lisp a una fuente de datos insegura no validada.

Como se mencionó, el REPL es leído - eval - print. @The Rook se centró en eval (con razón), pero no descartó READ. READ es un comando MUY poderoso en Common Lisp. El lector puede evaluar el código por sí mismo, incluso antes de GET para "evaluar".

NO exponga LEÍDO a nada en lo que no confíe.

Con suficiente trabajo, podría hacer un paquete personalizado, limitar el alcance de las funciones disponibles para ese paquete, etc. etc. Pero creo que eso es más trabajo que simplemente escribir un analizador de comandos simple y no preocuparse por algún efecto secundario que me perdí

+0

Es exactamente donde me preocupa: que el lector pueda evaluar el código. ¿Hay algún tipo de equivalente al Python 2.6 'raw_input()'? –

+1

http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_lin.htm – Ken

0

Encontré que la pregunta dejó de ser controvertida. La evaluación no evaluará su entrada a menos que usted lo solicite explícitamente. Quiero decir que su entrada no será tratada como un código LISP sino como una cadena.

No es porque su lenguaje tenga un concepto potente como el eval que no es "seguro".

Creo que la confusión proviene de SQL, donde en realidad se trata una entrada como [parte de] SQL.

(query (concatenate 'string "SELECT * FROM foo WHERE id = " input-id)) 

Aquí la identificación de entrada está siendo evaluada por el motor SQL. Esto se debe a que no tiene una buena forma de escribir SQL, o lo que sea, pero el hecho es que su entrada se convierte en parte de lo que se evalúa.

Así que eval no le traerá inseguridad a menos que lo esté usando con los ojos cerrados.

EDIT Se olvidó de decir que esto se aplica a cualquier idioma.

4
  1. Cree su propia lectura y llene con los ganchos necesarios: SET-MACRO-CHARACTER, SET-DISPATCH-MACRO-CHARACTER y otros.
  2. Enlazar READTABLE a su propia lectura.
  3. Enlazar READ-EVAL a nil para evitar #. (Puede no ser necesario si el paso 1 se hace bien)
  4. LEA

Probablemente algo más.

También hay un truco en los símbolos internos en el paquete temporal durante la lectura.

Si los datos no son LL (1) -ish, simplemente escriba el analizador habitual.