Pit: System zu schnell

Hallo,

meine Funktion, die mehrfach hintereinander aufgerufen wird, erzeugt manchmal dieselbe UID.

function makeUID($id) {
$uid = uniqid();
$query_UID = "UPDATE table SET uid = '".$uid."' WHERE...";

Gibt es da etwas narrensicheres als uniqid()?

Pit

  1. Hallo

    Hallo,

    meine Funktion, die mehrfach hintereinander aufgerufen wird, erzeugt manchmal dieselbe UID.

    function makeUID($id) {
    $uid = uniqid();
    $query_UID = "UPDATE table SET uid = '".$uid."' WHERE...";
    

    Gibt es da etwas narrensicheres als uniqid()?

    Schau in die Manualseite zu uniqid. Dort wird sowohl die Warnung vor der Möglichkeit nicht eindeutiger Werte ausgesprochen, als auch weitere Funktionen für kryptografisch sichere Werte genannt, die allerdings, mit Ausnahme von openssl_random_pseudo_bytes, erst mit PHP7 verfügbar sind. Zumindest kann man sich auch auf diesen Seiten mit „Anmerkungen“ und „siehe auch“ weiterhangeln, um sich zu informieren.

    [edit]Oft enthalten auch die „User contributed notes“ wertvolle Informationen.[/edit]

    Tschö, Auge

    --
    Wenn man ausreichende Vorsichtsmaßnahmen trifft, muss man keine Vorsichtsmaßnahmen mehr treffen.
    Toller Dampf voraus von Terry Pratchett
    1. Hi auge, hi dedlfix,

      ich hab mir das schon alles durchgelesen, aber ich nutze php 5.6 und die Funktion eines Users, die ich versucht habe, endet leider in no cryptographically secure random function available.

      Habe ich sonst was überlesen?

      pit

      1. Tach!

        ich hab mir das schon alles durchgelesen, aber ich nutze php 5.6 und die Funktion eines Users, die ich versucht habe, endet leider in no cryptographically secure random function available.

        Habe ich sonst was überlesen?

        Der erste Versuch wäre, als zweiten Parameter ($more_entropy) ein true zu übergeben. Aber wie gesagt, vielleicht gibt es in deinem Fall einfachere Alternativen.

        dedlfix.

        1. Hi,

          Der erste Versuch wäre, als zweiten Parameter ($more_entropy) ein true zu übergeben.

          Habe ich versucht, bei ca. 10 Vwersuchen hats geklappt, aber wer sagt mir, dass das nun n immer so bleibt?

          Pit

  2. Tach!

    Gibt es da etwas narrensicheres als uniqid()?

    Bitte zuerst die Handbuchseiten der verwendeten Funktionen lesen. Bei uniqid() stehen einige Hinweise zu deinem Problem, zu Vorschlägen, es zu entschärfen und auch Alternativen.

    dedlfix.

    1. Tach!

      Vielleicht lässt sich dein Problem auch auf ganz andere Weise lösen. Wozu genau brauchst du diese ID? Reicht da nicht bereits ein auto_increment? Muss der Wert einfach nur eindeutig sein oder hast du andere Ansprüche?

      dedlfix.

      1. Hi dedlfix,

        Muss der Wert einfach nur eindeutig sein oder hast du andere Ansprüche?

        Er muß (meist) eindeutig für eine Gruppe von Einträgen haben. Es geht darum, dass es Serientermine gibt, deren edit ich unterbinden möchte, wenn einer der Termine bereits anderweitig editiert wurde. Hierzu erhält jeder Termin die "unique" ID und wenn diese sich beim Eintrag eines edits (anderweitig) bereits geändert hat, erhält der User einen Hinweis darauf, dass sein Edit nicht vorgenommen werden kann.

        pit

        1. Tach!

          Er muß (meist) eindeutig für eine Gruppe von Einträgen haben. Es geht darum, dass es Serientermine gibt, deren edit ich unterbinden möchte, wenn einer der Termine bereits anderweitig editiert wurde. Hierzu erhält jeder Termin die "unique" ID und wenn diese sich beim Eintrag eines edits (anderweitig) bereits geändert hat, erhält der User einen Hinweis darauf, dass sein Edit nicht vorgenommen werden kann.

          Wenn ich das richtig verstehe, suchst du eine Art Optimistic Concurrency. Da würde ich aber einfacherweise einen Timestamp nehmen. Schneller als eine Sekunde werden deine Anwender nicht sein, die Daten abzufragen und geändert wieder zu speichern. Optimistic Looking basiert darauf, dass beim Zurückschreiben der damals abgefragte Timestamp immer noch im upzudatenden Datensatz steht und nicht geändert wurde. Das wird in der WHERE-Klausel mit geprüft, und wenn dann 0 Datensätze geändert werden konnten, sind die Daten in der Zwischenzeit geändert worden. Das Update muss auch gleichzeitig den neuen Timestamp setzen.

          dedlfix.

          1. Wenn ich das richtig verstehe, suchst du eine Art Optimistic Concurrency. Da würde ich aber einfacherweise einen Timestamp nehmen.

            Darüber kann man streiten, übrigens auch, ob ein User einen edit innerhalb einer Sekunde schaffen kann (ich schaffe das beim testen locker, auch mit Inhalt). Aber der timestamp hat ansonsten ja ggü. der unique id keinen Vorteil. Im Prinzip ist auch der nicht ganz sichere un ique timestamp für mich kein Problem, weil selbst der per Schleife erstellte ggf. identische unique id wird für verschiedene Gruppen gleich erstellt und ist hiermit unterscheidbar.

            Pit

            1. Tach!

              Aber der timestamp hat ansonsten ja ggü. der unique id keinen Vorteil.

              Er wird vom DBMS vergeben, du brauchst keine Funktion dafür zu schreiben, nur die vorhandenen aufrufen.

              Außerdem kennt MySQL die Funktion UUID(), die einen Wert "globally unique in space and time" liefert.

              dedlfix.

            2. Hallo Pit,

              will man einen Timestamp für optimistisches Locking verwenden, nimmt man (MySQL) nicht TIMESTAMP, sondern TIMESTAMP(6) und definiert die Spalte mit DEFAULT NOW(6) ON UPDATE NOW(6). Dann sind die Timestamps auf Millionstelsekunden genau - fraglich ist allerdings, ob der MySQL Server auch einen Mikrosekundengenauen internen Timer hat. Der wird vom Betriebssystem kommen und ist damit eine Größe variabler Genauigkeit.

              Besser für optimistisches Locking ist aber eine Satzversionsnummer, die man beim Lesen dem Editierer übergibt und die man beim Schreiben hochzählt.

              Beim Schreiben gibt man im WHERE die fachlichen eindeutigen Suchbegriffe an und dazu eine Abfrage, ob die Versionsnummer gleich geblieben ist. Wenn nicht, wurde konkurrierend geändert. Die Versionsnummer muss gar nicht so groß werden, da reicht ein short Integer, den man mit version = MOD(version + 1, 32768) hochzählt. Es müsste schon während eines Edit-Intervalls 32K Updates auf den gleichen Satz geben, um das zu brechen, und wenn Du eine solche Edit-Frequenz ERWARTEST, dann nimmst Du halt den Timestamp des Satzes noch hinzu.

              Die Verwendung einer außerhalb des Servers generierten, zufälligen "unique id" ist für optimistisches Locking nicht sinnvoll. Zufallszahlen können immer kollidieren, insbesondere wenn die Server im Cluster laufen. Ein sekundengenauer Timestamp ist komplett Banane (wobei man den Parameter für TIMESTAMP und NOW im MySQL Handbuch durchaus überlesen kann), ein Mikrosekunden-Timestamp sollte funktionieren, setzt aber einen Server voraus, der das auch liefert. Mit der Satzversion bist Du auf der sicheren Seite.

              Rolf

              --
              sumpsi - posui - clusi
  3. Hallo Pit,

    Gibt es da etwas narrensicheres als uniqid()?

    wenn deine Spalte in der DB unique ist gibt's eine Fehlermeldung, die du nutzen kannst.

    Gruss
    Henry

    1. wenn deine Spalte in der DB unique ist gibt's eine Fehlermeldung, die du nutzen kannst.

      Hi Henry,

      guter Tip, bei mir aber leider nciht einsetzbar, da ich gruppenweise dieselbe uid nutzen möchte.

      Pit

      1. Hallo Pit,

        hmm… weiss nicht genau wie du das mit der Gruppenarbeit meinst, weil der Wert wird ja erst ausgeliefert bzw. anerkannt wenn er in der DB steht, aber wahrscheinlich wirst du das das anders meinen.

        Wie auch immer, hab da ein Uraltscript von mir, dass noch nie 2x den gleichen Wert geliefert hat. Vielleicht nutzt es dir, bzw. eine Variation davon.

        function f_pass($anz=7,$abc=1,$_123=1,$small=0,$selfstr=0)
        {
        $xabc = "abcdefghkpqrstwxyz";
        $xnumber = "2345689";
        
        if($selfstr){$x = $selfstr;}                        // $selfstr können eigene Zeichen sein
        elseif($abc && !$_123){$x = $xabc;}
        elseif($_123 && !$abc){$x = $_123;}
        elseif($abc && $_123){$x = $xabc.$xnumber;}
        
        if($small == 1){$x = strtoupper($x);}                // alles gross | default = alles klein
        elseif($small == 2){ $x .= strtoupper($x);}        // gemischt    | default = alles klein
        
        
        $x = str_repeat($x,$anz);        // string vermehren. falls $anz grösser + besserer Zufall
        
        srand ((double)microtime()*1000000);        // Zufall initieren
        $pass =  str_shuffle($x);                // String zerwürfeln
        
        $pass = substr($pass,0,$anz);
        return $pass;
        } // END OF FUNC.
        
        

        Gruss
        Henry

      2. guter Tip, bei mir aber leider nciht einsetzbar, da ich gruppenweise dieselbe uid nutzen möchte.

        Dann gehören die Gruppe und zugehörige ID in eine eigene Tabelle (auf die die jetzige Tabelle verweist) und schon kümmert sich die Datenbank wieder um die Datenintegrität.