Forum: PC-Programmierung CORS mit PHP und Javascript


von Kolja L. (kolja82)


Lesenswert?

Guten Abend,

kann mir hier jemand auf einfache Weise erklären, wie CORS im 
Zusammenhang eines lokal ausgeführten Javascriptes und einer PHP API auf 
einem Webserver funktioniert?

Ich habe auf beiden Seiten ein "*" für den Origin vergeben, bekomme aber 
keine Daten geliefert.
Wenn das Skript auf dem Webserver liegt, funktioniert es.

Danke und Gruß

Kolja

von DPA (Gast)


Lesenswert?

Der Browser checkt beim erhalt der Antwort, ob der Origin passt. Per 
default ist der erlaubte Origin der von der Url, wo der Request hin 
soll. Aber wenn der Server den "Access-Control-Allow-Origin" mit der 
Antwort mit senden, können noch weitere origins erlaubt werden. Der 
Origin, der mit dem erlaubtem gegengecheckt wird, ist der der Seite, die 
das script ausführt. Der origin ist der Teil der url aus 
schema://domain:port, ohne den Pfad dahinter. In manchen speziellen 
Situationen (z.B. wenn Authentifizierungsdaten per SSO mitgesendet 
werden sollen) macht der Browser aber vor dem echten request noch einem 
OPTION request, um zuerst zu prüfen, ob der request gemacht werden darf. 
Bei dem option request ist dann aber das gleiche spiel. Es gibt noch ein 
paar andere cors header, z.B. einer für die erlaubten Methoden. In 
manchen Situationen & für manche cors header funktioniert auch der wert 
* nicht, da muss man dann explizit sein. Das ist auch, warum gewisse 
APIs Access-Control-Allow-Origin auf den Wert des Origin headers vom 
Request setzen.

von Εrnst B. (ernst)


Lesenswert?

Kolja L. schrieb:
> Wenn das Skript auf dem Webserver liegt

Wenn es nicht am Webserver ist, ist es dann eine lokale Datei? Wie will 
die einen "Access-Control-Allow-Origin"-Header mitschicken, wenn sie 
geöffnet wird?

von Kolja L. (kolja82)


Lesenswert?

Danke DPA für die ausführliche Antwort, jetzt ist mir das Prinzip schon 
etwas deutlicher geworden.

Aber zum "Laufen" habe ich es noch nicht bekommen.

Ich liste mal auf ;-)

Das ist die Fehlermeldung vom Browser,
wenn ich meine Seite auf einem lokalen Server ausführe und die Daten vom 
Webserver anfordere:
1
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://dev.irgendwo.de/savemydata/api/login. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
2
3
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://dev.irgendwo.de/savemydata/api/login. (Reason: CORS request did not succeed). Status code: (null).

Mit dieser Funktion frage ich die Daten an:
1
const response = await fetch('https://domain.com/api', {
2
    method: 'POST',
3
    // credentials: 'same-origin',
4
    body: formData,
5
    headers: {
6
        'Content-Type': 'application/json',
7
    },
8
});
ob mit oder ohne oder anderen 'credentials' macht keinen Unterschied.

Hier noch die Ausgabefunktion vom Webserver:
1
header( 'Access-Control-Allow-Origin: *' );
2
header( 'Content-Type: application/json; charset=UTF-8' );
3
header( 'Access-Control-Allow-Methods: POST' );
4
header( 'Access-Control-Max-Age: 3600' );
5
header( 'Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With' );
6
echo json_encode( $response );

Mmn sollte das ganze doch so funktionieren, oder was mache ich falsch?

Danke schonmal für eure Unterstützung.

Kolja

: Bearbeitet durch Moderator
von 🐧 DPA 🐧 (Gast)


Lesenswert?

Ich würde mal mit "curl -v" oder einem Tool wie postman oder so mal 
nachsehen, ob die Header auch tatsächlich gesetzt werden.
In php kann man die header nur setzen, solange sie noch nicht gesendet 
sind. u.A. darf also noch nichts ausgegeben worden sein. Dass kann recht 
schnell mal passieren, z.B. wenn man einen abstand, newline, oder 
unicode BOM vor dem <?php stehen hat, oder sonst irgendwo im Code 
irgendwas ausgegeben wurde. Wenn die Warnungen ausgeschaltet sind, 
bekommt man davon auch gar nichts mit.

von Εrnst B. (ernst)


Lesenswert?

Weitere Fehlerquelle, hängt aber evtl. auch mit dem verfrühtem Output 
vor den header() - Aufrufen zusammen:

der Browser kann die Cors-Header per "HEAD"-Request abfragen, da hast du 
dann keine POST-Daten, musst aber trotzdem die Header ausliefern.
Und das PHP-Script  wird dann evtl. beim ersten Output abgebrochen, da 
der HEAD-Request keinen Body erwartet.

von Kolja L. (kolja82)


Lesenswert?

Bin gerade nicht zuhause, aber würde bei einer PHP Ausgabe vor dem 
header das ganze nicht auch nicht funktionieren, wenn das JavaScript auf 
dem Server liegt?

Das ist es ja, was mich so irritiert. Liegt beides auf dem Webserver 
funktioniert es, liegt das JS auf dem lokalen Server, geht es nicht.

von Εrnst B. (ernst)


Lesenswert?

Kolja L. schrieb:
> Liegt beides auf dem Webserver
> funktioniert es

dann greift die Same-Origin Policy, und es sind garkeine CORS-Header 
nötig.

von bluppdidupp (Gast)


Lesenswert?

Die Browserfehlermeldung sagt ja recht eindeutig das dev.irgendwo.de 
scheinbar real keinen Access-Control-Allow-Origin HTTP-Header sendet.
Da würde ich im Netzwerk-Tab in den Dev-Tools des Browsers (F12-Taste) 
als erstes mal nachschauen wie die Header im Response und der Response 
selbst denn tatsächlich aussehen...

: Bearbeitet durch Moderator
von DPA (Gast)


Lesenswert?

bluppdidupp schrieb:
> Da würde ich im Netzwerk-Tab in den Dev-Tools des Browsers (F12-Taste)
> als erstes mal nachschauen wie die Header im Response und der Response
> selbst denn tatsächlich aussehen...

Ich habe da mit cross origin geschichten schlechte erfahrungen mit den 
browser dev tools gemacht. Die zeigen da nicht immer alles 1:1, bei den 
cors geschichten.

von Stefan F. (Gast)


Lesenswert?

Ich fand diese Doku dazu am hilfreichsten. Konnte es damit in mehreren 
Programmiersprachen auf Anhieb umsetzen: 
https://developer.mozilla.org/de/docs/Web/HTTP/CORS

von Kolja L. (kolja82)


Lesenswert?

So, nach etwas warten hat sich das Problem leider nicht von selbst 
behoben,
aber ich habe mir die Abfrage aus dem Projekt extrahiert, um evtl. 
Kreuzreaktionen zu verhindern.

Die api.php wird über XAMPP (Apache/PHP) ausgegeben und die JS Anfrage 
über den "Live-Server" des VSCode Editors gestellt.

Es bleibt aber bei demselben Fehler:

> Access to fetch at 'http://localhost./php/api.php?list'; from origin 
'http://127.0.0.1:5500'; has been blocked by CORS policy: No 
'Access-Control-Allow-Origin' header is present on the requested resource. If an 
opaque response serves your needs, set the request's mode to 'no-cors' to fetch 
the resource with CORS disabled.

Die Anfrage ist denkbar simpel:
1
const URL = "http://localhost";
2
fetch(URL + "./php/api.php?list", { method: "GET" })

Der Header wird wie folgt ausgegeben:
1
header('Access-Control-Allow-Origin: *');
2
header('Content-Type: application/json; charset=UTF-8');
3
header('Access-Control-Allow-Methods: POST,GET');
4
header('Access-Control-Max-Age: 3600');
5
header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
6
echo json_encode($response);

Es ist die einzige Ausgabe der PHP-Datei (auch keine Warnungen, Fehler 
etc.) und wenn die Abfrage auch über XAMPP aufgerufen wird, funktioniert 
es.

In der gewünschten Konstellation (Abfrage und Antwort auf verschiedenen 
(lokalen) Servern, gibt es nach der Anfrage (Kopfzeile aus den 
DevTools):
1
Accept:  */*  
2
Accept-Encoding: gzip, deflate, br  
3
Accept-Language: de,en-US;q=0.7,en;q=0.3  
4
Connection: keep-alive    
5
Host: localhost.  
6
Origin: http://127.0.0.1:5500  
7
Referer: http://127.0.0.1:5500/  
8
Sec-Fetch-Dest: empty  
9
Sec-Fetch-Mode: cors  
10
Sec-Fetch-Site:  cross-site  
11
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101   Firefox/98.0

folgende Antwort:
1
scheme: https
2
host: localhost.
3
filename: /php/api.php  
4
Übertragen: 0B (0 B Größe)
5
Referrer Policy: strict-origin-when-cross-origin

Hat dazu noch jemand eine Idee, oder gebe ich das Problem jetzt einfach 
auf?

Gruß Kolja

von MaWin (Gast)


Lesenswert?

Kolja L. schrieb:
> kann mir hier jemand auf einfache Weise erklären, wie CORS im
> Zusammenhang eines lokal ausgeführten Javascriptes und einer PHP API auf
> einem Webserver funktioniert?

Wiso?

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Bei der Antwort ist am Anfang vermutlich noch ein "GET /php/api.php 
HTTP/1.1" oder so, dass man nicht sieht.
Die Antwort sieht merkwürdig aus. Diese Header (scheme:, filename: 
Übertragen:) habe ich in einer HTTP/1.1 Response noch nie gesehen. (und 
wieso scheme:https, wenn im fetch http steht?!?). Den 
"Access-Control-Allow-Origin" Header sieht man da auch nicht.
Kannst du eventuell mit Wireshark oder so nochmal nachsehen, ob da nicht 
was anderes gesendet wird?
Oder macht der Server da vielleicht sogar irgend welchen experimentellen 
HTTP/2 / SPDY oder HTTP/3 / QUIC Nonsens, der da zu Problemen führt?

von Kolja L. (kolja82)


Angehängte Dateien:

Lesenswert?

Alle bisherigen Angaben stammten aus dem Firefox, Chrome sagt da 
deutlich mehr(siehe Anhang).

von Stefan F. (Gast)


Lesenswert?

Du musst auf dem Server zwei HTTP Methoden implementieren:

> OPTIONS http://localhost./php/api.php?list

Muss mit einem leeren Body und folgenden Header beantwortet werden:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 86400

Diese Requests hast du in deinen Logs womöglich nicht gesehen, weil der 
Browser die Berechtigungen cached, so dass er den OPTIONS Request beim 
Wiederhol-versuch normalerweise nicht erneut sendet.

Danach ruft der Browser auf:
> GET http://localhost./php/api.php?list

Das muss mit folgendem Header beantwortet werden:

Access-Control-Allow-Origin: *

und was du sonst noch so als Response zurück geben wolltest.

Anstelle der Sternchen kannst du natürlich restriktiver sein 
(Komma-separierte Listen), aber damit würde ich erstmal anfangen.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Der Status 404 erscheint mir wichtig. Offenbar findet es die Datei unter 
"http://localhost./php/api.php?list"; nicht. Vermutlich ist dass das 
Hauptproblem.

Vermutlich hat der Server Probleme mit dem "localhost." Spezifischer, 
dem . am Schluss. "localhost." ist zwar eine gültige fqdn, es ist das 
selbe wie localhost, nur dass die search Domains beim DNS Lookup nicht 
beachtet werden. Nur senden Browser dann "Host: localhost." auch statt 
"Host: localhost", und die meisten Webserver oft nicht so clever zu 
merken, dass "Host: localhost." und localhost den selben vhost betreffen 
sollten. (Zumindest nicht ohne spezielle Zusatzkonfigurationen)

Also nimm den . da mal weg, damit da "http 
://localhost/php/api.php?list" draus wird. Oder alternativ, mach aus dem 
"http ://localhost" statdessen "http ://localhost/", dann wird daraus 
"http ://localhost/./php/api.php?list", was dann wieder passen sollte.

Alternativ kann man die URL auch mit der URL Klasse zusammensetzen:
1
const url = "http://localhost";
2
fetch((new URL("./php/api.php?list", url)).href, { method: "GET" })

von 🐧 DPA 🐧 (Gast)


Lesenswert?


von Kolja L. (kolja82)


Lesenswert?

Stefan ⛄ F. schrieb:
>> OPTIONS http://localhost./php/api.php?list

Danke für die ausführliche Antwort!

Leider hatte ich das schon mal vergeblich versucht (Hinweis von 
stackoverflow):
1
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
2
    header('Access-Control-Allow-Origin: *');
3
    header('Access-Control-Allow-Methods: *');
4
    header('Access-Control-Allow-Headers: *');
5
    header('Access-Control-Max-Age: 1728000');
6
    // die();
7
}

Oder wie kann/soll ich die Anfrage (GET,POST,OPTIONS) auseinander 
sortieren?

Die GET mache ich so:
1
if (isset($_GET['list']))

von Kolja L. (kolja82)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Der Status 404 erscheint mir wichtig. Offenbar findet es die Datei unter
> "http://localhost./php/api.php?list";; nicht. Vermutlich ist dass das
> Hauptproblem.

JA :-)

Ohne Punkt und mit dem o.g. Header für die OPTIONs funktioniert es!

Danke, vor allem Stefan und DPA für Eure Hilfe,
jetzt muss das ganze nur noch im Hauptprojekt auch funktionieren.

Gruß Kolja

von Stefan F. (Gast)


Lesenswert?

Kolja L. schrieb:
> Oder wie kann/soll ich die Anfrage (GET,POST,OPTIONS) auseinander
> sortieren?

Ist schon richtig so.

von Kolja L. (kolja82)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ist schon richtig so.

Und damit funktioniert es auch im "großen" Projekt :-)
Danke nochmal.

von Εrnst B. (ernst)


Lesenswert?

Kolja L. schrieb:
> Und damit funktioniert es auch im "großen" Projekt :-)

Meinst du damit die Produktivumgebung?
Ich dachte da gehts ohne, weil es dort kein "Cross-Origin" sondern 
sowieso "Same-Origin" ist.
Dann solltest du dort auch ohne Not keine CORS-Header setzen, vor allem 
keine so weit gefassten mit "*".

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.