¿Cómo omitir Access-Control-Allow-Origin?

197

Estoy haciendo una llamada ajax a mi propio servidor en una plataforma que configuraron para evitar estas llamadas ajax (pero necesito que recupere los datos de mi servidor para mostrar los datos recuperados de la base de datos de mi servidor). Mi script ajax está funcionando, puede enviar los datos al script php de mi servidor para permitir que se procese. Sin embargo, no puede recuperar los datos procesados ​​ya que está bloqueado por"Access-Control-Allow-Origin"

No tengo acceso a la fuente / núcleo de esa plataforma. así que no puedo eliminar el script que me impide hacerlo. (P / SI utilizó la consola de Google Chrome y descubrió este error)

El código Ajax como se muestra a continuación:

 $.ajax({
     type: "GET",
     url: "http://example.com/retrieve.php",
     data: "id=" + id + "&url=" + url,
     dataType: 'json',   
     cache: false,
     success: function(data)
      {
        var friend = data[1];              
        var blog = data[2];           
        $('#user').html("<b>Friends: </b>"+friend+"<b><br> Blogs: </b>"+blog);

      } 
  });

o hay un JSONcódigo equivalente al script ajax anterior? Creo que JSONestá permitido

Espero que alguien pueda ayudarme.

ETAN
fuente
Todas las respuestas a su pregunta hasta ahora explicaron una forma de reescribir el código de su servidor para que ajax funcione. Ninguno de ellos tiene que ver con eludir, como usted preguntó específicamente en su pregunta. ¿Encontró de todos modos eludir este encabezado? Realmente dudo que haya uno.
Moradnejad
no hay forma de evitarlo. pero puede poner un archivo en su backend que realice la solicitud. Entonces, llama a ajax el archivo en su propio servidor, ese archivo carga los datos de retrieve.php y los envía de vuelta a su javascript. En ese caso, no hay reglas CORS que lo bloqueen.
Jona Paulus

Respuestas:

367

Pon esto encima de retrieve.php:

header('Access-Control-Allow-Origin: *');  

Tenga en cuenta que esto deshabilita efectivamente la protección CORS y deja a sus usuarios expuestos a ataques. Si no está completamente seguro de que necesita permitir todos los orígenes, debe bloquear esto a un origen más específico:

header('Access-Control-Allow-Origin: https://www.example.com')

Consulte la siguiente respuesta de la pila para comprender mejor Access-Control-Allow-Origin

https://stackoverflow.com/a/10636765/413670

Rafay
fuente
54
Eso es bastante inseguro. Mira mi respuesta en la parte inferior.
Rob
3
tnx, pero no debe permitir el acceso a todos los orígenes como lo menciona @RobQuist en su comentario, y en su respuesta proporcionó un mejor enfoque
Rafay
2
Así que encontré esta página porque realmente necesitaba 'evitar' el control de acceso en un servidor. La solución aquí no es omitir nada, sino simplemente configurar correctamente el control de acceso en su propio servidor. En caso de que alguien realmente necesite evitar esto, puede usar el file_get_contents de PHP ($ remote_url) ;. Obviamente, hay muchas maneras de hacer esto, pero así es como lo hice.
Shawn Whinnery
1
@ShawnWhinnery que es básicamente el acto de "representación". Buena solución si realmente desea cargar dinámicamente datos de otro sitio web que no tiene control.
Rob
1
quería ejecutar el script PHP desde el núcleo de dotnet; moví el script php a mi otra URL, pero recibí un error de scripting entre sitios. agregó el código que mostró al principio de PHP y funcionó perfectamente. ¡Gracias!
raddevus
291

De acuerdo, ¿pero todos ustedes saben que el * es un comodín y permite la creación de secuencias de comandos en todos los dominios?

Le gustaría enviar múltiples Access-Control-Allow-Originencabezados para cada sitio que está permitido, pero desafortunadamente oficialmente no es compatible para enviar múltiples Access-Control-Allow-Originencabezados o para poner en múltiples orígenes.

Puede resolver esto comprobando el origen y devolviendo el que está en el encabezado, si está permitido:

$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
    'http://mysite1.com',
    'https://www.mysite2.com',
    'http://www.mysite2.com',
];

if (in_array($origin, $allowed_domains)) {
    header('Access-Control-Allow-Origin: ' . $origin);
}

Eso es mucho más seguro. Es posible que desee editar la coincidencia y cambiarla a una función manual con alguna expresión regular, o algo así. Al menos esto solo devolverá 1 encabezado, y estará seguro de que es de donde proviene la solicitud. Tenga en cuenta que todos los encabezados HTTP pueden ser falsificados, pero este encabezado es para la protección del cliente. No proteja sus propios datos con esos valores. Si quieres saber más, lee un poco sobre CORS y CSRF.

¿Por qué es más seguro?

Permitir el acceso desde otras ubicaciones y luego su propio sitio de confianza permite el secuestro de sesiones. Voy a ir con un pequeño ejemplo: la imagen de Facebook permite un origen comodín; esto significa que puede crear su propio sitio web en algún lugar y hacer que active llamadas AJAX (o abra iframes) a Facebook. Esto significa que puede obtener la información de inicio de sesión del facebook de un visitante de su sitio web. Peor aún: puede realizar POSTsolicitudes de script y publicar datos en el facebook de alguien, solo mientras navega por su sitio web.

¡Ten mucho cuidado al usar los ACAOencabezados!

Robar
fuente
12
Creo que debe poner http: // delante de cada elemento de la lista. Al menos lo hice para un sitio en el que estaba trabajando.
blak3r
2
Lamentablemente, esto no parece funcionar. Creo que solo se puede proporcionar una excepción por llamada al encabezado ().
lewsid
55
@Shanimal & lewsid -> Supongo que la coma separada no funciona realmente. Referencia: w3.org/TR/cors
Rob
3
Para tratar con una lista de dominios, aquí hay una respuesta relevante: stackoverflow.com/a/1850482/766177
Valentin Despa
13
Esto no tiene sentido agregar 4 encabezados así porque cada llamada a header()reemplaza el encabezado anterior del mismo tipo. Entonces, realmente todo lo que está haciendo es configurar el último encabezado. La entrada manual indica que puede establecer un segundo parámetro falsepara evitar que se sobrescriba el encabezado anterior.
BadHorsie
31

Advertencia , Chrome (y otros navegadores) se quejarán de que se configuran múltiples encabezados ACAO si sigue algunas de las otras respuestas.

El error será algo como XMLHttpRequest cannot load ____. The 'Access-Control-Allow-Origin' header contains multiple values '____, ____, ____', but only one is allowed. Origin '____' is therefore not allowed access.

Prueba esto:

$http_origin = $_SERVER['HTTP_ORIGIN'];

$allowed_domains = array(
  'http://domain1.com',
  'http://domain2.com',
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

fuente
66
Esta es una solución aún mejor que la que publiqué.
Rob
7

He solucionado este problema al llamar a un controlador MVC3. Yo añadí:

Response.AddHeader("Access-Control-Allow-Origin", "*"); 

antes de mi

return Json(model, JsonRequestBehavior.AllowGet);

Y también me $.ajaxquejé de que no acepta el encabezado Content-type en mi llamada ajax, así que lo comenté porque sé que su JSON se pasa a la Acción.

Espero que ayude.

Atif Rehman
fuente
2

Es una idea realmente mala de usar *, lo que te deja completamente abierto a las secuencias de comandos de sitios cruzados. Básicamente, desea tener su propio dominio todo el tiempo, con un alcance de su configuración SSL actual y, opcionalmente, dominios adicionales. También desea que todos se envíen como un encabezado. Lo siguiente siempre autorizará su propio dominio en el mismo alcance SSL que la página actual, y opcionalmente también puede incluir cualquier número de dominios adicionales. Los enviará a todos como un encabezado y sobrescribirá los anteriores si algo más ya los envió para evitar cualquier posibilidad de que el navegador se queje de que se envíen múltiples encabezados de control de acceso.

class CorsAccessControl
{
    private $allowed = array();

    /**
     * Always adds your own domain with the current ssl settings.
     */
    public function __construct()
    {
        // Add your own domain, with respect to the current SSL settings.
        $this->allowed[] = 'http'
            . ( ( array_key_exists( 'HTTPS', $_SERVER )
                && $_SERVER['HTTPS'] 
                && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) 
                    ? 's' 
                    : null )
            . '://' . $_SERVER['HTTP_HOST'];
    }

    /**
     * Optionally add additional domains. Each is only added one time.
     */
    public function add($domain)
    {
        if ( !in_array( $domain, $this->allowed )
        {
            $this->allowed[] = $domain;
        }
    /**
     * Send 'em all as one header so no browsers grumble about it.
     */
    public function send()
    {
        $domains = implode( ', ', $this->allowed );
        header( 'Access-Control-Allow-Origin: ' . $domains, true ); // We want to send them all as one shot, so replace should be true here.
    }
}

Uso:

$cors = new CorsAccessControl();

// If you are only authorizing your own domain:
$cors->send();

// If you are authorizing multiple domains:
foreach ($domains as $domain)
{
    $cors->add($domain);
}
$cors->send();

Tienes la idea.

mopsyd
fuente
1

¿Has intentado realmente agregar el encabezado Access-Control-Allow-Origin a la respuesta enviada desde tu servidor? Como Access-Control-Allow-Origin: *?

Daniel Brockman
fuente
1
Es un encabezado HTTP que su servidor envía para informar al navegador que está bien revelar el resultado al script de llamada a pesar del hecho de que el dominio de origen del script no coincide con el dominio del servidor. ¡Lea sobre el intercambio de recursos de origen cruzado !
Daniel Brockman