Matthias Apsel: Wiki-Artikel Reloadsperre

0 48

Wiki-Artikel Reloadsperre

Matthias Apsel
  • php
  • programmiertechnik
  • selfhtml-wiki
  1. 0
    Christian Kruse
    1. 0
      Matthias Apsel
      1. 0
        Christian Kruse
        1. 0
          Matthias Apsel
          1. 0
            Christian Kruse
            1. 0
              Matthias Apsel
              1. 0
                Christian Kruse
                1. 0
                  Matthias Apsel
                  1. 0
                    Christian Kruse
                    1. 0
                      Matthias Apsel
                    2. 0
                      Matthias Apsel
                      1. 0
                        Matthias Apsel
                        1. 0
                          Christian Kruse
                          1. 0
                            Matthias Apsel
                            1. 0
                              Christian Kruse
                              1. 0
                                Matthias Apsel
                                1. 0
                                  Christian Kruse
                                  1. 0
                                    Matthias Apsel
                                    1. 0
                                      Der Martin
                                      1. 1
                                        Auge
                                        1. 0
                                          Christian Kruse
                                    2. 0
                                      Christian Kruse
        2. 0
          Mitleser
          1. 0
            Christian Kruse
    2. 0
      Matthias Apsel
      1. 1
        Christian Kruse
        1. 0
          Matthias Apsel
          1. 1
            Christian Kruse
            1. 0
              Matthias Apsel
              1. 0
                Christian Kruse
                1. 0
                  Matthias Apsel
                  1. 0
                    Christian Kruse
                2. 0
                  Matthias Apsel
                  1. 0
                    Christian Kruse
                    1. 0
                      Matthias Apsel
                3. 0
                  Matthias Apsel
                  1. 0
                    Christian Kruse
                    1. 0
                      Matthias Apsel
                      1. 0
                        Christian Kruse
                        1. 0
                          Matthias Apsel
                        2. 0
                          Matthias Apsel
                          1. 0
                            Christian Kruse
                            1. 0
                              Matthias Apsel
                              1. 0
                                Christian Kruse
                                1. 1
                                  Matthias Apsel
                                  1. 0
                                    Christian Kruse
  2. -1
    pl

problematische Seite

Hallo alle,

ich habe heute zufällig den Artikel zur Reloadsperre von 2013 gefunden. Was müsste geändert werden, damit der in den Hauptnamensraum darf?

Bis demnächst
Matthias

--
Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.

akzeptierte Antworten

  1. problematische Seite

    Hallo Matthias,

    ich habe heute zufällig den Artikel zur Reloadsperre von 2013 gefunden. Was müsste geändert werden, damit der in den Hauptnamensraum darf?

    Der Teil mit der Weiterleitung muss überarbeitet werden: wenn man History Back klickt, gelangt man nicht zum POST-Request, sondern zum vorhergehenden GET-Request. Die beiden anderen Methoden funktionieren weiterhin, allerdings hat sich der Redirect nach dem POST als best practice herausgestellt.

    LG,
    CK

    1. problematische Seite

      Hallo Christian Kruse,

      wenn man History Back klickt, gelangt man nicht zum POST-Request, sondern zum vorhergehenden GET-Request.

      Hm. Die Screenshots hab ich mir ja nicht ausgedacht. Und wenn ich mich recht erinnere, gelingt es tatsächlich, das Formular erneut abzusenden.

      Bis demnächst
      Matthias

      --
      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
      1. problematische Seite

        Hallo Matthias,

        wenn man History Back klickt, gelangt man nicht zum POST-Request, sondern zum vorhergehenden GET-Request.

        Hm. Die Screenshots hab ich mir ja nicht ausgedacht. Und wenn ich mich recht erinnere, gelingt es tatsächlich, das Formular erneut abzusenden.

        Dinge ändern sich. Früher war das so - heute nicht mehr.

        Auf diesem Prinzip basieren viele Web-Anwendungen, im Rails-Framework, im Django-Framework, im Zend-Framework – überall wird dieses Prinzip so genutzt. Du kannst es hier im Forum ja selber mal testen. Die letzte Zeile im Messages-Controller:

        redirect_to message_url(@thread, @message), :notice => I18n.t('messages.created')
        

        LG,
        CK

        1. problematische Seite

          Hallo Christian Kruse,

          Auf diesem Prinzip basieren viele Web-Anwendungen, im Rails-Framework, im Django-Framework, im Zend-Framework – überall wird dieses Prinzip so genutzt. Du kannst es hier im Forum ja selber mal testen. Die letzte Zeile im Messages-Controller:

          redirect_to message_url(@thread, @message), :notice => I18n.t('messages.created')
          

          Aber mit einem redirect allein bin ich offenbar trotzdem noch nicht auf der sicheren Seite. Versuche etwa dieses Formular.

          Bis demnächst
          Matthias

          --
          Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
          1. problematische Seite

            Hallo Matthias,

            Aber mit einem redirect allein bin ich offenbar trotzdem noch nicht auf der sicheren Seite. Versuche etwa dieses Formular.

            Da findet kein Redirect statt.

            Alternativ-Text

            LG,
            CK

            1. problematische Seite

              Hallo Christian Kruse,

              Da findet kein Redirect statt.

              Dann reden wir wohl (wieder mal) aneinander vorbei ;-)

              Im Artikel heißt es: „Wenn die Daten nicht eindeutig sein müssen, wird häufig empfohlen, auf eine Seite weiterzuleiten, die nicht das Formular enthält, sondern lediglich eine Mitteilung darüber, dass die Übertragung der Daten erfolgreich war.“

              Genau das passiert doch in meinem Beispiel??

              Bis demnächst
              Matthias

              --
              Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
              1. problematische Seite

                Hallo Matthias,

                Dann reden wir wohl (wieder mal) aneinander vorbei ;-)

                Ich glaube nicht.

                Im Artikel heißt es: „Wenn die Daten nicht eindeutig sein müssen, wird häufig empfohlen, auf eine Seite weiterzuleiten, die nicht das Formular enthält, sondern lediglich eine Mitteilung darüber, dass die Übertragung der Daten erfolgreich war.“

                Genau das passiert doch in meinem Beispiel??

                Nein, es wird nur eine Efolgsmeldung auf den POST-Request ausgeliefert (Status 200), siehe Screenshot. Es muss aber ein redirect stattfinden (Status 302), das weiterleiten aus dem obigen Text fehlt. Pseudo-Code von dem, was in deinem Form-Mailer passiert:

                parseData();
                createDatabaseEntry();
                sendSuccessResponse();
                

                Was passieren müsste:

                parseData();
                createDatabaseEntry();
                sendRedirect(status: 302, url: '/foo/bar');
                

                LG,
                CK

                1. problematische Seite

                  Hallo Christian Kruse,

                  Nein, es wird nur eine Efolgsmeldung auf den POST-Request ausgeliefert (Status 200), siehe Screenshot. Es muss aber ein redirect stattfinden (Status 302), das weiterleiten aus dem obigen Text fehlt.

                  Danke. Das war mir in der Deutlichkeit nicht klar.

                  Was passieren müsste:

                  parseData();
                  createDatabaseEntry();
                  sendRedirect(status: 302, url: '/foo/bar');
                  

                  Und wenn das so gemacht wird, sollte ich nicht mehr entsprechend der Screenshots (aus dem Wiki-Artikel) gefragt werden ob ich die Daten erneut absenden möchte, falls ich den zurück-Button des Browsers verwende?

                  Bis demnächst
                  Matthias

                  --
                  Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                  1. problematische Seite

                    Hallo Matthias,

                    parseData();
                    createDatabaseEntry();
                    sendRedirect(status: 302, url: '/foo/bar');
                    

                    Und wenn das so gemacht wird, sollte ich nicht mehr entsprechend der Screenshots (aus dem Wiki-Artikel) gefragt werden ob ich die Daten erneut absenden möchte, falls ich den zurück-Button des Browsers verwende?

                    Ich finde deine Formulierung ein wenig verwirrend, deshalb formuliere ich das Verhalten, dass Browser an den Tag legen:

                    Wenn ein Server auf einen POST-Request mit einem 302 antwortet und der User dann History Back auslöst, dann wird der POST-Request nicht wiederholt sondern man kehrt zur vorhergehenden GET-Ressource zurück. Du kannst das aber immer noch gerne hier im Forum testen, du kehrst dann zu /new zurück ;-)

                    LG,
                    CK

                    1. problematische Seite

                      Hallo Christian Kruse,

                      Ich finde deine Formulierung ein wenig verwirrend,

                      siehste, doch ein wenig an einander vorbei ;-)

                      Wenn ein Server auf einen POST-Request mit einem 302 antwortet und der User dann History Back auslöst, dann wird der POST-Request nicht wiederholt sondern man kehrt zur vorhergehenden GET-Ressource zurück. Du kannst das aber immer noch gerne hier im Forum testen, du kehrst dann zu /new zurück ;-)

                      Teste ich.

                      Bis demnächst
                      Matthias

                      --
                      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                    2. problematische Seite

                      Hallo Christian Kruse,

                      Ich finde deine Formulierung ein wenig verwirrend,

                      siehste, doch ein wenig an einander vorbei ;-)

                      Wenn ein Server auf einen POST-Request mit einem 302 antwortet und der User dann History Back auslöst, dann wird der POST-Request nicht wiederholt sondern man kehrt zur vorhergehenden GET-Ressource zurück. Du kannst das aber immer noch gerne hier im Forum testen, du kehrst dann zu /new zurück ;-)

                      Teste ich.

                      Bis demnächst
                      Matthias

                      --
                      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                      1. problematische Seite

                        Hallo Matthias Apsel, @Christian Kruse

                        Wenn ein Server auf einen POST-Request mit einem 302 antwortet und der User dann History Back auslöst, dann wird der POST-Request nicht wiederholt sondern man kehrt zur vorhergehenden GET-Ressource zurück. Du kannst das aber immer noch gerne hier im Forum testen, du kehrst dann zu /new zurück ;-)

                        Teste ich.

                        So. Zwei identische Nachrichten. Ich schrieb ja auch nicht, dass der Request automatisch wiederholt wird, sondern dass man ihn wiederholen kann.

                        Bis demnächst
                        Matthias

                        --
                        Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                        1. problematische Seite

                          Hallo Matthias,

                          So. Zwei identische Nachrichten. Ich schrieb ja auch nicht, dass der Request automatisch wiederholt wird, sondern dass man ihn wiederholen kann.

                          Nein, du schriebst von einer Reload-Sperre. History back und das formular händisch erneut absenden (mit ggfls einigen Änderungen) ist ein valider Anwendungsfall und fällt nicht in das Thema Reloadsperre.

                          LG,
                          CK

                          1. problematische Seite

                            Hallo Christian Kruse,

                            Nein, du schriebst von einer Reload-Sperre. History back und das formular händisch erneut absenden (mit ggfls einigen Änderungen) ist ein valider Anwendungsfall und fällt nicht in das Thema Reloadsperre.

                            Ok. Also ist auch der Abschnitt Weiterleitung prinzipiell richtig?

                            Übrigens wird dann auch mit Status 200 das Formular nicht automatisch erneut abgesendet.

                            Bis demnächst
                            Matthias

                            --
                            Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                            1. problematische Seite

                              Hallo Matthias,

                              Ok. Also ist auch der Abschnitt Weiterleitung prinzipiell richtig?

                              Nein. Die Meldung aus deinen Screenshots gibt es nicht bei einem 302.

                              Übrigens wird dann auch mit Status 200 das Formular nicht automatisch erneut abgesendet.

                              Doch. Bei einem Status 200 auf einen POST wird bei einem Reload der POST-Request wiederholt – und in diesem Fall wird auch eine entsprechende Meldung bei einem History Back gezeigt.

                              Ich versuche das nochmal zu skizzieren, mir scheint das Verhalten ist noch nicht klar.

                              • POST
                                • 200
                                  • User klickt auf Link -> History Back, die Meldung erscheint
                                  • Reload, ein Popup mit Warnung erscheint
                                  • History Back, keine Meldung erscheint (Vorfahre ist ein GET-Request auf das Formular)
                                • 302
                                  • User klickt auf Link -> History Back (egal wie oft), keine Meldung erscheint
                                  • Reload, keine Meldung erscheint
                                  • History Back, keine Meldung erscheint

                              LG,
                              CK

                              1. problematische Seite

                                Hallo Christian Kruse,

                                Ich versuche das nochmal zu skizzieren, mir scheint das Verhalten ist noch nicht klar.

                                Mir scheint, die (mein) Browser verhalten sich anders als sie sollten, zumindest anders als du es beschreibst. Schade, dass wir in Erfurt nicht darüber sprechen können.

                                Vielleicht fehlen mir auch wieder die korrekten Fachtermini.

                                Bis demnächst
                                Matthias

                                --
                                Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                                1. problematische Seite

                                  Hallo Matthias,

                                  Ich versuche das nochmal zu skizzieren, mir scheint das Verhalten ist noch nicht klar.

                                  Mir scheint, die (mein) Browser verhalten sich anders als sie sollten, zumindest anders als du es beschreibst.

                                  Das bezweifle ich ;-) Aber mach doch mal ein Video oder eine Screenshot-Serie, dann kann man nachvollziehen was du meinst.

                                  Schade, dass wir in Erfurt nicht darüber sprechen können.

                                  Finde ich auch.

                                  LG,
                                  CK

                                  1. problematische Seite

                                    Hallo Christian Kruse,

                                    Das bezweifle ich ;-) Aber mach doch mal ein Video oder eine Screenshot-Serie, dann kann man nachvollziehen was du meinst.

                                    Ok. Fehler im Kommunikationsprotokoll gefunden ;-)

                                    Das, was du beschreibst trifft für die Seite zu, auf die man gelangt, nachdem das Formular abgesendet wurde. Was allerdings immer möglich ist: Ich kann durch Verwenden des Zurückbuttons wieder zu der Seite mit dem schon ausgefüllten Formular gelangen und es von dort erneut absenden.

                                    Bis demnächst
                                    Matthias

                                    --
                                    Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                                    1. problematische Seite

                                      Hi,

                                      Das, was du beschreibst trifft für die Seite zu, auf die man gelangt, nachdem das Formular abgesendet wurde.

                                      genau, denn dort ist man per Redirect hingekommen, und diese Seite wurde dann vom Browser mit GET angefordert. Die kann man auch beliebig oft neu laden, bis man keine Lust mehr hat. ;-)

                                      Was allerdings immer möglich ist: Ich kann durch Verwenden des Zurückbuttons wieder zu der Seite mit dem schon ausgefüllten Formular gelangen und es von dort erneut absenden.

                                      Das wiederum kann klappen, muss aber nicht. Ob beim Zurück-Navigieren die Formularinhalte noch da sind oder nicht, ist von den Browser-Einstellungen abhängig. Und davon, was in der Zwischenzeit so alles im Browser-Cache passiert ist.

                                      Ciao,
                                       Martin

                                      --
                                      Nothing travels faster than the speed of light with the possible exception of bad news, which obeys its own special laws.
                                      - Douglas Adams, The Hitchhiker's Guide To The Galaxy
                                      1. problematische Seite

                                        Hallo

                                        Was allerdings immer möglich ist: Ich kann durch Verwenden des Zurückbuttons wieder zu der Seite mit dem schon ausgefüllten Formular gelangen und es von dort erneut absenden.

                                        Das wiederum kann klappen, muss aber nicht. Ob beim Zurück-Navigieren die Formularinhalte noch da sind oder nicht, ist von den Browser-Einstellungen abhängig.

                                        … und im Falle des Falles, wie in dem, den Christian beschreibt, so gewollt. Das erneute absenden des Formularinhalts, so noch vorhanden, bedarf dann ja einer bewussten Aktion und geschieht nicht „aus Versehen“.

                                        Tschö, Auge

                                        --
                                        Wo wir Mängel selbst aufdecken, kann sich kein Gegner einnisten.
                                        Wolfgang Schneidewind *prust*
                                        1. problematische Seite

                                          Hallo Auge,

                                          Das erneute absenden des Formularinhalts, so noch vorhanden, bedarf dann ja einer bewussten Aktion und geschieht nicht „aus Versehen“.

                                          Exakt, danke für die bessere Formulierung.

                                          LG,
                                          CK

                                    2. problematische Seite

                                      Hallo Matthias,

                                      Das, was du beschreibst trifft für die Seite zu, auf die man gelangt, nachdem das Formular abgesendet wurde. Was allerdings immer möglich ist: Ich kann durch Verwenden des Zurückbuttons wieder zu der Seite mit dem schon ausgefüllten Formular gelangen und es von dort erneut absenden.

                                      Ja, wie ich bereits sagte: das ist ein valider Anwendungsfall und nicht das Thema einer Reload-Sperre. Das ist als wenn der User erneut zum Formular navigiert und dort alles nochmal einträgt und nochmal absendet. Wenn man doppelte Einsendungen vermeiden will, dann hilft keine der benannten Methoden, sondern nur in der Datenbank nachschauen ob ein Datensatz mit identischen Werten bereits existiert.

                                      LG,
                                      CK, prokrastinierend ;-)

        2. problematische Seite

          wenn man History Back klickt, gelangt man nicht zum POST-Request, sondern zum vorhergehenden GET-Request.

          Hm. Die Screenshots hab ich mir ja nicht ausgedacht. Und wenn ich mich recht erinnere, gelingt es tatsächlich, das Formular erneut abzusenden.

          Dinge ändern sich. Früher war das so - heute nicht mehr.

          Hmm... Wie lange soll das denn her sein? Ich meine, dass vor ca. 10 Jahren schon mal so implementiert zu haben - ohne Probleme.

          1. problematische Seite

            Hallo Mitleser,

            Hmm... Wie lange soll das denn her sein? Ich meine, dass vor ca. 10 Jahren schon mal so implementiert zu haben - ohne Probleme.

            Sehr lange - Netscape 3? Ich glaube, das könnte hinkommen, aber ich bin mir nicht sicher.

            LG,
            CK

    2. problematische Seite

      Hallo Christian Kruse,

      Die beiden anderen Methoden funktionieren weiterhin,

      Kannst du bitte noch mal über den Teil 1 schauen?

      Bis demnächst
      Matthias

      --
      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
      1. problematische Seite

        Hallo Matthias,

        Die beiden anderen Methoden funktionieren weiterhin,

        Kannst du bitte noch mal über den Teil 1 schauen?

        Autowert… urghs. ;-)

        $id_check -> bindParam(':BName', $_POST['bname']);
        $id_check -> execute();
        

        Ich würde hier lieber die Parameter an execute() übergeben, das ist einfacher lesbar:

        $id_check -> execute(array(':BName' => $_POST['bname']));
        

        Das gleiche weiter unten. Weiter:

        if(isset($benutzer['ID'])) array_push($fehler,'Dieser Benutzer existiert schon.');
        

        Diese Notation ist gefährlich, siehe goto fail - ich würde immer Klammern nutzen. In dem INSERT hast du einen Quoting-Fehler: "Ich", es müsste aber \"Ich\" sein (SQL embedded in PHP).

        Bzgl des Inhalts: ich würde mir die Bemerkung über natürliche Primärschlüssel sparen: es macht den Satz schlechter lesbar, tut hier nichts zur Sache und der Wahrheitsgehalt ist mindestens strittig (um natürliche vs künstliche Primärschlüssel existiert seit Ewigkeiten eine leidenschaftliche Diskussion). Bei den Error-Codes würde ich explizit MySQL erwähnen, vllt im Stile von „so lauten der Error-Code und die entsprechende Fehlermeldung bei MySQL“ oder so.

        Ansonsten sieht das gut aus.

        LG,
        CK

        1. problematische Seite

          Hallo Christian Kruse,

          Autowert… urghs. ;-)

          Ich bin dabei ;-)

          $id_check -> bindParam(':BName', $_POST['bname']);
          $id_check -> execute();
          

          Ich würde hier lieber die Parameter an execute() übergeben, das ist einfacher lesbar:

          $id_check -> execute(array(':BName' => $_POST['bname']));
          

          Ist damit auch der Kontextwechsel abgesichert? bindParam sorgt ja auch dafür, dass eben nicht mehr mysql_real_escape_string() o. ä. verwendet werden muss oder sehe ich das falsch und das macht schon das prepare?

          Ansonsten sieht das gut aus.

          :-)

          Bis demnächst
          Matthias

          --
          Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
          1. problematische Seite

            Hallo Matthias,

            Ich würde hier lieber die Parameter an execute() übergeben, das ist einfacher lesbar:

            $id_check -> execute(array(':BName' => $_POST['bname']));
            

            Ist damit auch der Kontextwechsel abgesichert?

            Ja. Das ist ein Shortcut für bindValue.

            bindParam sorgt ja auch dafür, dass eben nicht mehr mysql_real_escape_string() o. ä. verwendet werden muss oder sehe ich das falsch und das macht schon das prepare?

            Das macht das Konzept der prepared statements mit placeholders, ja. Wobei bei PostgreSQL das gleiche Konzept auf für normale Statements existiert (Stichwort EXECUTE). Das bindParam ist nur ein Konzept, um prepared statements in Loops zu optimieren. Die Variablen werden via Referenz an den Parameter gebunden und können vor dem execute nochmal geändert werden. Das erlaubt es, das bindParam vor dem Loop einmal auszuführen und im Loop nur noch den Wert der Variablen zu ändern:

            create table foo (bar int);
            
            $db = new PDO("pgsql:host=localhost;dbname=test");
            
            $bar = 0;
            
            $stmt = $db->prepare("INSERT INTO foo (bar) VALUES (:bar)");
            $stmt->bindParam(':bar', $bar);
            
            for($bar = 0; $bar < 10; $bar++) {
              $stmt->execute();
            }
            
            test=# select * from foo;
             bar 
            -----
               0
               1
               2
               3
               4
               5
               6
               7
               8
               9
            (10 rows)
            

            LG,
            CK

            1. problematische Seite

              Hallo Christian Kruse,

              Ist damit auch der Kontextwechsel abgesichert?

              Ja. Das ist ein Shortcut für bindValue.

              Danke für die ausführliche Erläuterung.

              [bindParam: ] Die Variablen werden via Referenz an den Parameter gebunden.

              Deshalb kann man in Schleifen eben nicht bindValue verwenden, weil da der zum Zeitpunkt des Aufrufs aktuelle Variablenwert an den Parameter gebunden wird - richtig?

              Falls du magst, könntest du noch mal über die Endfassung schauen.

              Bis demnächst
              Matthias

              --
              Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
              1. problematische Seite

                Hallo Matthias,

                bindParam: Die Variablen werden via Referenz an den Parameter gebunden.

                Deshalb kann man in Schleifen eben nicht bindValue verwenden, weil da der zum Zeitpunkt des Aufrufs aktuelle Variablenwert an den Parameter gebunden wird - richtig?

                Doch, können sie: aber halt bei jedem Schleifenaufruf erneut :-) Weil, wie du korrekt erkannst hast, bindValue den Wert der Expression an den Parameter bindet.

                Falls du magst, könntest du noch mal über die Endfassung schauen.

                Sieht gut aus, mir gefallen die Änderungen. Jetzt noch zum dritten Teil: üblich ist hier nicht, das Token in der Session zu speichern, sondern in einer Spalte bei dem Datum mit einem UNIQUE-Constraint. Sonst muss man das Token bei jedem Formular-Aufruf neu generieren und in der Session ändern; und wenn man das macht, ist man auch nicht mehr wirklich gegen einen Reload geschützt: ich rufe das Formular erneut auf, das Token ändert sich, ich lade die Seite mit dem POST-Request neu, das Token unterscheidet sich - zack, doppelter Datensatz.

                Speichert man es bei dem Datum, hat man dieses Problem nicht.

                LG,
                CK

                1. problematische Seite

                  Hallo Christian Kruse,

                  Jetzt noch zum dritten Teil: üblich ist hier nicht, das Token in der Session zu speichern, sondern in einer Spalte bei dem Datum mit einem UNIQUE-Constraint.

                  Ok. Verstehe.

                  Sonst muss man das Token bei jedem Formular-Aufruf neu generieren und in der Session ändern; und wenn man das macht, ist man auch nicht mehr wirklich gegen einen Reload geschützt: ich rufe das Formular erneut auf, das Token ändert sich, ich lade die Seite mit dem POST-Request neu, das Token unterscheidet sich - zack, doppelter Datensatz.

                  Aber das muss man ja auch schon absichtlich machen. Zum Beispiel in zwei Browsertabs das Formular öffnen, es nur in einem abschicken und dann in beiden Tabs F5 drücken.

                  Bis demnächst
                  Matthias

                  --
                  Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                  1. problematische Seite

                    Hallo Matthias,

                    Sonst muss man das Token bei jedem Formular-Aufruf neu generieren und in der Session ändern; und wenn man das macht, ist man auch nicht mehr wirklich gegen einen Reload geschützt: ich rufe das Formular erneut auf, das Token ändert sich, ich lade die Seite mit dem POST-Request neu, das Token unterscheidet sich - zack, doppelter Datensatz.

                    Aber das muss man ja auch schon absichtlich machen. Zum Beispiel in zwei Browsertabs das Formular öffnen, es nur in einem abschicken und dann in beiden Tabs F5 drücken.

                    Vielleicht, aber nicht mit dem Wunsch den Reload zu umgehen. Meinem Chef passiert das öfter, einfach weil er gerne schonmal den Überblick über seine Browser-Tabs verliert. Und durch mangelnde Geduld wird dann halt einfach CMD+r gedrückt. Bei uns ist das allerdings idR kein Problem, dem Redirect sei dank ;-)

                    LG,
                    CK

                2. problematische Seite

                  Hallo Christian Kruse,

                  Sieht gut aus, mir gefallen die Änderungen. Jetzt noch zum dritten Teil: üblich ist hier nicht, das Token in der Session zu speichern, sondern in einer Spalte bei dem Datum mit einem UNIQUE-Constraint. Sonst muss man das Token bei jedem Formular-Aufruf neu generieren und in der Session ändern; und wenn man das macht, ist man auch nicht mehr wirklich gegen einen Reload geschützt: ich rufe das Formular erneut auf, das Token ändert sich, ich lade die Seite mit dem POST-Request neu, das Token unterscheidet sich - zack, doppelter Datensatz.

                  Ich bin (immer noch nicht sicher | jetzt wieder unsicher), ob das so stimmt:

                  • Formular wird im 1. Tab geladen -> $_POST-token = foo
                  • Formular wird im 2. Tab geladen -> $_POST-token = bar
                  • Formular wird im 2. Tab abgeschickt -> $_SESSION-token = bar
                  • 1. Tab wird refreshed -> $_POST-token = quz
                  • 2. Tab wird refreshed -> $_POST-token = $_SESSION-token = bar
                    • => keine erneute Eintragung

                  Bis demnächst
                  Matthias

                  --
                  Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                  1. problematische Seite

                    Hallo Matthias,

                    • Formular wird im 1. Tab geladen -> $_POST-token = foo
                    • Formular wird im 2. Tab geladen -> $_POST-token = bar
                    • Formular wird im 2. Tab abgeschickt -> $_SESSION-token = bar
                    • 1. Tab wird refreshed -> $_POST-token = quz
                    • 2. Tab wird refreshed -> $_POST-token = $_SESSION-token = bar
                      • => keine erneute Eintragung

                    Das ist nicht das Szenario, das ich skizziert habe ;)

                    • Formular wird im 1. Tab geladen, ausgefüllt und abgeschickt. Session-Token ist jetzt das aus dem 1. Tab.
                    • Formular wird im 2. Tab geladen, ausgefüllt und abgeschickt. Session-Token ist jetzt das aus dem 2. Tab.
                    • 1. Tab wird refreshed. Doppelter Eintrag, weil das in den FormData geschickte Token sich von dem Token in der Session unterscheidet.

                    LG,
                    CK

                    1. problematische Seite

                      Hallo Christian Kruse,

                      Das ist nicht das Szenario, das ich skizziert habe ;)

                      Ok. Aus deiner Skizze konnte ich aber das Szenario nicht entnehmen ;-)

                      Aber das leuchtet ein.

                      Bis demnächst
                      Matthias

                      --
                      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                3. problematische Seite

                  Hallo Christian Kruse,

                  Falls du magst, könntest du noch mal über die Endfassung schauen.

                  Jetzt noch zum dritten Teil: üblich ist hier nicht, das Token in der Session zu speichern, sondern in einer Spalte bei dem Datum mit einem UNIQUE-Constraint.

                  Ich habe noch folgendes ergänzt:

                  „Allerdings könnte es unter Umständen, etwa wenn mehrere Tabs oder Browserfenster im Spiel sind, immer noch passieren, dass die Daten versehentlich erneut abgesendet werden.

                  Um dies zu umgehen, können Sie statt in der Session das Token auch als eindeutigen Wert in die Datenbank schreiben. Die Datenbankoperationen würden dann wie im ersten Abschnitt umgesetzt werden.“

                  Bis demnächst
                  Matthias

                  --
                  Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                  1. problematische Seite

                    Hallo Matthias,

                    „Allerdings könnte es unter Umständen, etwa wenn mehrere Tabs oder Browserfenster im Spiel sind, immer noch passieren, dass die Daten versehentlich erneut abgesendet werden.

                    Um dies zu umgehen, können Sie statt in der Session das Token auch als eindeutigen Wert in die Datenbank schreiben. Die Datenbankoperationen würden dann wie im ersten Abschnitt umgesetzt werden.“

                    Wäre es nicht sinnvoller, den Absatz zu streichen und den ersten Absatz umzuschreiben, so dass man nur die Variante mit dem UNIQUE-Constraint hat? Die Session-Variante hab ich in freier Wildbahn so noch nicht gesehen, und mir erscheint sie auch nicht unbedingt einen Vorteil zu bieten, eher im Gegenteil.

                    Just my 2 cents.

                    LG,
                    CK

                    1. problematische Seite

                      Hallo Christian Kruse,

                      Die Session-Variante hab ich in freier Wildbahn so noch nicht gesehen,

                      Ich werde doch nicht etwa der Erfinder dieser Variante sein?

                      und mir erscheint sie auch nicht unbedingt einen Vorteil zu bieten, eher im Gegenteil.

                      das spräche dafür.

                      Wäre es nicht sinnvoller, den […] ersten Absatz umzuschreiben, so dass man nur die Variante mit dem UNIQUE-Constraint hat?

                      Also auch auf die Erläuterungen mit den Primärschlüsseln verzichten? Denn das Token als UNIQUE-Constraint-Spalte in die DB einzutragen entspricht ja eigentlich dem, was unter natürliche Primärschlüssel beschrieben ist.

                      Bis demnächst
                      Matthias

                      --
                      Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                      1. problematische Seite

                        Hallo Matthias,

                        Die Session-Variante hab ich in freier Wildbahn so noch nicht gesehen,

                        Ich werde doch nicht etwa der Erfinder dieser Variante sein?

                        Das kann ich nicht beurteilen :-)

                        Wäre es nicht sinnvoller, den […] ersten Absatz umzuschreiben, so dass man nur die Variante mit dem UNIQUE-Constraint hat?

                        Also auch auf die Erläuterungen mit den Primärschlüsseln verzichten?

                        Nicht aus dem Kontext reissen ;-) ich bezog mich vollständig auf den dritten Teil des Artikels. Ich würde Teil 1 und Teil 2 lassen und in Teil 3 ein künstliches UNIQUE-Constraint vorstellen, denn das unterscheidet sich ja schon deutlich von einem Primärschlüssel.

                        Denn das Token als UNIQUE-Constraint-Spalte in die DB einzutragen entspricht ja eigentlich dem, was unter natürliche Primärschlüssel beschrieben ist.

                        Der Grundgedanke ist der Gleiche, ja - ist er aber ja auch bei der Variante mit den Sessions. Ich betrachte ein eindeutiges Kriterium um zu prüfen, ob ich die Daten schonmal hatte.

                        LG,
                        CK

                        1. problematische Seite

                          Hallo Christian Kruse,

                          Nicht aus dem Kontext reissen ;-) ich bezog mich vollständig auf den dritten Teil des Artikels.

                          Ah. OK. Das hatte ich nicht so verstanden.

                          Bis demnächst
                          Matthias

                          --
                          Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                        2. problematische Seite

                          Hallo Christian Kruse,

                          Ich würde Teil 1 und Teil 2 lassen und in Teil 3 ein künstliches UNIQUE-Constraint vorstellen, denn das unterscheidet sich ja schon deutlich von einem Primärschlüssel.

                          Naja, so groß sind die Unterschiede ja nun wirklich nicht. Beide fordern die Eindeutigkeit der entsprechenden Spalten, bei UNIQUE ist NULL (für jede beteiligte Spalte) genau einmal erlaubt, bei PRIMARY KEY nicht. Damit ist eine UNIQUE-Spalte doch auch nur ein Zweit- oder Drittschlüssel.

                          Denn das Token als UNIQUE-Constraint-Spalte in die DB einzutragen entspricht ja eigentlich dem, was unter natürliche Primärschlüssel beschrieben ist.

                          Der Grundgedanke ist der Gleiche, ja - ist er aber ja auch bei der Variante mit den Sessions. Ich betrachte ein eindeutiges Kriterium um zu prüfen, ob ich die Daten schonmal hatte.

                          Hebt man denn die Tokens bis in alle Ewigkeit auf? Denn es könnte ja nun passieren, dass identische Tokens generiert werden. (Wenn die Wahrscheinlichkeit doch auch sehr klein ist) Speichert man da zusätzlich noch einen Zeitstempel mit und setzt man Token und Zeitstempel zusammen auf UNIQUE Oder macht man sich um diesen praktisch nur theoretisch (sic) auftretenden Fall keine Gedanken?

                          Bis demnächst
                          Matthias

                          --
                          Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                          1. problematische Seite

                            Hallo Matthias,

                            Ich würde Teil 1 und Teil 2 lassen und in Teil 3 ein künstliches UNIQUE-Constraint vorstellen, denn das unterscheidet sich ja schon deutlich von einem Primärschlüssel.

                            Naja, so groß sind die Unterschiede ja nun wirklich nicht. Beide fordern die Eindeutigkeit der entsprechenden Spalten, bei UNIQUE ist NULL (für jede beteiligte Spalte) genau einmal erlaubt, bei PRIMARY KEY nicht. Damit ist eine UNIQUE-Spalte doch auch nur ein Zweit- oder Drittschlüssel.

                            Was?? Nein, NULL != NULL ist wahr, du kannst soviele NULL-Werte einfügen wie du willst:

                            MariaDB [test]> CREATE TABLE foo(bar INT UNIQUE);
                            Query OK, 0 rows affected (0.05 sec)
                            
                            MariaDB [test]> INSERT INTO foo (bar) VALUES (NULL);
                            Query OK, 1 row affected (0.01 sec)
                            
                            MariaDB [test]> INSERT INTO foo (bar) VALUES (NULL);
                            Query OK, 1 row affected (0.01 sec)
                            
                            MariaDB [test]> INSERT INTO foo (bar) VALUES (NULL);
                            Query OK, 1 row affected (0.01 sec)
                            
                            MariaDB [test]> INSERT INTO foo (bar) VALUES (NULL);
                            Query OK, 1 row affected (0.00 sec)
                            
                            MariaDB [test]> SELECT * FROM foo;
                            +------+
                            | bar  |
                            +------+
                            | NULL |
                            | NULL |
                            | NULL |
                            | NULL |
                            +------+
                            4 rows in set (0.00 sec)
                            
                            MariaDB [test]> 
                            

                            Das gilt für MySQL und PostgreSQL auch, nur MS SQL ist da kaputt und hält sich nicht an den Standard.

                            Mir ging es aber auch um den logischen Unterschied, nicht um den technischen.

                            Denn das Token als UNIQUE-Constraint-Spalte in die DB einzutragen entspricht ja eigentlich dem, was unter natürliche Primärschlüssel beschrieben ist.

                            Der Grundgedanke ist der Gleiche, ja - ist er aber ja auch bei der Variante mit den Sessions. Ich betrachte ein eindeutiges Kriterium um zu prüfen, ob ich die Daten schonmal hatte.

                            Hebt man denn die Tokens bis in alle Ewigkeit auf?

                            Ja.

                            Denn es könnte ja nun passieren, dass identische Tokens generiert werden. (Wenn die Wahrscheinlichkeit doch auch sehr klein ist) Speichert man da zusätzlich noch einen Zeitstempel mit und setzt man Token und Zeitstempel zusammen auf UNIQUE Oder macht man sich um diesen praktisch nur theoretisch (sic) auftretenden Fall keine Gedanken?

                            Man nutzt eine Funktion, die einem mehr oder weniger garantiert, dass die ID nur einmal auftreten kann. In anderen Sprachen wäre das eine UUID oder eine GUID, in PHP muss man dafür auf uniqueid() zurückgreifen; da die Funktion aber eine Funktion der Zeit ist, sollte das hier auch gegeben sein. Kollisionen werden damit so unwahrscheinlich, dass man sie ignoriert. Wenn man es aber richtig[tm] machen will, setzt man die Spalte regelmäßig auf NULL oder so, ja.

                            LG,
                            CK

                            1. problematische Seite

                              Hallo Christian Kruse,

                              Was?? Nein, NULL != NULL ist wahr, du kannst soviele NULL-Werte einfügen wie du willst:

                              Das gilt für MySQL und PostgreSQL auch, nur MS SQL ist da kaputt und hält sich nicht an den Standard.

                              Aha. Auch das war mir nicht bewusst.

                              Mir ging es aber auch um den logischen Unterschied, nicht um den technischen.

                              Der da wäre? Dass über die Primärschlüssel die referentielle Integrität abgesichert wird, ist ja auch eher technisch,

                              Oder macht man sich um diesen praktisch nur theoretisch (sic) auftretenden Fall keine Gedanken?

                              Kollisionen werden damit so unwahrscheinlich, dass man sie ignoriert. Wenn man es aber richtig[tm] machen will, setzt man die Spalte regelmäßig auf NULL oder so, ja.

                              Das fällt dann aber unter Datenbankhygiene, oder?

                              Bis demnächst
                              Matthias

                              --
                              Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                              1. problematische Seite

                                Hallo Matthias,

                                Was?? Nein, NULL != NULL ist wahr, du kannst soviele NULL-Werte einfügen wie du willst:

                                Das gilt für MySQL und PostgreSQL auch, nur MS SQL ist da kaputt und hält sich nicht an den Standard.

                                Aha. Auch das war mir nicht bewusst.

                                Es ist auch nicht besonders logisch, finde ich… ;-)

                                Mir ging es aber auch um den logischen Unterschied, nicht um den technischen.

                                Der da wäre? Dass über die Primärschlüssel die referentielle Integrität abgesichert wird, ist ja auch eher technisch,

                                Der Primärschlüssel identifiziert einen Datensatz eindeutig, die Aufgabe der Unique-ID ist es den Request eindeutig zu identifizieren.

                                Edit: was ich damit meine: der PK ist somit oft erst nach dem INSERT bekannt, die UID zwangsläufig immer vorher.

                                Oder macht man sich um diesen praktisch nur theoretisch (sic) auftretenden Fall keine Gedanken?

                                Kollisionen werden damit so unwahrscheinlich, dass man sie ignoriert. Wenn man es aber richtig[tm] machen will, setzt man die Spalte regelmäßig auf NULL oder so, ja.

                                Das fällt dann aber unter Datenbankhygiene, oder?

                                IMHO ja.

                                LG,
                                CK

                                1. problematische Seite

                                  Hallo Christian Kruse,

                                  Edit: was ich damit meine: der PK ist somit oft erst nach dem INSERT bekannt, die UID zwangsläufig immer vorher.

                                  Ja, das ist einleuchtend.

                                  Ich habe den 3. Abschnitt deutlich gekürzt und den Artikel in den Hauptnamensraum verschoben. Hast du noch Anmerkungen?

                                  Danke noch mal für deine Geduld.

                                  Bis demnächst
                                  Matthias

                                  --
                                  Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
                                  1. problematische Seite

                                    Hallo Matthias,

                                    Ich habe den 3. Abschnitt deutlich gekürzt und den Artikel in den Hauptnamensraum verschoben. Hast du noch Anmerkungen?

                                    Sieht gut aus, IMHO.

                                    Danke noch mal für deine Geduld.

                                    Äh… gerne? ;)

                                    LG,
                                    CK

  2. problematische Seite

    Der Page-Token ist bei Doppelklicks wirkungslos, weil er immer noch derselbe ist. Eine Checksum der gesendeten Daten in die Session schreiben ist der richtige Weg. Idealerweise mit dem URL der Seite als Schlüssel (ein Page-Token kann manipuliert werden, der url nicht).

    Und wenns dann doch in MySQL ankommt -- MySQL kann auch Checksummen bilden.

    Und dann wäre noch die Möglichkeit, eine Checksumme mit JS zu erzeugen und mitzuzählen, wie oft Daten mit dieser Checksum submittet wurden.

    MfG