CORS Fehler erklärt mit Lösungen – Missing Allow-Origin

CORS Erklärung und Lösung- Steffen Lippke

Was bedeutet CORS? Wie löse ich den Fehler?

Du möchtest verstehen, warum CORS entsteht und wie ich diesen verhindern kann?

Der Post garantiert, dass Du Deinen CORS beheben kannst.

Starten wir!

Ziel: CORS-Fehler verstehen + lösen

Wenn Du schon etwas mit Angular / Ionic gearbeitet hast, arbeitest Du Angulars HTTPModule. Die Anfänger frustriert der CORS-Fehler.

Heute will ich Dir zeigen, warum der CORS-Fehler entsteht und wie Du den CORS Fehler kurzfristig und dauerhaft umgehen kannst.

Die beakannte Webframeworks wie Race, Vue, Angular oder Ionic nutzen JavaScripts XMLHttpRequest als technische Grundlage, um entfernte APIs mit GET und POST abzurufen. XMLHttpRequest vermeidet das Neuladen der Tabs. Bei PHP ist das Neuladen zwingend notwendig, weil der Server die Daten für den Client vorbereitet (Server-seitiges Rendering).

Mehr Lesestoff: Alles zu GET / Post RFC

XMLHttpRequest cannot load https://api.example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.

Ein Framework wie Angular / Ionic vereinfacht auf den ersten Blick die Nutzung des umständlichen JavaScript XMLHttpRequest. Die Wirklichkeit sieht anders aus.

Konzept: SOP, CORS, GET + POST

Ich zeige Dir, wie ein typischer GET- und POST-Anfrage abläuft und welche „Regeln“ Dein Browser ständig überprüfen muss.

Um den Fehler zu verstehen, möchte ich isoliert GET– und POST-Abfragen auf Basis des Angular / Ionic Framework entwickeln. Wir bauen ein simples Userinterface mit zwei Buttons, um einen GET- und POST-Anfrage absenden zu können.

Ein Textfeld benötigt die Benutzeroberfläche unseres CORS-Demo-Projekts, um die Inhalte für die POST-Anfrage zu einzugeben.

Ein Angular-Provider / Service stellt das Angular Modul als Schnittstelle zum Internet dar.

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.

Grundlagen: Unterschiedliche Auflösungen. Gleiche App.

Bevor ich CORS erklären kann, möchte ich Dir die Same-Orign-Policy (SOP) näher bringen, weil diese mit dem CORS-Fehler unmittelbar zusammenhängt.

Unser Web soll sicher bleiben.

Die Same-Origin Policy (SOP) garantiert, dass der Browser ein  Webressource wie eine JavaScript- oder CSS-Dokument NUR von der GLEICHEN Domain laden darf.

Diese Regel unterbindet, dass ein Hacker mit XSS bösartigen Code über Deine Webseite auf den Geräten der Webseitennutzer ausführen kann.

In der Vergangenheit konnten Hacker bei Google Adsenes Werbung einkaufen. In der Werbung konnten sie JavaScript oder das veraltet Adobe Flash ausführen und Code von anderen Domänen laden. Der Code vom NICHT-GLEICHEN-URSPRUNG installierte Malware innerhalb vom Browsers oder hat Schadprogramme auf den Computer heruntergeladen.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://lippke.li/. (Reason: CORS request did not succeed).

Corss-Origin Resource Sharing (CORS) hilft dabei, die Sperre von Same-Origin Policy (SOP) zu umgehen. Dann kann Dein Browser auf die Script- und CSS-Dokumenten auf anderen Domains zugreifen.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://lippke.li/. (Reason: CORS header 'Access-Control-Allow-Origin' does not match 'http://localhost:8100/').

Wer hätte’s gedacht, CORS ist gut! – Wäre nicht die Anfänger unfreundliche Verwendung mit Angular.

Eine SIMPLE, grafische Erklärung von CORS und SOP

Wir gehen mal davon aus, dass Du eine neue fanzy Schriftart für Deine App / Webseite brauchst.

Um die kostenlose Schriftart von Google in Deine App einzubinden, musst Du eine HTTP-GET-Anfrage an den Google Server senden.

<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">

<style>
@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
</style>

Folgende Bilderfolge zeigen (stark vereinfacht), wie die Datenströme aussehen könnten.

Deine Webseite fragt mit GET nach und erhält ein .ttf Schriftart-Datei als Antwort zurück. Leider ist das nicht so einfach, weil die Same-Origin-Policy (SOP) das verbietet.

So stellt sich ein GET ohne CORS vor
So stellt sich ein GET ohne CORS vor

In der Realität läuft es aber etwas anderes ab.

Auf den ersten Blick sieht es so aus, dass wir eine GET-Abfrage absenden können, aber keine Antwort von Google erhalten.

Same Origin Policy sagt nein
Same Origin Policy sagt nein

Bei Dir taucht nicht mal eine 40X oder 50X Fehler auf, sondern folgender Warnung erscheint in Deiner Web-Konsole.

"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at $somesite" message: "Http failure response for https://lippke.li: 0 Unknown Error" name: "HttpErrorResponse" ok: false status: 0 statusText: "Unknown Error"

Bevor der Browser eine GET-Abfrage sendet, geht dem GET- eine OPTIONS-Anfrage zu vor.

Die OPTIONS-Anfrage ist wie ein Aufklärer / Späher im Krieg.

Die OPTIONS-Anfrage befragt den Server, welche Anfrage-Methoden der Server unterstützt. Die bekanntesten Abfragemethoden sind GET, POST, HEAD, PUT und DELETE.

Der Server antwortet zur Überprüfung von CORS; ob dieser die Allow-Orign-*-Header gesetzt sind. Falls diese nicht gesetzt sind, versucht Dein Browser es nicht einmal, die GET-Anfrage abzusenden.

Idalfall mit CORS
Idalfall mit CORS

Blöd.

Coden: LÖSUNG FÜR CORS

Zur Beruhigung: Ich habe für Dich 3 Lösungswege im Angebot.

Überlege zu nächst, ob Du den Backendtyp(in) kennst oder nicht …

Hast Du diese Frage geklärt, können wir EINEN, der drei Lösungsmöglichkeiten umsetzen.

CORS ist fast zu 100 % Backend-Programmierfehler (solange Du im Frontend nicht total unfähig bist).

Ich zeige Dir, wie Du die drei Lösungsansätze umsetzen kannst.

3 Lösungen für CORS
3 Lösungen für CORS

Fall #1: Du hast Kontrolle über das Backend

Setzte die CORS-Header mit PHP. Hier ein Beispiel:

function addCors() {

    // Teste ob die URL mit anderer Domain gestezt sist
    if (isset($_SERVER['HTTP_ORIGIN'])) {
        // Wichtige CORS header, um GET und POST zu erlauben
        header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');    // cache for 1 day
    }

    // Access-Control headers bekommt der OPTIONS-Request angehängt
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
            // Weitere Methoden wie PUT, DELETE ... möglich
            header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         

        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
            header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

        exit(0);
    }
}

addCors();

Die GET- und POST-Anfragen sollten zu DIESEM Server funktionieren. Die OPTION-Anfrage sagt Deinem Browser, dass der Server CORS-fähig ist und die GET-Anfrage ihr Glück versuchen kann.

header(*) setzt die CORS_ Header und der Befehl
$_SERVER['REQUEST_METHOD'] == 'OPTIONS'
sagt dem OPTION Request, welche HTTP-Methoden möglich sind.

Fall #2: Du brauchst schnell eine Lösung

Der folgende Abschnitt zeigt Dir schnellste Lösung. Später solltest Du Dir überlegen, ob Du nicht besser Fall 1 oder Fall 3 bearbeiten solltest.

In Firefox funktioniert das CORS-Everywhere Add-on am besten. https://addons.mozilla.org/de/firefox/addon/cors-everywhere/

CORS Everywhere Addon
CORS Everywhere Addon

Installiere Dir das Add-on, klicke auf das Symbol in der Toolbar, lade die Seite neu und BOOM – der CORS Fehler ist verschwunden.

In Chrome / Vivaldi (bzw. Chromium Engine Browser) funktioniert Vitvads CORS Plugin am besten.

https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi

Installiere Dir das Add-on, klicke auf das Symbol in der Toolbar und stelle den Schalter auf grün, lade die Seite neu und BOOM – der CORS Fehler ist verschwunden.

Sobald Du mit der Entwicklung Deiner App fertig bist, solltest Du Dir Gedanken über Fall 1 oder Fall 3 machen.

Fall #3: Du wirst den Backend-Deep NIE kennen.

Wenn Du eine langfristige bombenfeste Lösung brauchst, empfehle ich Dir einen PHP-Proxy zu schreiben. Dieser funktioniert folgendermaßen:

  1. Der Proxy nimmt jede URL per GET-Parameter auf. Der $_GET Befehl extrahiert die entsprechende URL heraus und speichert diese in einer Variable ab.
  2. Der Proxy ruft die Inhalte vom Server ab. Dafür verwendet PHP die Funktionen des cURL-Pakets. Dort kannst Du noch weitere Einstellungen für die GET-Abfrage vornehmen
  3. Der Proxy fügt die Allow-Origins-*-Header an.
  4. Der Proxy antwortet Deiner App. Dazu schreibt PHP die empfangenen Bytes mit dem Echo-Befehl in die PHP-Datei hinein.

Dieser Proxy arbeitet mit JSON: 'Content-type: application/json' und G-Zipping zur Komprimierung header('Content-Encoding: gzip');

<?php

// CORS Header
header('Content-Encoding: gzip'); 
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: X-Requested-With");
header('Content-Type: application/json');

// Komprimierung starten
ob_start('ob_gzhandler');

$url = $_GET['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
	'Content-type: application/json'
));
$response = curl_exec($ch);
echo $response;
curl_close($ch);

ob_end_flush();

?>

Praxis in Ionic

Erstelle eine Ionic Projekt mit

ionic start ioniccors tabs --type=angular

Für einen Provider für den HTTP-Traffic hinzu.

ionic g service corstester

Füge zwei Buttons und ein Textfeld in src > app >tab1> tab1.pages.html ein

<ion-header>
  <ion-toolbar>
    <ion-title>
   CORS Test
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
    <ion-input placeholder="POST Input" [(ngModel)]="postContent"></ion-input>
  <ion-button color="primary" (click)="sendGET()">Send a GET</ion-button>
  <ion-button color="secondary" (click)="sendPOST()">Send a POST</ion-button>
</ion-content>


TypeScript:

import { Component } from '@angular/core';
import { CorstesterService } from '../corstester.service';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {

  postContent;

  constructor(public corstester:CorstesterService) {}

  sendGET(){
    this.corstester.get("https://fonts.googleapis.com/css?family=Open+Sans&display=swap").subscribe(font =>{
      console.log(font);
      
    });
  }

  sendPOST(){
    this.corstester.post("https://lippke.li", {"hello":this.postContent}).subscribe(resp =>{
      console.log(resp);
      
    });
  }

}


Provider:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
@Injectable({
  providedIn: 'root'
})
export class CorstesterService {

  constructor(private http: HttpClient) { }

  get(url){
   
   return this.http.get(url, {responseType: 'text'});
  }

  post(url, content){
    return this.http.post(url, content, {responseType: 'text'});

  }
}

Erweiterungen: CORS vernichten

Wenn Du CORS verstanden hast, kannst Du auch mit anderen HTTP-Methoden experimentieren.

Teste doch einmal:

  • DELETE – Kann Daten vom Server löschen
  • PUT – Kann Dateien auf dem Server verändern
  • TRACE – Zum Zurückverfolgen des HTTP-Befehls

Erstelle einen Datenbank-Eintrag mit einem POST-Befehl.

<?php

//CORS HEADER + Zipping
header('Content-Encoding: gzip');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: X-Requested-With");

$connect = mysqli_connect("XXX.XXX.XXX.XXX:3306", "XXXXXX", "XXXXX", "XXXXX") or die("cannot connect");
$jsonTransaction = json_decode(file_get_contents('php://input'), true);


if ($connect->query($sql) === TRUE) { } else {
    $conn->error;
}
if ($connect->query($sqlChangeAccount) === TRUE) { } else {
    $conn->error;
}
$connect->close();
exit();

ob_end_flush();
?>
  1. Aktivere G-ZIP zur Komprimierung der Datenübertragung
  2. Setze die Standard-CORS-Header wie schon oben erwähnt
  3. Stelle eine Verbindung zur MySQL-Instanz her
  4. Mit file_get_contents erhältst Du den JSON-Inhalt
  5. Sende einen SQL-Befehl
  6. Schließe die Verbindung zur Datenbank
  7. Beende das G-Zipping

Fazit: Nie wieder CORS.

Ich hoffe, dass ich Euch ein besseres Verständnis von CORS verschaffen konnte.

Wenn CORS immer noch geht, kommentiere den Post.

Viel Spaß beim Programmieren.

Credits zu den verwendeten BildernIcons made by Vectors Market from www.flaticon.com is licensed by CC 3.0 BY

Kommentare 2

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Jeden Monat teile ich mit Mitgliedern
4 neue praxisnahe Tutorials (je 1000+ Wörter).


Trage Deine Mail, damit Du
Deine Coding + Hacking Skills erweitern kannst!

Die Webseite nutzt nur technisch notwendige Cookies.