PHP - ¿Cómo hacer un filtrado de entrada seguro a la luz de las vulnerabilidades de codificación multibyte?

8

Desde hace días, trato de hacerme una idea general de cómo escribir una aplicación web segura en PHP, y resulta ser particularmente difícil. Cuanto más leo, más me hundo en profundos pantanos llenos de vulnerabilidades que no son mencionadas por personas amables como Matt Robinson o Chris Shiflett .

Para algunos ejemplos, toma:

  • enlace
  • enlace
  • También vea el consumo de personajes como en la respuesta de Rook

En resumen, veo los siguientes problemas:

  • Al filtrar la entrada, no está muy claro cómo se descodificarán esos datos más adelante y, por lo tanto, la codificación de caracteres y los sistemas de escape pueden omitir el filtrado de entrada. (como la decodificación de doble url)
  • Al escapar de la salida, uno usa funciones estándar como htmlspecialchars . Está bien que htmlspecialchars tenga un parámetro de codificación, sin embargo, eso no impide que le envíes una entrada UTF-16, y eso probablemente podría romper el valor de seguridad de la función.

Parece que hay un módulo mbstring en php, pero si es vagamente tan seguro como su documentación es comprensible, entonces probablemente será inútil incluso si puedo averiguar cómo usarlo. Solo una muestra de los documentos para ilustración :

mbstring.strict_detection boolean

    Enables the strict encoding detection.

Genial, eso es útil.

Lamentablemente, las funciones también dependen de lo que establezca en las opciones de configuración ... Parece que hay una función llamada mb_convert_encoding , pero los documentos no dicen nada sobre el aspecto de seguridad y parece que necesita conocer la codificación de entrada (una zona de no acceso para la seguridad). También hay mb_check_encoding . Lo que parece estar destinado al propósito, pero leer los comentarios de los usuarios en los documentos no inspira exactamente la confianza.

Entonces, a la luz de todo esto, la pregunta es ¿cómo se realiza el filtrado de entrada seguro ? Algo como esto?

  1. mb_convert_encoding a utf-8
  2. mb_check_encoding para rechazar la entrada no válida
  3. bucle url_decode hasta que la cadena deje de cambiar
  4. Realice su filtrado de entrada normal con comparación de texto y expresiones regulares, etc. ...

editar: tenga en cuenta que 3 es problemático porque su filtro de entrada normal podría introducir entidades de nuevo que pueden decodificarse por url

editar
Encontré una respuesta parcial aquí , de Shiflett. Parece que para htmlspecialchars usar su parámetro de codificación y asegurarse de que establece el encabezado de codificación de caracteres para el navegador, evitaría que el navegador interprete los caracteres de manera diferente a como lo hace htmlspecialchars. Esto es todo asumiendo que la entrada de htmlspecialchars es válida para la codificación dada o que para cada entrada inválida posible htmlspecialchars interpreta la cadena exactamente de la misma manera que lo hace cada navegador. Sabemos que si no encontramos una manera de desinfectar nuestra entrada, no podemos asegurar que la entrada de htmlspecialchars esté codificada de manera válida, ya que un atacante podría preparar una cadena con una codificación no válida. Esto nos lleva a la segunda posibilidad, que htmlspecialchars se comportará de forma idéntica al navegador para todas las entradas posibles. Esto es un problema, ya que no podemos usar el mismo tokenizador para escapar y usar, ya que uno sucede en el servidor y otro en el navegador.

Todo esto es similar a lo que hace msql_real_escape para una base de datos, aunque creo que puedes resolver este problema correctamente para msql usando en su lugar declaraciones preparadas.

Una tercera salida problemática es el sistema de archivos cuando se usa php para realizar cargas de archivos u otras manipulaciones del sistema de archivos. Sobre este último parece que hay muy poca información disponible. Ni siquiera conozco una función de escape específica, y mucho menos una que sea robusta cuando se obtiene una entrada torcida.

    
pregunta 14.05.2012 - 18:56
fuente

3 respuestas

10

Para poder montar una defensa adecuada contra una amenaza, necesitas entenderla. Los esquemas de codificación de capas ciegas son muy peligrosos ya que esto puede presentar un problema cuando, de manera predeterminada, por lo general no es un problema. En general, los problemas de seguridad relacionados con la codificación surgen porque las funciones de escape pueden actuar sobre los datos de manera diferente a como se interpretan. Pero esa no es la única preocupación, también puede confundir al programador porque piensan que una cadena se escapa, cuando no lo es.

La primera preocupación es que las funciones de escape pueden crear cadenas peligrosas para el atacante .

mysql_real_escape_string () es una función especial que sabe qué tipo de codificación está utilizando la base de datos, y ajusta su escape para seguir su ejemplo. Ahí nunca puede haber una desconexión entre el método de codificación, la función de escape y el intérprete. El simple uso de addslashes() puede ser muy peligroso por esta razón.

Como ejemplo, permite usar 0xbf27 , que es un solo carácter GBK. addslashes() no entiende GBK, solo entiende ASCII. Si esta cadena se interpretara como ASCII, el primer carácter sería 0xbf, que es un carácter ASCII no imprimible, permite llamar a ¿ . El segundo carácter ASCII es 0x27, que es una comilla simple ' . Después de las barras de adición () 0xbf27 se convierte en 0xbf5c27 o si se imprimiera en ASCII sería ¿\' . El problema es que 0xbf5c es un carácter GBK válido, por lo que addlashes () ha convertido un carácter de múltiples bytes en 2 caracteres, uno de ellos es una comilla simple. Otra forma de pensar esto es que la barra invertida está siendo consumida por la codificación GBK.

El consumo de caracteres puede ser un problema sin funciones de escape . Permite usar SHIFT-JS y HTML como ejemplo. Este ejemplo se tomó de The Tangled Web :

<img src="http://fuzzybunnies.com/0xEO">...thisisstillapartofthemarkup..." onerror="alret('this will execute!')"
<div>
...the page continues...

En este caso, los "> al final de la etiqueta img son caracteres de control muy importantes que se están consumiendo . " está siendo consumido por el esquema de codificación, y luego > y algún otro texto parece estar separado del atributo src de HTML, no es hasta que alcanza el " onerror que el atributo está terminado.

... Pero eso no es todo. Vamos a cambiar de marcha un poco. ¿Qué hay de urldecode() ?

<?php
$id=mysql_real_escape_string($_GET['id']);
$id=urldecode($id);
mysql_query("select * from user where id='".$id."'");

¿Ves la vulnerabilidad? Desde mi experiencia, la mayoría de los programadores de PHP no lo ven. El problema es que urldecode() se puede usar para crear una cadena vulnerable, digamos que el atacante proporcionó una cadena como http://localhost/vuln.php?id=%2527 or sleep(30)-- . Bueno, casi todas las plataformas de aplicaciones web ejecutarán automáticamente un código de usuario en todas las entradas HTTP. Por lo tanto, el contenido de $ _GET ['id'] es en realidad %27 or sleep(30)-- Esto se debe a que% 25 se ha decodificado en % (% es hex 25). Después de mysql_real_escape_string() o incluso addslashes() , el valor sigue siendo solo %27 or sleep(30)-- Después de ejecutar urldecode () nuevamente, el contenido de $id ahora es ' or sleep(30)-- , lo cual es peligroso.

Por lo tanto, como una sugerencia de pirata informático, cuando audito una base de código que usa magic_quotes_gpc o algo similar, repaso el código buscando funciones de decodificación , como urldcode() , htmlspecialchars_decode() , base64_decode() y funciones similares. A pesar de que la aplicación ejecuta el comando addlashes () a ciegas en todas las entradas, una función de decodificación permitirá al atacante construir su cadena de ataque.

Entonces, ¿cómo te defiendes? Pues prueba tu código. Algunos escáneres de aplicaciones web probarán la codificación de fallas exactamente como esta. De hecho, investigué este tema mientras construía un escáner vulnerable . Otro punto, siempre desinfectar la entrada en el momento de uso, esto evita el problema de descodificar después de escapar. No se puede saber cómo se usará, por lo que la codificación / decodificación / escape de todo siempre será defectuosa. Se utilizaron consultas parametrizadas para crear sentencias de SQL. UTF-8 es un gran valor predeterminado en la mayoría de los casos (pero no siempre), y * la mayoría * de estos problemas no surgen con este esquema de codificación de muli-byte.

    
respondido por el rook 15.05.2012 - 04:45
fuente
2

No, no recomiendo el enfoque que mencionaste.

Primero que nada, déjame retroceder. Conceptualmente, quizás el enfoque más seguro es aplicar una combinación de validación de entrada y escape de salida. Validación de entrada significa que usted define cuál es la forma de las entradas esperadas / buenas, y verifica que las entradas tengan esa forma. Escape de salida significa que se escapa de las salidas, según el contexto en el que se consumirán. La validación de entrada generalmente se realiza a las entradas tan pronto como las recibe; el escape de la salida generalmente se realiza al final, justo antes de insertar un valor en un documento HTML u otra salida.

Para obtener información general sobre los conceptos de validación de entrada y salida de salida, así como sugerencias de implementación, OWASP tiene algunos recursos excelentes. Consulte también esta pregunta: ¿Filtra las entradas del usuario antes de la base de datos o en la pantalla? , para obtener más información sobre los conceptos.

Para realizar la validación de entrada, creo que definiría una lista blanca de caracteres permitidos o una expresión regular que contenga la entrada esperada, y verificaría que la entrada coincida con esta expresión regular / lista blanca. (Una lista blanca adecuada debería tratar los problemas relacionados con las codificaciones de entrada divertidas). En algunos casos, otra forma de validación de entrada es convertir la entrada en un tipo específico, como (int) . La función de validación específica dependerá del tipo y formato de la entrada; no se puede usar un validador de talla única.

Para hacer un escape de salida, recomiendo usar una biblioteca de escape, como OWASP ESAPI. Debe comprender los diferentes contextos de análisis donde pueden aparecer los valores, y luego usar la función de escape correcta para ese contexto de análisis. Por ejemplo, htmlspecialchars() es una función de escape apropiada para valores dinámicos que se insertarán en un documento HTML entre etiquetas, pero no es apropiada para una URL (para las URL, también debe verificar que el protocolo sea válido). OWASP ESAPI proporciona un conjunto de funciones de escape para los contextos más comunes donde puede insertar datos en las salidas.

Asegúrese de utilizar declaraciones preparadas. No cree consultas SQL a través de la concatenación de cadenas.

Dicho esto, el soporte de PHP para la seguridad es débil. Muchos otros marcos de programación web modernos brindan un mayor soporte para la seguridad, por ejemplo, al proporcionar un sistema de plantillas con escape automático sensible al contexto , al proporcionar soporte automático para tokens CSRF, gestión segura de sesiones, inicios de sesión de usuarios, ORM de bases de datos, etc.

Ver también Ataques de seguridad en aplicaciones web de PHP , ¿Qué características de seguridad debería tener un marco PHP? , ¿Es strip_tags () horriblemente inseguro? , ¿Cuáles son algunas buenas herramientas gratuitas para ejecutar auditorías de seguridad automatizadas para el código PHP? , ¿Cómo realizar una auditoría de seguridad para una aplicación PHP? , ¿Por qué la gente dice que PHP es intrínsecamente inseguro? .

    
respondido por el D.W. 16.05.2012 - 03:02
fuente
0

He llegado a la conclusión de usar:

$input = mb_convert_encoding( $input, 'UTF-8' );

Para sanear la codificación de caracteres antes de realizar cualquier otra operación, como la validación de entrada y el escape de salida. Probablemente tendré que buscar en la fuente de mb_convert_encoding y en las pruebas unitarias para estar seguro, pero tengo la impresión de que siempre devolverá una cadena UTF-8 válida.

El resto de mi aplicación utilizará utf-8 en todo momento, lo que evita que todo se interprete de manera diferente hasta el navegador.

    
respondido por el user9651 29.09.2015 - 11:59
fuente

Lea otras preguntas en las etiquetas