El tutorial Jobeet

22.7. Guardando formularios en la cache

Guardar en la cache la página de publicación de ofertas de trabajo es complicado porque contiene un formulario. Para que entiendas mejor el problema, accede una vez a la página para publicar una oferta de trabajo. Ahora que la página se ha guardado en la cache, borra la cookie de la sesión y trata de publicar la oferta de trabajo. Si has seguido estos pasos, verás un mensaje de error advirtiendo de un posible ataque de tipo CSRF:

Mensaje sobre un posible ataque de tipo CSRF al usar la cache

Figura 22.9 Mensaje sobre un posible ataque de tipo CSRF al usar la cache

¿Por qué sucede este error? Como al crear la aplicación frontend configuramos una palabra secreta relacionada con CSRF, Symfony incluye un token CSRF en todos los formularios. Para evitar ataques de tipo CSRF, el token es único para cada formulario de cada usuario.

La primera vez que accedes a la página del formulario, el codigo HTML del formulario que se guarda en la cache incluye el token del usuario actual. Si después otro usuario accede a la misma página, el navegador muestra la página guardada en la cache y que contiene el token del primer usuario. Cuando el usuario envía el formulario, Symfony detecta que los dos tokens no coinciden y muestra el mensaje de error sobre un posible ataque de tipo CSRF.

¿Cómo podríamos solucionar el problema y al mismo tiempo seguir guardando el formulario en la cache? El formulario de publicación de ofertas de trabajo no depende del usuario y no modifica ninguna información del usuario actual. Por tanto, en este caso no necesitamos activar la protección CSRF y podemos eliminar el token CSRF del formulario:

// plugins/sfJobeetJob/lib/form/PluginJobeetJobForm.class.php
abstract class PluginJobeetJobForm extends BaseJobeetJobForm
{
  public function __construct(BaseObject $object = null, $options = array(), $CSRFSecret = null)
  {
    parent::__construct($object, $options, false);
  }

  // ...
}

Después de realizar este cambio, borra la cache y vuelve a probar el mismo escenario explicado anteriormente para comprobar que ahora todo funciona correctamente.

A continuación aplica la misma configuración al formulario para seleccionar el idioma que se encuentra en el layout y que queremos guardar en la cache. Como utilizamos el formulario sfLanguageForm por defecto, en vez de crear una nueva clase sólo para eliminar el token CSRF, vamos a realizar el cambio directamente en la acción y el componente del módulo sfJobeetLanguage:

// plugins/sfJobeetJob/modules/sfJobeetLanguage/actions/components.class.php
class sfJobeetLanguageComponents extends sfComponents
{
  public function executeLanguage(sfWebRequest $request)
  {
    $this->form = new sfFormLanguage($this->getUser(), array('languages' => array('en', 'fr')));
    unset($this->form[$this->form->getCSRFFieldName()]);
  }
}
// plugins/sfJobeetJob/modules/sfJobeetLanguage/actions/actions.class.php
class sfJobeetLanguageActions extends sfActions
{
  public function executeChangeLanguage(sfWebRequest $request)
  {
    $form = new sfFormLanguage($this->getUser(), array('languages' => array('en', 'fr')));
    unset($form[$form->getCSRFFieldName()]);

    // ...
  }
}

El método getCSRFFieldName() devuelve el nombre del campo que contiene el token CSRF. Eliminar este campo del formulario provoca que también se eliminen el widget y el validador asociados al campo.