Blog

Modul des Monats: Data::Validate::WithYAML

15.09.2020 // Renée Bäcker

Schon relativ früh in der Webentwicklung merkt man: Benutzereingaben sind böse! Man sollte ihnen einfach nicht vertrauen. Es muss ja nicht immer gleich eine Sicherheitslücke entstehen, es reicht schon, wenn Nutzer:innen einfach falsche Daten eingeben, die eine Weiterverarbeitung unmöglich machen.

Aus diesem Grund nehmen wir mal ein eigenes Modul als Modul des Monats: Data::Validate::WithYAML

Dieses Modul ist nicht mehr ganz so jung (erster Commit in Github ist von 2008 – und das war ein Import vom CVS-Repository), aber es ist bei uns immer noch in etlichen Anwendungen im Einsatz. Auf unseren Webseiten gibt es verschiedene Formulare, wie das typische Kontaktformular oder das Bestellen von Demoinstanzen unserer Erweiterungen für die ((OTRS)) Community Edition.

Ich habe keines der damals existierenden Module genommen, weil ich bei Änderungen am Formular und/oder Erkenntnissen was valide ist ohne Code-Änderungen in der Überprüfung der Formularinhalte übernehmen wollte.

Nehmen wir mal ein Formular zur Anmeldung Hundesteuer:

3978a611-a90e-4e36-b994-2c7696624cb0.png

Das ist ein bunter Mischmasch an erlaubten Werten in den Feldern... Das muss alles geprüft werden. Im ersten Feld (Anzahl der Hunde) wird es eine natürliche Zahl sein. Bei der Anrede gibt es eine Auswahl an Werten, der Name ist ziemlich frei, bei der Adresse könnte man prüfen, ob diese tatsächlich existiert und für die E-Mail-Adressen gibt es auch Regeln.

YAML ist ein tolles Format für Konfigurationsdateien. Aus diesem Grund beschreiben wir die Regeln für die Felder in einer YAML-Datei. In dem Dokument muss das Formular angegeben werden und dort die einzelnen Felder.

---
name_des_formulars:
  feld1:
    ...
  feld2:
    ...

Bei der Definition gibt man dann an, ob es ein Pflichtfeld ist oder nicht

---
name_des_formulars:
  feld1:
    type: required
    ...
  feld2:
    type: optional
    ...

Alle weiteren Angaben bei den Feldern beschreiben die Regeln für dieses Feld. In dem Modul sind schon einige Regeln definiert:

  • regex

    Der eingegebene Wert muss dem regulären Ausdruck entsprechen.

  • not_regex

    Der eingegebene Wert darf nicht dem regulären Ausdruck entsprechen. Wir nutzen das z.B., um Spam zu finden.

  • min

    Der eingegebene Wert muss größer sein als der bei min angegebene Wert.

  • max

    Der eingegebene Wert muss kleiner sein als der bei max angegebene Wert.

  • enum

    Eine Liste von gültigen Werten.

  • length

    Die Länge des eingegebenen Wertes muss dem Längenbereich entsprechen...

    length: 1,
    

    ... der Wert muss mindestens 1 Zeichen lang sein.

    length: 3,5
    

    ... der Wert muss zwischen 3 und 5 Zeichen (Grenzen eingeschlossen) lang sein.

    length: ,5
    

    ... der Wert darf maximal 3 Zeichen lang sein.

    length: 3
    

    ... der Wert muss exakt 3 Zeichen lang sein.

  • datatype

    Für einige Datentypen gibt es auch schon vorgefertigte Prüfungen:

    • num
    • int
    • positive_int

Mit diesen grundlegenden Regeln kommt man bei dem obigen Formular schon ein Stück weiter:

---
hundesteuer:
  anzahl_hunde:
    type: required
    min: 1
    datatype: positive_int
  anrede:
    type: required
    enum:
        - Herr
        - Frau
        - ''
  name:
    type: required
    length: 3,
  geburtsdatum:
    type: required
    regex: ^[0-9]+\.[0-9]+\.[0-9]+$
  wohnort:
    type: required
    length: 2,
  email:
    type: required
    regex: \@[\w\.]+

Das ist mit Sicherheit noch nicht optimal, denn ergibt es Sinn, dass jemand 1.000 Hunde anmeldet? Es wäre ein Geburtsdatum 9999.13913.1 möglich oder dass eine Adresse angegeben wird, die gar nicht existiert. E-Mailadressen mit einem so einfachen regulären Ausdruck zu prüfen ist nicht die beste Idee.

Aus diesem Grund gibt es noch weitergehende Möglichkeiten, eine der wichtigsten ist die Benutzung von Plugins. Für E-Mail-Adressen gibt es beispielsweise schon fertige Plugins wie Data::Validate::WithYAML::Plugin::EmailMX.

  email:
    type: required
    plugin: EmailMX

Weiterhin ist es möglich, mehrere Regeln zu kombinieren. So könnte man bei der Mail z.B. den regulären Ausdruck, eine Mindestlänge und das Plugin verwenden:

  email:
    type: required
    regex: \@[\w\.]+
    length: 6,300
    plugin: EmailMX

Zu jedem Feld kann man auch noch eine message angeben, die im Fehlerfalle von validate zurückgegeben wird.

Sind die Regeln für das Formular in der YAML-Datei hinterlegt, kann man das Formular validieren:

use Data::Validate::WithYAML;
use Data::Printer;

my $validator = Data::Validate::WithYAML->new(
    $path_to_yaml_config,
);

# Simulation der Benutzereingaben
my %input_hash = (
    anrede => 'Herr',
    anzahl_hunde => 1,
    name => 'Franziska Meier',
);

my %errors = $validator->validate( step1 => %input_hash );
p %errors;

Für die Verwendung von Data::Validate::WithYAML in Mojolicious gibt es auch ein entsprechendes Plugin. Damit wird die Verwendung weiter vereinfacht. Die YAML-Datei bleibt die gleiche. In der Mojolicious-Anwendung wird das Plugin geladen:

#!/usr/bin/env perl
use Mojolicious::Lite;

plugin 'Data::Validate::WithYAML' => {
    conf_path => app->home . '/conf'
};

get '/' => 'hundesteuer_form';
post '/' => sub {
    my ($c)    = shift;
    my %errors = $c->validate( 'form' => 'step1' );

    if ( %errors ) {
        $c->stash( errormsgs => \%errors );
        return $c->render('hundesteuer_form');
    }

    $c->render( data => $c->dumper( $c->req->params ) );
};

app->start;

__DATA__
@@ hundesteuer_form.html.ep
... Code siehe Gitlab-Repository ...

Das Mojolicious-Plugin stellt die Methode validate zur Verfügung. Dort gibt man nur noch den Dateinamen (ohne .json) an. Wenn alles ok ist, liefert die Methode nichts zurück, ansonsten gibt es einen Hash mit den Fehlern.

Dieser Code ist auch im Repository vorhanden.


Permalink: