¿Es seguro corregir errores de Access-Control-Allow-Origin (CORS origin) con una directiva de encabezado PHP?
Estoy tratando de mostrar un formulario de Formidable Pro desde un sitio WordPress a otro. Seguí la API del desarrollador y la API REST, pero me encontré con un problema de CORS.
Luego encontré una sugerencia en un hilo del foro que sugería agregar esta línea de código al functions.php
del sitio donde está el formulario original:
header("Access-Control-Allow-Origin: *");
Probé este código y funcionó perfectamente.
Mi pregunta es: ¿este código genera riesgos de seguridad u otras vulnerabilidades?
La solución parece demasiado simple para un problema que enfrentan muchas personas.
Agradezco sus comentarios.

Sí, estás abriendo tu sitio para que pueda ser solicitado vía AJAX desde cualquier otro script en toda la web.
Sería mejor si limitas el origen a un dominio remoto específico desde el cual estás consumiendo la API, como este ejemplo:
header("Access-Control-Allow-Origin: http://mozilla.com");
Sin embargo, como indica la documentación de Mozilla, un cliente puede falsificar el origen, pero limitar los sitios a los que un usuario casual puede conectarse es un disuasivo para algunos ataques.
Aún mejor, puedes limitar tus solicitudes a solo los métodos que realmente necesitas permitir, la esencia es este fragmento de código, y funciona para varios dominios, si tienes la variable $_SERVER['HTTP_ORIGIN']
poblada:
add_action('rest_api_init', function() {
/* desenganchar la función por defecto */
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
/* luego añadir tu propio filtro */
add_filter('rest_pre_serve_request', function( $value ) {
$origin = get_http_origin();
$my_sites = array( 'http://site1.org/', 'http://site2.net', );
if ( in_array( $origin, $my_sites ) ) {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
} else {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( site_url() ) );
}
header( 'Access-Control-Allow-Methods: GET' );
return $value;
});
}, 15);
Como puedes ver, este fragmento usa la función get_http_origin proporcionada por WordPress, pero devolverá null o vacío, si la clave HTTP_ORIGIN
no está poblada en el array $_SERVER
, por lo tanto no está disponible para el script PHP, quizás porque está bloqueado por el proxy de Cloudflare que estás usando. Yo comprobaría rápidamente, con un script con el código <?php phpinfo(); ?>
, si tienes esta variable poblada.
Quizás el sitio de origen está poblado en otra cabecera por Cloudflare, y podrías usarlo en una función enganchada al filtro http_origin
. Si estás perdido en este punto, edita tu pregunta original publicando los contenidos de la variable _SERVER, excepto tus rutas de sistema de archivos o contraseñas.
Estaré encantado de ayudar.

Gracias. La primera solución funcionó para mí. Pero como tengo 4 sitios a los que quiero permitirles acceder al sitio principal, ¿repito la línea 4 veces y cambio la URL del sitio, o se puede combinar en un solo comando? Gracias.

Por supuesto que puedes, suelo permitir el acceso a la API solo a unos pocos sitios, he actualizado mi respuesta con la verificación para esto, si funciona, ¿te importaría votar la respuesta?

La primera solución funcionó solo para 1 dominio. Si repetí la línea, no funciona. Probé el método en este hilo enlace, pero tampoco funciona.
Cuando aplico la solución 2, esto es lo que obtengo: XMLHttpRequest cannot load https://coptic-treasures.com/wp-json/frm/v2/forms/6?return=html. The 'Access-Control-Allow-Origin' header has a value 'https://coptic-treasures.com' that is not equal to the supplied origin. Origin 'https://audio.coptic-treasures.com' is therefore not allowed access.

No puedes usar el encabezado Allow Origin más de una vez. He actualizado mi respuesta con más instrucciones para ayudarte a depurar el problema, porque siempre devolver el mismo sitio no debería ocurrir y debería reconocer la lista de dominios que pasas al array. Por favor actualiza tu pregunta con los datos de la variable $_SERVER, devuelta por phpinfo()
, excluyendo contraseñas y rutas del sistema de archivos. Estaré encantado de ayudar.

Me ha tomado alrededor de 2 días descubrir cómo eliminar el acceso CORS de la API WP-JSON. Gracias por esto. ¡Elimina la llamada a la función rest_send_cors_headers
!

Antecedentes - los navegadores están restringiendo el acceso remoto desde scripts solo al sitio desde el cual se cargó. Si no se hiciera este tipo de verificación, mientras visitas un sitio X, sería posible que este enviara datos a tu cuenta de Gmail (si estás logueado) sin siquiera necesitar adivinar tu usuario y contraseña, porque el navegador enviaría las cookies de autenticación adecuadas a Gmail.
El "protocolo" CORS está ahí para ayudarte a relajar esta restricción cuando sea necesario.
Así que la pregunta que debes hacerte es: ¿realmente lo necesito? Por un lado, no veo por qué el 99% de los sitios WordPress lo necesitarían, por otro lado, las cookies de WordPress tienen una vida relativamente corta y el 99% de los sitios WordPress no van a ser objetivo de un ataque aleatorio de este tipo.
Yo diría que, dado que la API REST es mutable y tiene acceso a información privada, no deberías usar la API REST en tu solución si necesitas habilitar CORS. Es mejor que escribas tu propia API de "solo lectura".

El OP no está hablando del 99% de los sitios WP que existen, sino de sus propios sitios, y según la pregunta es necesario hacer disponibles recursos desde otro sitio.

Que el punto es responder la pregunta, ese es el objetivo de este sitio.

@JesúsFranco, creo que mi reputación demuestra que sé cómo responder preguntas y no necesito tus consejos?

Entonces, ¿tienes una idea específica de cómo el OP puede resolver este problema?

@JesúsFranco, el OP preguntó si es seguro. La respuesta es que no lo es. La solución obviamente es no hacer cosas que requieran usar cors

Mi pregunta se basa en lo que mencionas sobre el riesgo de acceso a información privada. En realidad, uno de los principios fundamentales de la API es que solo el desarrollador puede otorgar acceso a información privada, ya que la API tal como se proporciona originalmente no lo hace.

@JesúsFranco Hasta donde yo sé, la API utiliza cookies para autenticación y personas con rol de administrador, por ejemplo, pueden obtener casi cualquier información. Si deshabilitas completamente CORS y estoy usando algún tipo de phishing para llevarte a mi sitio, puedo escribir JS que realizará cualquier operación en tu sitio, porque estás autenticado en él y tu navegador enviará las cookies de autenticación como parte de la solicitud. Ni siquiera necesitaré adivinar tu nombre de usuario... No es suficiente con bloquear el acceso a los endpoints de "escritura", ya que como administrador puedes obtener información clasificada.

Es posible que actualmente no haya mucho que se pueda obtener con la API, pero su objetivo es poder reemplazar por completo el panel de administración, por lo que tendrá que servir al cliente todos los datos existentes.

Gracias Mark, conocer el mecanismo que puede ser abusado para convertir el acceso API en algo inseguro, es muy útil para pensar cómo manejar esos riesgos, y luego poder decidir si se puede usar un parche, limitar de otra manera los usos si los métodos utilizables para scripts remotos, o escribir una API completamente nueva.

@MarkKaplun Soy nuevo en WP-API así que tenía una pregunta. ¿Permitir CORS solo para métodos 'GET' también expondría el servidor a vulnerabilidades?

@shariqkhan, probablemente esto sea mejor preguntarlo en SO o en el stack de seguridad, pero en general, la respuesta es sí. CORS debería estar deshabilitado solo para sitios en los que confíes o en subdominios tuyos que no realicen ninguna acción sin credenciales de autorización enviadas en el cuerpo de la solicitud. Mientras la API REST use cookies para autenticación y se ejecute en el mismo dominio que el sitio WP, debes ser muy cuidadoso al deshabilitarlo.

pero nuevamente, si deshabilitas CORS solo para una URL específica que sabes que no podrá usarse para causar daño, entonces podría estar bien. No es tanto una cuestión de GET vs POST, sino de qué es lo que realmente se está haciendo en ese endpoint

Gracias @MarkKaplun, he visto muchas de tus respuestas en SO/SE así que sé que sabes de lo que hablas. Probablemente lo preguntaré en SO, aunque siento que la pregunta podría cerrarse por ser "demasiado amplia" o como "duplicada". Me intriga cómo varios sitios web como Github, Twitter (y muchos sitios basados en WordPress también) abren sus APIs deshabilitando restricciones CORS para todos. Tenía la impresión de que si deshabilitas CORS solo para solicitudes GET, es bastante inofensivo porque solo permites que otros obtengan datos y no modifiquen nada.

La diferencia entre WordPress y otros sitios es que no usan autenticación basada en cookies para la API, y el uso de la API requiere enviar información de usuario y contraseña explícitamente en cada solicitud. La API de WordPress <alerta de rant> estaba mal diseñada en ese aspecto, y probablemente no pudo diseñarse mejor ya que tenía que soportar comunicación insegura sobre HTTP, lo que le impedía usar la técnica de usuario/contraseña ya que cualquiera que escuchara en una (por ejemplo) WiFi habría descubierto los valores.

el hecho de que tengas las cookies configuradas cuando estás conectado es lo que los atacantes pueden explotar para enviar solicitudes AJAX como si fueran originadas por ti. Con CORS activado, esas solicitudes fallarán para otros dominios, y como eres el único que controla el dominio, estás seguro. Cuantos más sitios para los que desactives CORS, más lugares tendrá un atacante para tomar el control y atacarte induciéndote a visitar ese sitio. La pregunta era sobre desactivar completamente CORS para todos los sitios, lo cual es muy malo, pero si lo desactivas para otro dominio que controlas por completo, aunque no sea algo ideal, no es tan malo.
