misterunknown: Cross-Origin-Request und $_SESSION-Array

Moin,

für eine HTML5-App habe ich ein API geschrieben, welches verschiedene Abfragen an den Server regelt. Serverseitig existiert ein Login-Skript (login.php) und ein Skript für das Handling der Abfragen (api.php).

Beim Anmelden wird der Username sowie ein gehashtes Passwort übergeben und serverseitig mit den Angaben abgeglichen; ist alles ok, werden verschiedene Daten in das Session-Array geschrieben. Anschließend wird die Session-ID zurückgegeben. Soweit funktioniert alles. Nun habe ich aber folgendes Problem:

Wenn ich dann von einem Client aus, auf das API-Skript zugreifen will, übergebe ich die Session-ID und versuche sie wiederherzustellen (mit session_name() und session_start()). Soweit scheint das auch zu funkionieren (es kann natürlich auch sein, er erstellt einfach eine neue Session, die er so nennt). Fakt ist: Das Session-Array bleibt leer. Nun habe ich schon einige Zeit herumprobiert, habe aber scheinbar ein Verständnisproblem.
Die Frage ist: Wie schaffe ich es, dass $_SESSION-Array wiederherzustellen?

Hier der relevante Code:
login.php:

if($_POST['password']==$set['password']) {  
	$_SESSION = $set;  
	$_SESSION['auth'] = "OK";  
	echo("{\n\t\"status\": \"OK\",\n\t\"id\" : \"".session_id()."\"\n}");  
}

api.php:

if(isset($_POST['SID'])){ session_name($_POST['SID']); echo("SESSION wiederaufgenommen: ".$_POST['SID']."\n");}  
session_start();  
echo(session_name()."\n"); // hier wird die korrekte Session-ID, die auch übergeben wurde, ausgegeben  
print_r($_SESSION); // ist trotz allem leer :(

Noch ein Hinweis: Zum Entwickeln hatte ich eine Test-Client-Skript auf der selben Subdomain wie login.php und api.php liegen. Da hat alles gepasst, selbst ohne Übergeben der Session-ID, da die Session ja bestehen blieb. Von einer anderen Domain ist es ein Cross-Origin-Request, welchen ich auch erst über eine .htaccess-Datei erlauben musste. Irgendwie muss es ja aber gehen, es gibt ja viele Apps, die per HTTP-Requests mit Servern kommunizieren...

Grüße Marco

--
Ich spreche Spaghetticode - fließend.
  1. if(isset($_POST['SID'])){ session_name($_POST['SID']); echo("SESSION wiederaufgenommen: ".$_POST['SID']."\n");}

    session_start();
    echo(session_name()."\n"); // hier wird die korrekte Session-ID, die auch übergeben wurde, ausgegeben
    print_r($_SESSION); // ist trotz allem leer :(

      
    Ich würde sagen, du erzeugst einfach ne neue Session  und überschreibst die ID mit der bekannten.  
    Schonmal versucht, die ID direkt nach dem Erzeugen anzeigen zu lassen?  
      
    
    > Noch ein Hinweis: Zum Entwickeln hatte ich eine Test-Client-Skript auf der selben Subdomain wie login.php und api.php liegen. Da hat alles gepasst, selbst ohne Übergeben der Session-ID, da die Session ja bestehen blieb. Von einer anderen Domain ist es ein Cross-Origin-Request, welchen ich auch erst über eine .htaccess-Datei erlauben musste. Irgendwie muss es ja aber gehen, es gibt ja viele Apps, die per HTTP-Requests mit Servern kommunizieren...  
      
    Ich hab mir eigene Session-Handler geschrieben, die auf eine Mysql-Tabelle zugreifen. Da kann ich festlegen, ob andere Server drauf zugreifen dürfen. Habs zwar nie versucht aber so könnte man evtl. mit ner Shared Session über mehrere Server arbeiten.
    
    1. Moin,

      Ich würde sagen, du erzeugst einfach ne neue Session  und überschreibst die ID mit der bekannten.
      Schonmal versucht, die ID direkt nach dem Erzeugen anzeigen zu lassen?

      Was meinst du mit "direkt nach dem Erzeugen"? Nach session_start(); gebe ich ja den Name der Session nochmal aus. Es ist derselbe, wie der übergebene.

      Ich hab mir eigene Session-Handler geschrieben, die auf eine Mysql-Tabelle zugreifen.

      Das wäre auch eine Möglichkeit. Wenn ich die Daten in einer Datenbank speichere, kann ich die ja mit der Session-Id auch wieder herausholen. Allerdings muss man dann ja auch Dienste laufen lassen, die die Sessions aus der Datenbank löschen, wenn sie abgelaufen sind (nach einer definierten Zeit). Oder wie hast du das gelöst?

      Wenn es funktionieren würde, einfach das $_SESSION-Array wiederherzustellen, würde ich eben einiges an Arbeit sparen...

      Grüße Marco

      --
      Ich spreche Spaghetticode - fließend.
      1. Allerdings muss man dann ja auch Dienste laufen lassen, die die Sessions aus der Datenbank löschen, wenn sie abgelaufen sind (nach einer definierten Zeit). Oder wie hast du das gelöst?

        Der Garbage-Collector läuft doch eh. Du brauchst im Handler ja nur angeben, was der löschen soll. Ist in der PHP-Doku ein Beispiel drin, glaub ich. Zumindest die entsprechenden Methoden/Funktionen und was sie tun.

  2. Tach!

    Wie schaffe ich es, dass $_SESSION-Array wiederherzustellen?

    Du brauchst die Session-ID und die Datei, in der die Session-Daten aufbewahrt sind. Die ID kannst du ja mittels Kontrollausgabe kontrollieren. Die Datei(en) sollten an einem Platz liegen, an dem kein anderes PHP-Script aufräumt. Das heißt, wenn du (oder der Hoster) mehrere Projekte auf demselben Server laufen hast, sollten alle ein separates Verzeichnis bekommen (session.save_path). Es kann sonst passieren, dass die Garbage Collection einer Anwendung auf kürzere Zeit eingestellt ist und so die Sessiondaten aller Scripte vorzeitig löscht. Diese werden nämlich nicht unterschiedlich behandelt, nur weil sie ein anderes Script angelegt hat.

    echo(session_name()."\n"); // hier wird die korrekte Session-ID, die auch übergeben wurde, ausgegeben

    Die Session-ID wird aber über die Funktion session_id() gesetzt und gelesen. session_name() oder auch die Konfigurationsdirektive session.name setzt nur den Cookie- oder GET/POST-Parameter-Namen. Das ist das, was per default PHPSESSID heißt.

    Zum Entwickeln hatte ich eine Test-Client-Skript auf der selben Subdomain wie login.php und api.php liegen. Da hat alles gepasst, selbst ohne Übergeben der Session-ID, da die Session ja bestehen blieb. Von einer anderen Domain ist es ein Cross-Origin-Request, welchen ich auch erst über eine .htaccess-Datei erlauben musste.

    PHP unterscheidet nicht nach der Herkunft der Session-ID. Es ist also egal, ob sie aus einem String-Literal oder einer externen Datenquelle kommt - wenn sie denn der richtigen Funktion übergeben wird.

    dedlfix.

    1. Moin,

      Das heißt, wenn du (oder der Hoster) mehrere Projekte auf demselben Server laufen hast, sollten alle ein separates Verzeichnis bekommen (session.save_path). Es kann sonst passieren, dass die Garbage Collection einer Anwendung auf kürzere Zeit eingestellt ist und so die Sessiondaten aller Scripte vorzeitig löscht. Diese werden nämlich nicht unterschiedlich behandelt, nur weil sie ein anderes Script angelegt hat.

      Der Hoster bin ich, das ist manchmal das Problem^^ Danke für die Infos, ich werde das jetzt mal versuchen so umzusetzen.

      Die Session-ID wird aber über die Funktion session_id() gesetzt und gelesen.

      Die Funktion hatte ich schon gesehen, hatte aber nicht wirklich einen Unterschied zwischen Sessionname und Sessionid gesehen.

      Das ist das, was per default PHPSESSID heißt.

      Alles klar. Ich hatte zwar in der PHP-Doku nach Sessions geguckt, dort wurde ich aber nur begrenzt informiert.

      PHP unterscheidet nicht nach der Herkunft der Session-ID. Es ist also egal, ob sie aus einem String-Literal oder einer externen Datenquelle kommt - wenn sie denn der richtigen Funktion übergeben wird.

      Ok, das dachte ich mir schon. Aber wenn mans nicht genau weiß...^^

      Grüße Marco

      --
      Ich spreche Spaghetticode - fließend.
  3. Moin!

    für eine HTML5-App habe ich ein API geschrieben, welches verschiedene Abfragen an den Server regelt. Serverseitig existiert ein Login-Skript (login.php) und ein Skript für das Handling der Abfragen (api.php).

    Wo ist das Cross-Origin in deiner Frage?

    Wenn ich dann von einem Client aus, auf das API-Skript zugreifen will, übergebe ich die Session-ID und versuche sie wiederherzustellen (mit session_name() und session_start()). Soweit scheint das auch zu funkionieren (es kann natürlich auch sein, er erstellt einfach eine neue Session, die er so nennt). Fakt ist: Das Session-Array bleibt leer. Nun habe ich schon einige Zeit herumprobiert, habe aber scheinbar ein Verständnisproblem.

    Dass "session_name" die falsche Funktion ist, um die Session-ID zu setzen, hat dedlfix schon gesagt. Es sei hier aus Gründen der Eindrücklickkeit wiederholt.

    if(isset($_POST['SID'])){ session_name($_POST['SID']); echo("SESSION wiederaufgenommen: ".$_POST['SID']."\n");}

    session_start();
    echo(session_name()."\n"); // hier wird die korrekte Session-ID, die auch übergeben wurde, ausgegeben
    print_r($_SESSION); // ist trotz allem leer :(

      
    Es ist zu beachten, dass das Ausgeben von Text vor dem Start der Session unschöne Effekte mit nicht setzbaren Headern zur Folge haben kann, sofern PHP standardmäßig ein Session-Cookie mit der ID generieren will.  
      
     - Sven Rautenberg
    
    1. Moin,

      Wo ist das Cross-Origin in deiner Frage?

      Zum Entwickeln der Funktionen des APIs habe ich eine Seite auf derselben Subdomain liegen gehabt, die per AJAX die Funktionalität getestet hat. Dort musste ich mir keine Sorgen um die Session machen, da sie die ganze Zeit weiterbestanden hat; man konnte also einfach mehrere AJAX-Requests absenden.

      Als ich die Seite dann auf eine andere Subdomain verschoben habe ging gar nichts mehr - Grund: Cross-Origin-Request. Das habe ich serverseitig mit einer .htaccess gelöst, in der selbiges erlaubt wurde. Clientseitig erlaubt das Chrome schon einige Zeit. Einzelne Requests konnte ich jetzt senden, aber die Session wurde nach jedem Request entweder vernichtet oder eben nicht erkannt. Selbigem wollte ich eben mit übergeben der Session-ID vorbeugen.

      Es ist zu beachten, dass das Ausgeben von Text vor dem Start der Session unschöne Effekte mit nicht setzbaren Headern zur Folge haben kann, sofern PHP standardmäßig ein Session-Cookie mit der ID generieren will.

      Das stimmt. Das war von mir bewusst so zu Debuggingzwecken gebaut.

      Grüße Marco

      --
      Ich spreche Spaghetticode - fließend.
      1. Moin!

        Wo ist das Cross-Origin in deiner Frage?

        Zum Entwickeln der Funktionen des APIs habe ich eine Seite auf derselben Subdomain liegen gehabt, die per AJAX die Funktionalität getestet hat. Dort musste ich mir keine Sorgen um die Session machen, da sie die ganze Zeit weiterbestanden hat; man konnte also einfach mehrere AJAX-Requests absenden.

        Als ich die Seite dann auf eine andere Subdomain verschoben habe ging gar nichts mehr - Grund: Cross-Origin-Request. Das habe ich serverseitig mit einer .htaccess gelöst, in der selbiges erlaubt wurde. Clientseitig erlaubt das Chrome schon einige Zeit. Einzelne Requests konnte ich jetzt senden, aber die Session wurde nach jedem Request entweder vernichtet oder eben nicht erkannt. Selbigem wollte ich eben mit übergeben der Session-ID vorbeugen.

        Was einigermaßen unsinnig ist, weil es sich mit dem Konfigurieren des Session-Cookies viel leichter umgehen lässt. Wenn das Cookie für die gesamte Domain "*.example.com" gilt, kriegt jede Subdomain diese Information, und es muss nicht manuell in der URL weitergereicht werden.

        - Sven Rautenberg