borisbaer: Regeln zur PHP/JS-Validierung in JSON-Datei speichern

Hallo zusammen,

die Fehlermeldungen bei einer PHP/JS-Validierung werden ja häufig automatisch generiert (z.B. „xyz required“). Im Deutschen funktioniert das nicht ganz so gut wegen der Artikel bzw. der Flexion von Wörtern (z.B. diese E-Mail-Adresse, aber dieser Benutzername). Jedenfalls habe ich mich dazu entschieden, auch um mehr Freiheit zu haben, für jeden Fehler je nach Namen des Eingabefeldes eine spezielle Fehler-Formulierung zu definieren.

Ganz einfaches Beispiel:

if ( $rule === 'unique' ) {
	if ( $name === 'username' )
		return 'Dieser Benutzername ist bereits vergeben.';
	if ( $name === 'email' )
		return 'Diese E-Mail-Adresse ist bereits vergeben.';
}

An sich funktioniert das gut für mich, doch da ich zusätzlich auch JS-Validierung verwende, empfand ich es als lästig, jede Änderung an zwei Stellen durchführen zu müssen. Insofern kam mir die Idee, einfach alle Formulierungen in einer JSON-Datei zu speichern, auf die dann sowohl die PHP- als auch die JS-Validierung zugreift, in etwa so:

{
	"minlength":
	[
		{
			"username": "Der Benutzername darf nicht weniger als %s Zeichen lang sein."
		},
		{
			"password": "Das Passwort darf nicht weniger als %s Zeichen lang sein."
		}
	]
}

Meine Frage ist, ob etwas gegen ein solches Vorgehen spricht (z.B. aus Gründen der Sicherheit).

Grüße
Boris

  1. Insofern kam mir die Idee, einfach alle Formulierungen in einer JSON-Datei zu speichern, auf die dann sowohl die PHP- als auch die JS-Validierung zugreift, in etwa so:

    {
    	"minlength":
    	[
    		{
    			"username": "Der Benutzername darf nicht weniger als %s Zeichen lang sein."
    		},
    		{
    			"password": "Das Passwort darf nicht weniger als %s Zeichen lang sein."
    		}
    	]
    }
    

    Meine Frage ist, ob etwas gegen ein solches Vorgehen spricht (z.B. aus Gründen der Sicherheit).

    Sone Nachrichten vom Code zu trennen ist generell eine ganz gute Idee ;-) Es gibt große Frameworks, die Dir solche Fragen weitgehend abnehmen, aber da scheinst Du nicht unterwegs zu sein. Also: Ja, kannst Du so machen. Sicherheitsprobleme fallen mir da keine ein, außer jemand schmuggelt was in dein JSON rein.

    1. n'Abend,

      {
      	"minlength":
      	[
      		{
      			"username": "Der Benutzername darf nicht weniger als %s Zeichen lang sein."
      		},
      		{
      			"password": "Das Passwort darf nicht weniger als %s Zeichen lang sein."
      		}
      	]
      }
      

      die geforderte Mindestanzahl an Zeichen ist ein Zahlenwert, kein String. Insofern hätte ich hier eher %i oder %d als Platzhalter erwartet, nicht %s. Andererseits ist das mit den Datentypen ja sowohl in Javascript, als auch in PHP eine so unscharfe Geschichte, dass das möglicherweise sogar funktioniert.

      Sicherheitsprobleme fallen mir da keine ein, außer jemand schmuggelt was in dein JSON rein.

      Das kann eigentlich nur clientseitig passieren. Und auf eine clientseitige Validierung sollte man sich sowieso nie verlassen. Die kann ein Komfortfeature sein, ist aber immer manipulierbar.
      Und wenn jemand die JSON-Datei auf dem Server manipulieren kann, dann hat Boris ganz andere Probleme als einfach bloß eine Validierung der Eingaben. 😉

      Einen schönen Tag noch
       Martin

      --
      Wie man sich bettet, so schallt es heraus.
      1. Hallo Martin,

        die geforderte Mindestanzahl an Zeichen ist ein Zahlenwert, kein String. Insofern hätte ich hier eher %i oder %d als Platzhalter erwartet, nicht %s. Andererseits ist das mit den Datentypen ja sowohl in Javascript, als auch in PHP eine so unscharfe Geschichte, dass das möglicherweise sogar funktioniert.

        stimmt, prinzipiell kann ich hier als Variable auch %i schreiben, das macht keinen Unterschied. Ich habe %s in Anlehnung an die sprintf-Funktion von PHP verwendet und weil die Zahl ja trotzdem Teil eines strings ist und auch selbst als string dargestellt wird.

        Sicherheitsprobleme fallen mir da keine ein, außer jemand schmuggelt was in dein JSON rein.

        Das kann eigentlich nur clientseitig passieren. Und auf eine clientseitige Validierung sollte man sich sowieso nie verlassen. Die kann ein Komfortfeature sein, ist aber immer manipulierbar.

        Eigentlich kann hier nichts passieren. Ist ja nur eine Ansammlung von strings. Ich hatte anfangs die Idee, die Parameter (also minlength-, maxlength-Werte usw.) ebenfalls in der JSON-Datei zu speichern. Jedoch habe ich aufgrund von Sicherheitsbedenken davon Abstand genommen.

        Diese müsste ich dann halt weiterhin an zwei Stellen ändern.

        Und wenn jemand die JSON-Datei auf dem Server manipulieren kann, dann hat Boris ganz andere Probleme als einfach bloß eine Validierung der Eingaben. 😉

        Ja, das stimmt wohl!

        Vielen Dank für die Rückmeldungen!

        Grüße
        Boris

        1. Hallo,

          die geforderte Mindestanzahl an Zeichen ist ein Zahlenwert, kein String. Insofern hätte ich hier eher %i oder %d als Platzhalter erwartet, nicht %s. Andererseits ist das mit den Datentypen ja sowohl in Javascript, als auch in PHP eine so unscharfe Geschichte, dass das möglicherweise sogar funktioniert.

          stimmt, prinzipiell kann ich hier als Variable auch %i schreiben, das macht keinen Unterschied. Ich habe %s in Anlehnung an die sprintf-Funktion von PHP verwendet und weil die Zahl ja trotzdem Teil eines strings ist und auch selbst als string dargestellt wird.

          jaja, aber die Philosopie, gerade auch bei sprinf, ist ja die, dass der Platzhalter den ursprünglichen Typ des Parameters angeben soll. Dass er hinterher Teil eines Strings wird, ist klar. Aber deswegen habe ich angedeutet, dass es PHP und Javascript mit den Datentypen eh nicht so eng sehen.

          Das ist in C ganz anders: Wenn da im Formatstring für sprintf() ein %s steht, dann interpretiert die Funktion den übergebenen Parameter auch mit aller Gewalt als Zeiger auf eine Zeichenkette. Wenn dann beispielsweise der Zahlenwert 8 übergeben wird, will sprintf() auf eine Zeichenkette ab Adresse 0x00000008 im Arbeitsspeicher der Anwendung zugreifen. Das ist dann eventuell ein nicht erlaubter Zugriff, und die Anwendung fliegt mit Memory Access Violation achtkantig raus.

          Das kann eigentlich nur clientseitig passieren. Und auf eine clientseitige Validierung sollte man sich sowieso nie verlassen. Die kann ein Komfortfeature sein, ist aber immer manipulierbar.

          Eigentlich kann hier nichts passieren. Ist ja nur eine Ansammlung von strings.

          Ja, aber sollte die JSON-Datei nicht urprünglich auch die Limits selbst enthalten? - Egal, ich wollte nur nochmal betonen, dass alles, was clientseitig abläuft, vom Nutzer manipulierbar und damit nicht vertrauenswürdig ist. Eine clientseitige Validierung von Eingaben ist nice to have, aber nach dem Absenden der Daten muss in jedem Fall noch eine serverseitige Validierung vor der Weiterverarbeitung erfolgen.

          Bedenke auch, dass böse Buben vielleicht gar nicht dein Formular oder deine Web-App verwenden, um Daten an dein Script zu senden. Sondern Tools wie wget oder curl, mit denen sie sorgfältig handgestrickte Requests bauen können, die deine aufgestellten Regeln ganz gezielt verletzen.

          Ich hatte anfangs die Idee, die Parameter (also minlength-, maxlength-Werte usw.) ebenfalls in der JSON-Datei zu speichern. Jedoch habe ich aufgrund von Sicherheitsbedenken davon Abstand genommen.

          Ah, okay. Das hatte ich so noch im Hinterkopf.

          Einen schönen Tag noch
           Martin

          --
          Wie man sich bettet, so schallt es heraus.
          1. Hallo Martin,

            jaja, aber die Philosopie, gerade auch bei sprinf, ist ja die, dass der Platzhalter den ursprünglichen Typ des Parameters angeben soll. Dass er hinterher Teil eines Strings wird, ist klar. Aber deswegen habe ich angedeutet, dass es PHP und Javascript mit den Datentypen eh nicht so eng sehen.

            Beim sprintf() von PHP gibt es ja gar kein %i. Könntest du mir sagen, welcher von den Specifiers einem „normalen“ Integer gleichkommt?

            Das ist in C ganz anders: Wenn da im Formatstring für sprintf() ein %s steht, dann interpretiert die Funktion den übergebenen Parameter auch mit aller Gewalt als Zeiger auf eine Zeichenkette. Wenn dann beispielsweise der Zahlenwert 8 übergeben wird, will sprintf() auf eine Zeichenkette ab Adresse 0x00000008 im Arbeitsspeicher der Anwendung zugreifen. Das ist dann eventuell ein nicht erlaubter Zugriff, und die Anwendung fliegt mit Memory Access Violation achtkantig raus.

            Nun, bin leider kein Fachmann. Aber interessant zu wissen ist es schon. Womöglich komme ich ja mal in andere Gefilde als Web-Programmierung. Neben meinem richtigen Beruf und zwei kleinen Kindern reicht die Zeit leider immer nur bedingt für das Hobby. Es gibt aber viel zu wissen.

            Ja, aber sollte die JSON-Datei nicht urprünglich auch die Limits selbst enthalten? - Egal, ich wollte nur nochmal betonen, dass alles, was clientseitig abläuft, vom Nutzer manipulierbar und damit nicht vertrauenswürdig ist. Eine clientseitige Validierung von Eingaben ist nice to have, aber nach dem Absenden der Daten muss in jedem Fall noch eine serverseitige Validierung vor der Weiterverarbeitung erfolgen.

            Wäre schön, vielleicht baue ich die Limits auch mal ein. Die ganze Validierungsgeschichte ist für meine Verhältnisse doch ein recht großes Konstrukt. Die JSON-Datei liegt halt im public-Ordner und der ist ja, wie der Name schon sagt, öffentlich. Deswegen meine Bedenken, da Limits zu speichern. Die serverseitige Validierung hatte ich als allererstes geschrieben. Sie greift ebenso wie die clientseitige Validierung auf diese JSON-Datei zu, in der sich die Formulierungen für die Fehlermeldungen befinden.

            Bedenke auch, dass böse Buben vielleicht gar nicht dein Formular oder deine Web-App verwenden, um Daten an dein Script zu senden. Sondern Tools wie wget oder curl, mit denen sie sorgfältig handgestrickte Requests bauen können, die deine aufgestellten Regeln ganz gezielt verletzen.

            Nun, kann ich mit PHP nicht überprüfen, ob der POST-Request vom Formular stammt oder von irgendwo anders? Es ist ein Abgrund mit den potenziellen Sicherheitslücken.

            Grüße
            Boris

            1. Hi,

              Beim sprintf() von PHP gibt es ja gar kein %i. Könntest du mir sagen, welcher von den Specifiers einem „normalen“ Integer gleichkommt?

              das wäre dann wohl %d für decimal. In C sind %d und %i (für integer) äquivalent, und ich meine, ich hätte aus lauter Gewohnheit auch in PHP schon %i verwendet. Kann sein, dass das angesichts der Verwandtschaft zu C auch stillschweigend unterstützt wird, auch wenn es nicht dokumentiert ist. Alternativ gibt es noch %u (unsigned).

              Bedenke auch, dass böse Buben vielleicht gar nicht dein Formular oder deine Web-App verwenden, um Daten an dein Script zu senden. Sondern Tools wie wget oder curl, mit denen sie sorgfältig handgestrickte Requests bauen können, die deine aufgestellten Regeln ganz gezielt verletzen.

              Nun, kann ich mit PHP nicht überprüfen, ob der POST-Request vom Formular stammt oder von irgendwo anders?

              Nein. Wenn der Angreifer gut ist, kann er die Umgebung überzeugend nachbilden. Du kannst nur überprüfen, ob die gelieferten Werte deinen Regeln und Vorstellungen entsprechen.

              Es ist ein Abgrund mit den potenziellen Sicherheitslücken.

              Wohl wahr ...

              Einen schönen Tag noch
               Martin

              --
              Wie man sich bettet, so schallt es heraus.
            2. Moin,

              Nun, kann ich mit PHP nicht überprüfen, ob der POST-Request vom Formular stammt oder von irgendwo anders? Es ist ein Abgrund mit den potenziellen Sicherheitslücken.

              Ganz verhindern kannst du manipulierte Formulare nicht, aber du kannst CSRF-Token einsetzen womit Formulare nicht mehr so einfach nachgebaut werden können da das gerade aktuelle Token mitgeschickt werden muss.

              Gruß
              Tobias

              1. Hallo Tobias,

                Ganz verhindern kannst du manipulierte Formulare nicht, aber du kannst CSRF-Token einsetzen womit Formulare nicht mehr so einfach nachgebaut werden können da das gerade aktuelle Token mitgeschickt werden muss.

                das steht auf meiner Liste, wird aber wohl noch ein Weilchen dauern. Ich wollte statt des CSRF-Tokens ein JWT erstellen lassen. Muss ich mich aber noch reinfuchsen. Ich hatte dazu sogar schon ein Thema hier im Forum erstellt.

            3. Wäre schön, vielleicht baue ich die Limits auch mal ein. … Die JSON-Datei liegt halt im public-Ordner und der ist ja, wie der Name schon sagt, öffentlich.

              Also wenn Du eine clientseitige Validierung durchführst gehen die Regeln dafür so, so oder so online. Es ist ergo egal, ob Du diese in der JSON-Datei oder sonstwo notierst.

              BTW: Dich hindert nichts daran, serverseitig vollständig parametrisierte JS-„Funktionen“ zu generieren, statt die JSON-Datei explizit zu laden, denn PHP kann beliebigen Text, also auch Javascript (sogar Binarys) erzeugen. Du musst halt nur den korrekten Content-Type senden. In den Fall würde ich das Ergebnis serverseitig cachen (anhand des Änderungsdatums der JSON-Datei) und auch gleich gepackt ausliefern.

              1. Also wenn Du eine clientseitige Validierung durchführst gehen die Regeln dafür so, so oder so online. Es ist ergo egal, ob Du diese in der JSON-Datei oder sonstwo notierst.

                Jedoch greift auch die PHP-Validierung auf diese JSON-Datei zu, momentan aber nur, um sich die Formulierungen für die Fehlermeldungen zu schnappen. Meine Sorge war, dass, sollten die Limits ebenfalls in der JSON stehen, diese manipulierbar seien und somit auch die PHP-Validierung. Für die clientseitige Validierung ist mir das egal.

                BTW: Dich hindert nichts daran, serverseitig vollständig parametrisierte JS-„Funktionen“ zu generieren, statt die JSON-Datei explizit zu laden, denn PHP kann beliebigen Text, also auch Javascript (sogar Binarys) erzeugen. Du musst halt nur den korrekten Content-Type senden. In den Fall würde ich das Ergebnis serverseitig cachen (anhand des Änderungsdatums der JSON-Datei) und auch gleich gepackt ausliefern.

                Das müsste ich mir bei Gelegenheit mal genauer ansehen. Klingt aber interessant.