En 2018 Drupal implementó el soporte para oEmbed en media y la posibilidad de servirlos desde un dominio distinto al principal con el fin de añadir una capa extra de seguridad contra ataques de tipo XSS (Cross Site Scripting).
Por defecto, estos recursos de terceros ya se muestran dentro de un iframe, pero bajo el mismo dominio, lo cual hace posible que el recurso oembed pueda acceder a cookies del dominio principal. Esto constituye una brecha de seguridad que el proveedor del recurso puede usar.
Configurar un dominio propio para los iframes de oEmbed es muy sencillo. Solo necesitas acceder a /admin/config/media/media-settings y establecer un subdominio específico. De esta manera, Drupal comenzará a servir los contenidos oEmbed desde ese subdominio, aislándolos del dominio principal y añadiendo una capa extra de seguridad.
Sin embargo, esta configuración no solo afecta a los iframes de oEmbed, sino que también provoca que toda la web sea accesible desde ese subdominio, lo que puede causar problemas de SEO, ya que los motores de búsqueda podrían indexar el contenido duplicado o confundir la autoridad del dominio principal.
Por lo tanto, es importante restringir el tráfico del dominio oEmbed y establecer la configuración correcta en las reglas CSP.
Requisitos
- Dominio con su certificado apuntando al mismo servidor que la web principal
- Módulo para gestionar las reglas CSP (Content Security Policy)
Cómo restringir el tráfico del dominio para oEmbed
Para restringir el tráfico en el dominio para oembed, usaremos una regla en el .htaccess con la que restringiremos las consultas que pueden acceder a esa URL. Usaremos el {HTTP_REFERER} para hacer esa criba, estableciendo que si el {HTTP_REFERER} es del dominio principal es una petición valida y se muestra el recurso.
<IfModule mod_rewrite.c>
RewriteEngine on
# Only request to oembed subdomains from allowed domains are allowed,
# otherwise a 403 is displayed.
<If "%{HTTP_HOST} =~ m#oembed.(.*)# && %{HTTP_REFERER} =~ m#https?://(.*\.)*(ddev\.site|my-domain\.com).*#">
Header set x-Drupal-Oembed "TRUE"
</If>
<ElseIf "%{HTTP_HOST} =~ m#oembed.(.*)#">
RewriteRule .* - [F,L]
</ElseIf>
</IfModule>
En la primera regla se comprueba que:
- El host de la petición es al dominio de los oembed, en este caso "oembed.my-domian.com"
- El referer no está vacío y coincide con alguno de los dominios de la web:
- https://www.my-domian-com -> produccion
- https://my-domian-com -> produccion
- https://dev.my-domian-com -> entorno de desarollo
- http://my-domian.ddev.site -> entorno local
No es necesaria ninguna acción si se cumplen las condiciones anteriores, en el ejemplo se establece una cabecera a modo de debug.
En la segunda condición se devuelve un 403, deteniéndose la ejecución del htaccess. En lugar de devolver un 403, se puede realizar una redirección, por ejemplo, al dominio principal, pero habría que tener en cuenta la configuración de las capas de caché externas que puedan existir en el proyecto (por ejemplo, Varnish), ya que puede cachear las redirecciones generando efectos indeseados.
Configuración del CSP
El módulo CSP (Content Security Policy) mejora la seguridad de un sitio web, definiendo qué fuentes de contenido pueden cargarse y mostrarse.
Para que nuestra implementación sea segura y funcione correctamente, es fundamental configurar adecuadamente este módulo.
URLs que se han de tener en cuenta
- La URL del sitio (https://www.my-domain) y sus variantes para cada entorno (https://www.dev.my-domain).
- La URL "oembed" (https://oembed.my-domain) y sus variantes para cada entorno (https://dev.oembed.my-domain).
- Finalmente, la URL del recurso que queremos embeber, por ejemplo, YouTube (https://www.youtube.com).
Aprovecharemos el uso del símbolo * en CSP, que actúa como comodín, para simplificar la configuración de las URLs:
- https://*.my-domain: referencia tanto la URL principal como las de los distintos entornos y las de oembed;
- https://*.youtube.com: aplicamos el mismo patrón, aunque en este caso no es estrictamente necesario.
Directivas
Existen tres directivas en CSP en las que debemos centrarnos:
- frame-src: especifica las fuentes fiables para los elementos <iframe> y <frame>;
- frame-ancestors: define los hosts fiables que tienen permitido embeber los recursos del sitio mediante <iframe>, <frame>, <object>, <embed> y <applet>;
- child-src: especifica las fuentes fiables para los elementos <iframe> y <frame>, así como para la carga de Workers.
Configuración de directivas
Las directivas deberán configurarse de la siguiente manera:
- frame-src: 'self' https://*.my-domain.com https://*.youtube.com
- frame-ancestors: 'self' https://*.my-domain
- child-src: 'self' https://*.youtube.com
Posibles problemas
Caché de las redirecciones
Como se comentó en el ejemplo, si se opta por hacer una redirección, es necesario tener en cuenta la configuracion de capa de caché externa ya que si alguien intenta acceder directamente al recurso (https://oembred.my-domain.com/media/oembed?...) al no tener el referer adecuado se devuelve un 403 y resuelve a una pantalla con sus cabeceras de caché. Esa respuesta va a quedar cacheada en la capa de caché externa y en futuras peticiones ya es la capa de caché externa la que devuelve el 403, no llega a Drupal.
Iframe allow features
El tag "allow" en los iframe, indica las APIs del navegador a las que puede acceder el contenido del iframe. Es obligatorio rellenarlo ya que por defecto los iframes que sirven contenido de otro origen no tienen acceso a las APIs. Un subdominio se considera un origen diferente.
Otro punto a tener en cuenta es que aunque se indique que puede acceder a un API, el dominio tiene que ser un origen válido. Por defecto son todos (*), pero puede suceder que estén restringidos con la cabecera "Permissions-Policy".