Proteger una aplicación CodeIgniter contra CSRF

En el tutorial de hoy, aprenderemos cómo proteger sin problemas su aplicación CodeIgniter (pre 2.0) contra los ataques de falsificación de solicitudes entre sitios. La biblioteca que crearemos hoy automatizará todos los mecanismos de protección, haciendo que su sitio sea más sólido y seguro..


Paso 1 - Entendiendo el vector de ataque

Los ataques de falsificación de solicitudes entre sitios se basan en formularios no protegidos en sus sitios.

Un atacante podría crear un formulario falso en su sitio, por ejemplo, un formulario de búsqueda. Este formulario podría tener entradas ocultas que contienen datos maliciosos. Ahora el formulario no se envía realmente al sitio del atacante para realizar la búsqueda; en realidad, la forma apunta a tu ¡sitio! Dado que su sitio web confiará en que el formulario es genuino, pasa y ejecuta las acciones solicitadas (y quizás maliciosas).

Imagine que un usuario ha iniciado sesión en su sitio y se le redirige al sitio del atacante por algún motivo (phishing, XSS, nombre). El formulario del atacante podría apuntar al formulario de eliminación de su cuenta en su sitio. Si el usuario realiza una "búsqueda" en el sitio de los atacantes, su cuenta se eliminará sin que él lo sepa.!

Existen numerosas formas de prevenir este tipo de ataques..

  • Verifique el encabezado de HTTP Referer y vea si pertenece a su sitio. El problema con este método es que no todos los navegadores envían este encabezado (personalmente tuve este problema una vez con IE7); podría ser forjado de todos modos.
  • Otro método (el que usaremos), es incluir una cadena aleatoria (un "token") en cada formulario y almacenar ese token en la sesión del usuario. En cada ENVIAR solicite, compare el token enviado con el de la tienda y, si difieren, deniegue la solicitud. Sin embargo, su sitio aún necesita ser protegido contra XSS, porque si no lo es, este método se vuelve inútil..

Paso 2 - Planificación

Tendremos que hacer tres cosas para cada solicitud:

  • Si la solicitud es un ENVIAR Solicitar, validar que el token enviado..
  • Generar un token en caso de que no haya uno.
  • Inyecte el token en todas las formas. Esto hace que el método sea transparente e indoloro, ya que no se necesitan modificaciones en sus vistas..

Para hacer esto automáticamente, usaremos los ganchos de CodeIgniter. Los ganchos nos permiten ejecutar todas las acciones en diferentes partes de la solicitud. Necesitaremos tres:

  • post_controller_constructor - Para comprobar el token enviado, necesitaremos un post_controller_constructor gancho. Conectar esta acción antes de este evento no nos da acceso a la instancia de CodeIgniter correctamente.
  • Generar el token - Para generar el token, usaremos el mismo gancho que antes. Esto nos permite tener acceso a él en caso de que necesitáramos imprimirlo manualmente en nuestras vistas.
  • display_override - Para inyectar el token automáticamente en nuestras vistas, necesitaremos usar el display_override gancho. Sin embargo, este es un gancho difícil, ya que, como su nombre lo indica, anula la visualización de las vistas. Necesitamos enviar el contenido nosotros mismos si usamos este gancho (consulte la documentación de CodeIgniter para obtener más información).

Paso 3 - Generación de fichas

Empecemos. Iremos paso a paso para explicar todo lo mejor posible. Primero, crearemos el método que genere el token, de modo que podamos probar todo correctamente. Crea un archivo en tu sistema / aplicación / ganchos carpeta llamada "csrf.php", y pegue el siguiente código:

CI = & get_instance (); 

Con suerte, lo que hemos agregado arriba debería parecerte algo básico. Estamos creando una clase, llamada CSRF_Protección, una variable de instancia para mantener la instancia de CodeIgniter, dos variables estáticas para mantener el nombre del parámetro que almacenará el token y una para almacenar el token en sí para un fácil acceso en toda la clase. Dentro del constructor de la clase (__construir), simplemente recuperamos la instancia de CodeIgniter y la almacenamos en nuestra variable de instancia correspondiente.

Nota: El "nombre del parámetro" es el nombre del campo que contiene el token. Así que en los formularios, es el nombre de la entrada oculta.

Después del constructor de la clase, pegue el siguiente código:

/ ** * Genera un token CSRF y lo almacena en la sesión. Solo se genera un token por sesión. * Esto debe estar vinculado a un enlace posterior al controlador, y antes del enlace * que llama al método inject_tokens (). * * @return void * @autor Ian Murray * / public function generate_token () // Cargar biblioteca de sesión si no está cargada $ this-> CI-> load-> library ('session'); if ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Genere un token y guárdelo en la sesión, ya que el antiguo parece haber caducado. self :: $ token = md5 (uniqid (). microtime (). rand ()); $ this-> CI-> session-> set_userdata (self :: $ token_name, self :: $ token);  else // Establézcalo en una variable local para un fácil acceso self :: $ token = $ this-> CI-> session-> userdata (self :: $ token_name); 

Paso a paso:

  • Nosotros cargamos la biblioteca de sesiones, en caso de que no se cargue automáticamente. Necesitamos esto para guardar el token..
  • Determinamos si el token ya fue generado. Si el datos del usuario método devuelve FALSO, entonces el token aún no está presente.
  • Generamos un token y lo almacenamos en la variable de clase para facilitar el acceso. El token podría ser casi cualquier cosa en realidad. Utilicé esas tres funciones para asegurar que sea muy aleatorio.
  • Luego lo almacenamos en la variable de sesión con el nombre configurado previamente.
  • Si el token ya estaba presente, nosotros no hacer genérelo y, en su lugar, almacénelo en la variable de clase para facilitar el acceso.

Paso 4 - Validación del token

Necesitamos asegurarnos de que el token haya sido enviado, y sea válido en caso de que la solicitud sea un ENVIAR solicitud. Continúa y pega el siguiente código en tu csrf.php expediente:

/ ** * Valida un token enviado cuando se realiza una solicitud POST. * * @return void * @autor Ian Murray * / public function validate_tokens () // ¿Es esta una solicitud de publicación? if ($ _SERVER ['REQUEST_METHOD'] == 'POST') // ¿Está establecido el campo del token y es válido? $ posted_token = $ this-> CI-> input-> post (self :: $ token_name); if ($ posted_token === FALSE || $ posted_token! = $ this-> CI-> session-> userdata (self :: $ token_name)) // Solicitud no válida, enviar error 400. show_error ('La solicitud no fue válida. Las fichas no coinciden. ', 400); 
  • Arriba, validamos la solicitud, pero solamente si es un ENVIAR solicitud, lo que significa que un formulario fue, de hecho, presentado. Comprobamos esto echando un vistazo a la REQUEST_METHOD dentro de $ _SERVER super global.
  • Compruebe si el token fue publicado. Si la salida de $ this-> CI-> input-> post (self :: $ token_name) es FALSO, entonces el token nunca fue publicado.
  • Si el token no se publicó o si no es igual al que generamos, rechace la solicitud con un error de "Solicitud incorrecta".

Paso 5 - Inyectar fichas en las vistas

¡Esta es la parte divertida! Necesitamos inyectar los tokens en todas las formas. Para hacernos la vida más fácil, vamos a colocar dos etiquetas meta dentro de nuestra (Como rieles). De esa manera, podemos incluir el token en las solicitudes AJAX también.

Agregue el siguiente código a su csrf.php expediente:

/ ** * Esto inyecta etiquetas ocultas en todos los formularios POST con el token csrf. * También inyecta meta encabezados en  de salida (si existe) para un fácil acceso * desde marcos de JS. * * @return void * @autor Ian Murray * / public function inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Inyectar en la forma $ output = preg_replace ('/ (<(form|FORM)[^>] * (método | METHOD) = "(post | POST)" [^>] *>) / ',' $ 0', $ output); // inyectar en  $ output = preg_replace ('/ (<\/head>) / ',''. "\ n". ''. "\ n". '$ 0', $ salida); $ this-> CI-> output -> _ display ($ output); 
  • Ya que esta es una display_override Enganche, necesitamos recuperar la salida generada desde CodeIgniter. Hacemos esto usando el $ this-> CI-> output-> get_output () método.
  • Para inyectar las etiquetas en nuestros formularios, usaremos expresiones regulares. La expresión asegura que inyectamos una etiqueta de entrada oculta (que contiene nuestro token generado) solo en formularios con un método de tipo ENVIAR.
  • También necesitamos inyectar nuestras etiquetas meta en el encabezamiento (si está presente). Esto es simple, ya que la etiqueta de encabezado de cierre solo debe estar presente una vez por archivo.
  • Por último, ya que estamos usando el display_override gancho, el método predeterminado para mostrar su vista no será llamado. Este método incluye todo tipo de cosas, que no deberíamos, solo con el propósito de inyectar algún código. Llamándolo nosotros mismos resuelve esto.

Paso 6 - Ganchos

Por último, pero no menos importante, debemos crear los ganchos por sí mismos, para que nuestros métodos sean llamados. Pegue el siguiente código en su sistema / aplicación / config / hooks.php expediente:

// // CSRF Los enlaces de protección, no los toques a menos que sepas lo que estás haciendo. // // ¡LA ORDEN DE ESTOS GANCHOS ES EXTREMADAMENTE IMPORTANTE! // // ESTO TIENE QUE IR PRIMERO EN LA LISTA DE GANCHOS post_controller_constructor. $ hook ['post_controller_constructor'] [] = array (// Fíjate en "[]", este no es el único hook post_controller_constructor 'class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filename' = > 'csrf.php', 'filepath' => 'hooks'); // Genera el token (DEBE PASAR DESPUÉS DE QUE SE HAGA LA VALIDACIÓN, PERO ANTES DE QUE EL CONTROLADOR // SE EJECUTE, EL USUARIO DE OTRA MANERA NO TIENE ACCESO A UN TOKEN VÁLIDO PARA FORMULARIOS PERSONALIZADOS). $ hook ['post_controller_constructor'] [] = array (// Tenga en cuenta que "[]", este no es el único hook post_controller_constructor 'class' => 'CSRF_Protection', 'function' => 'generate_token', 'filename' = > 'csrf.php', 'filepath' => 'hooks'); // Esto inyecta tokens en todas las formas $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filename' => 'csrf.php', 'filepath' => 'ganchos');
  • El primer gancho llama al validate_tokens método. Esta no es la unica post_controller_constructor gancho, así que tenemos que añadir esos corchetes ("[]"). Consulte la documentación en los ganchos de CodeIgniter para más información.
  • El segundo gancho, que también es un post_controller_constructor, genera el token en caso de que aún no se haya generado.
  • El tercero es la anulación de la pantalla. Este gancho inyectará nuestras fichas en el formar y el encabezamiento.

Terminando

Con un mínimo esfuerzo, hemos construido una biblioteca bastante agradable para nosotros mismos..

Puede usar esta biblioteca en cualquier proyecto, y protegerá automáticamente su sitio contra CSRF.

Si desea contribuir a este pequeño proyecto, deje un comentario a continuación, o bifurque el proyecto en GitHub. Como alternativa, a partir de CodeIgniter v2.0, la protección contra ataques CSRF ahora está incorporada en el marco!