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:
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:
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: /2020-09-15-data-validate-withyaml