Korrekte Sache

Datenvalidierung: Benutzereingaben überprüfen [How-to]
Kommentare

Wer kennt es nicht: Da hat man eine schöne Eingabemaske gebaut, und dann kommen die Benutzer. Diese verschreiben sich oder sind sehr kreativ bei dem Ausfüllen von Formularen. Dieser Artikel zeigt verschiedene Möglichkeiten, Benutzereingaben zu überprüfen.

Programme ohne Eingabemasken gibt es kaum, und mit ihrer Einführung sind auch ihre Bediener hinzugekommen. Beides zusammen ist eine interessante Mischung. Denn Benutzer machen nicht nur unabsichtliche Eingabefehler. Wenn sie nicht auf Anwendungen geschult worden sind oder sie weitere Eingabefelder benötigten, die die Anwendung aber nicht bietet, landen schon mal bewusst in einem Adresszusatzfeld interne Bemerkungen oder in E-Mail-Adressfeldern einfach Telefonnummern oder Ansprechpartner. So lange nur eine Speicherung der Daten in String-Feldern erfolgt, ist sie nicht weiter problematisch. Allerdings nur dann, wenn die Texte von der Länge her in die Datenbankfelder passen. Ebenfalls interessant wird es, wenn die Daten weiterverwendet werden sollen. Eine Faxnummer in einem E-Mail-Feld erzeugt unnötig Fehler bei einem E-Mail-Versand. Sinnvollerweise sollten die eingegebenen Daten vor dem Speichern geprüft werden. Natürlich sollte der Benutzer hingewiesen werden, wenn er eine fehlerhafte Eingabe getätigt hat bzw. das Programm sollte in diesem Fall nicht abstürzen.

Um Eingabedaten zu prüfen, gibt es viele Möglichkeiten. Eine ist es, die Daten manuell zu prüfen. Bei einer Längenprüfung ist das noch sehr einfach, denn es muss nur mit der Delphi-Funktion Length(<String>) die Länge ermittelt werden. Diese wird anschließend mit der Maximal- und Minimallänge geprüft. Die Prüfung einer Telefonnummer in der Schreibweise „Richtlinie für den Schriftsatz aus dem Druckmedien ABC“ ist schon aufwändiger. Diese Norm besagt, dass Telefonnummern sowie Faxnummern wie folgt geschrieben werden sollen:

+49 (0) 89 - 1 23 45 67

So soll die Nummer von rechts nach links in Zweiergruppen getrennt durch ein Leerzeichen geschrieben werden. Die erste Null in der Vorwahl soll in Rundenklammern gefasst werden. Die Vorwahl selbst wird mit einem Minuszeichen von der Rufnummer getrennt. Voran wird dann nur noch die internationale Vorwahl gestellt. Das Exit-Symbol wird als Pluszeichen dargestellt. Diese Prüfung zu implementieren, ist sehr aufwändig. Ein Glück, dass die DIN 5008 die Schreibweise von Telefonnummern seit Kurzem festlegt. Die Schreibweise wurde stark vereinfacht und enthält nur noch Leerzeichen zum Trennen der internationalen Vorwahl, der Vorwahl und der Rufnummer. Das Problem bleibt jedoch. Sollen komplexere Dinge geprüft werden, dann wird die Implementierung aufwändig. Das nächste Beispiel liegt direkt auf der Hand, denn die Prüfung von E-Mail-Adressen ist ebenfalls aufwändig: Die Adresse müsste in Postfach bzw. Name, in Domain und in Toplevel Domain zerlegt werden. Dabei ist dann der Domainanteil auch variabel. Beispiel:

Max.Muster@muster.email.de

Reguläre Ausdrücke

Doch zum Glück gibt es Reguläre Ausdrücke. Diese sind für die Programmiersprache Perl entwickelt worden. Philip Hazel empfand diese Ausdrücke als praktisch und hat eine C-Bibliothek für die E-Mail-Server Exim entwickelt. Diese Bibliothek ist inzwischen sehr stark verbreitet und wird in vielen Programmen mit Rang und Namen eingesetzt. Unter diesen Programmen sind unter anderem der Apache HTTPD, PHP oder Postfix. Durch die hohe Verbreitung gibt es natürlich auch eine Version für Windows von dieser Bibliothek. In Delphi XE2 ist diese Bibliothek ebenfalls integriert. Ein Wrapper für die Bibliothek für vorherige Delphi-Versionen kann hier heruntergeladen werden.

Doch kommen wir erst einmal zu dem Funktionsumfang der Bibliothek. Mit dem Perl Compatible Regular Expressions (PCRE) kann man suchen. Die Suche selbst wird mit einem speziell formatierten Text beschrieben. Wenn nun beschrieben werden soll: Ich suche einen Text, der acht Zeichen lang ist, mit einer Null anfangen soll und mit einem X beendet werden muss, kann das ganz einfach wie folgt beschrieben werden: ^0.{6}X$

Das Dachzeichen steht für den Textanfang, das Dollarzeichen für Textende und der Punkt ist ein Platzhalter für ein beliebiges Zeichen. Die geschweiften Klammen geben eine erforderliche Anzahl an – in diesem Fall müssen es sechs Zeichen sein. Zusammen gelesen mit dem Punkt: Nach der Null, sechs beliebige Zeichen gefolgt von einem X. Da es sehr viele Metazeichen und Funktionen in den Regulären Ausdrücken gibt, verweise ich einfach mal an dieser Stelle auf die Webseite regular-expressions.info. Dort befindet sich eine umfassende Erklärung zu den Ausdrücken. Ich möchte mich hier lieber auf ein paar kleine praxisnahe Beispiele beschränken. Kommen wir zurück auf das E-Mail-Beispiel: Die E-Mail-Adresse ist in vielen Internetstandards gleich. Beschrieben ist diese unter anderem in der RFC 5322 (Absatz 3.4.1). Kurz zusammengefasst besagt die RFC, das eine E-Mail-Adresse nicht mehr als 254 Zeichen lang werden darf. Vor dem Klammeraffen (@) dürfen nicht mehr als 64 Zeichen stehen. Dieser Abschnitt wird localpart genannt. Hinter dem Klammeraffen befindet sich der Internetdomainanteil. Schauen wir uns den nachfolgenden Ausdruck genauer an: ^[a-zA-Z0-9._%+-]{1,64}@(?:[a-zA-Z0-9-]+.)+[a-zA-Z]{2,6}$

Der erste Teil bis zum Klammeraffen soll den localpart erkennen. Er muss von Textanfang mindestens ein Zeichen bis maximal 64 Zeichen lang sein. Erlaubt sind nur die Zeichen a–z, A–Z, 0–9, +, -, %, _ und der Punkt. Der zweite Teil inklusive dem Pluszeichen sucht einen oder mehrere Domainnamen wie domain. oder domain. domain. . Domainnamen dürfen nur aus den Zeichen a bis z, A bis Z, 0 bis 9 und Minus bestehen. Der letzte Teil sucht nun die Toplevel-Domain. Diese darf nur aus a–z und A–Z bestehen und ist zwischen zwei und sechs Zeichen lang. Letzteres soll sicherstellen, dass auch die neueren TLD wie mobil, museum oder Berlin funktionieren. Da die TLD-Domains feststehen, kann auch eine Liste hinterlegt werden. Auf das Prüfen der Gesamtlänge wurde in dem Beispiel verzichtet. Betrachten wir in Listing 1 die Prüfung in Delphi.

  with TPerlRegEx.Create() do
  try
    RegEx:='^[a-zA-Z0-9._%+-]{1,64}@(?:[a-zA-Z0-9-]+.)+[a-zA-Z]{2,6}$';
    Subject:='test@test.test.de';
    if Match then
      MessageDlg('Email gültig', mtConfirmation, [mbOK], 0)
    else
      MessageDlg('Email nicht gültig.', mtError, [mbOK], 0);
  finally
    Free;
  end;

Wie man hier sieht, lassen sich relativ einfach komplexe Suchen definieren. Natürlich können die Regulären Ausdrücke auch für einfache Suchen verwendet werden. Wenn man sich an die Schreibeweise erst einmal gewöhnt hat und die gängigsten Metazeichen auswendig kennt, dann machen die Regulären Ausdrücke richtig Spaß.

Aufmacherbild: business and office concept – businessman working in office von Shutterstock / Urheberrecht: Syda Productions

[ header = Seite 2: E-Mail-Prüfung via DNS ]

E-Mail-Prüfung via DNS

Durch die Prüfung mithilfe von Regulären Ausdrücken kann leider nur die Syntax einer E-Mail-Adresse geprüft werden. Allerdings fallen bei der Prüfung Buchstabendreher und ähnliche Fehler nicht auf. Gleich vorweg – eine E-Mail-Adresse kann niemals auf vollständige Gültigkeit geprüft werden – das liegt am localpart. Allerdings lässt sich der Domänenanteil einer E-Mail-Adresse prüfen. Wenn ein E-Mail-Server eine E-Mail übertragen will, dann benötigt er die IP-Adresse des zuständigen Empfänger-E-Mail-Servers. Die erfragt der eigene E-Mail-Server beim Domain Name System (kurz DNS). Für den E-Mail-Versand (Englisch MailExchange/MX) bietet das DNS spezielle Möglichkeiten. Dort können so genannte MX-Records hinterlegt werden. In einem MX-Record steht nun der Rechnername, der zuständig ist. Bei größeren Anbietern stehen dort auch mehrere Server. Leider haben wir nun noch keine IP-Adresse, da sie im so genannten A-Record abgelegt ist, was nichts anderes ist als eine Zuordnung von einem Namen zu einer IP. Um nun zu ermitteln, ob der Domainteil einer E-Mail gültig ist, braucht nur im DNS gefragt werden, ob es einen (oder mehrere) MX-Einträge/Mailserver gibt. Anschließend prüft man, ob die Rechnernamen in IP-Adressen aufgelöst werden können. Gibt es mindestens eine gültige IP-Adresse, dann kann davon ausgegangen werden, dass die E-Mail-Adresse gültig ist. Was nicht geprüft werden sollte, ist, ob eine Verbindung zum E-Mail-Server aufgebaut werden kann. E-Mail-Systeme sind des Öfteren wegen temporärer Störungen nicht erreichbar. Häufig kommt es übrigens vor, dass manche Administratoren in die MX-Records IP-Adressen stecken. Das ist zwar nicht gültig, wird aber dennoch von vielen Systemen akzeptiert. In diesen Fällen kann die Abfrage der A-Records entfallen. Manche Administratoren vergessen auch manchmal ganz, MX-Records für ihre Domains anzulegen. Auch dieser Fehler wird oft ignoriert und stattdessen dann einfach nur der A-Record zum Domainnamen abgerufen, um die IP-Adresse zu erhalten. Der E-Mail-Servername muss übrigens nichts mit dem Domainnamen zu tun haben. So nutzt z. B. gmail.com die E-Mail-Server mit den Namen gmail-smtp-in.l.google.com und alt1.gmail-smtp-in.l.google.com. Mit dem Kommandozeilenprogramm nslookup lassen sich DNS- Einträge abrufen (Listing 2).

> nslookup -type=mx gmail.com
Server:  dns.blackhole.local
Address:  10.10.0.4

Nicht autorisierende Antwort:
gmail.com       MX preference = 40, mail exchanger = alt4.gmail-smtp-in.l.google.com
gmail.com       MX preference = 5, mail exchanger = gmail-smtp-in.l.google.com
gmail.com       MX preference = 10, mail exchanger = alt1.gmail-smtp-in.l.google.com
gmail.com       MX preference = 20, mail exchanger = alt2.gmail-smtp-in.l.google.com
gmail.com       MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com

Was hier noch zusätzlich gesehen werden kann, ist, dass Google mit verschiedenen Prioritäten arbeitet. Je kleiner die Zahl, desto eher sollte der Server zum E-Mail-Empfang genutzt werden. Sollte nun aus Delphi heraus auf das DNS-System zugegriffen werden, so können unter anderem die Internet.Direkt.Komponenten (INDY) genutzt werden. In diesen ist eine Komponente mit dem Namen TldDNSResolver, welche die DNS-Abfragen implementiert. In Listing 3 befindet sich ein kleines Beispiel, wie von einer Domain der MX- und anschließend der A-Record abgefragt werden kann. Das Beispiel hat eine ähnliche Ausgabe wie der zuvor gezeigte nslookup. Zu dem Beispiel muss noch eines gesagt werden: Wenn eine Domain nicht aufgelöst werden kann, dann erzeugt die Methode Resolve eine Ausnahme. Diese muss unbedingt in einem produktiven Einsatz abgefangen werden.

var
  i, j, gueltigeServer: integer;
  domain: String;
begin
  domain:='gmail.com';

  gueltigeServer:=0;
  with TIdDNSResolver.Create() do
  try
    Host:='8.8.8.8'; // Google DNS Server
    QueryType:=[qtMX];
    Resolve(domain);
    for i:=0 to QueryResult.Count-1 do
      if QueryResult[i] is TMXRecord then
        with TMXRecord(QueryResult[i]) do
          with TIdDNSResolver.Create() do
          try
            mem_1.lines.add('Server: '+ExchangeServer+' pref:'+inttostr(Preference));
            Host:='8.8.8.8';
            QueryType:=[qtA];
            Resolve(ExchangeServer);
            for j:=0 to QueryResult.Count-1 do
              if QueryResult[j] is TARecord then
                with TARecord(QueryResult[j]) do
                begin
                  mem_1.lines.add('Ip: '+IPAddress);
                  inc(gueltigeServer);
                end;
          finally
            Free();
          end;
  finally
    Free();
  end;
  mem_1.lines.add('Gültige Server: '+inttostr(gueltigeServer));
end;

Prüfsummen

Wie am E-Mail- bzw. im DNS-Beispiel zu sehen ist, kann die Prüfung nicht ohne langsame Abfragen ins Internet gemacht werden. Hinzu kommt, dass das Beispiel eine E-Mail nicht vollständig prüfen kann. Daten können allerdings direkt und offline geprüft werden; für diesen Zweck sind Prüfsummen erfunden worden. Prüfsummen sind schnell erklärt: Durch gezieltes Hinzufügen von Zusatzinformationen können bestehende Daten geprüft werden. Eine der einfachsten Verfahren sind Quersummen von Daten. Aus der Zahlenreihe 123 wird die Quersumme 6 ermittelt. Diese Zahl wird nun an die Reihen angefügt. Es entsteht 1236. Geht nun eine Zahl verloren oder wird sie ausgetauscht, dann würde sich die Quersumme ändern und könnte dann als Fehler erkannt werden. Die Quersumme ist allerdings ein sehr einfaches Verfahren. Das spiegelt sich auch in der Erkennungsrate von Fehlern wider. Dieses Verfahren erkennt zum Beispiel nicht, wenn Zahlen untereinander vertauscht werden. Damit auch solche Fehler erkannt werden können, wurden Verfahren mit Wertigkeiten für Stellen entwickelt. Solche Verfahren werden in sehr vielen Zahlen verwendet. Einer der bekanntesten Vertreter, die im Einzelhandel zu finden ist: die EAN (European Article Number) ist auf nahezu jedem Produkt, das im Supermarkt erworben werden kann. Bezahlen wir den Einkauf mit der Bankkarte, dann begegnen uns gleich die nächsten Zahlen, welche durch Prüfsummen gesichert sind: Kontonummern und die neuen IBAN-Nummern sind ebenfalls vor Fehleingaben gesichert. Die Prüfsummen für Kontonummern sind allerdings nicht einheitlich; wer die Prüfsummen berechnen möchte, kann bei der Bundesbank eine Bankleitzahlentabelle herunterladen. Darin ist hinterlegt, welches Verfahren zu Kontonummern einer Bankleitzahl genutzt werden muss, um die Prüfsumme zu berechnen. Die verschiedenen Verfahren können ebenfalls bei der Bundesbank heruntergeladen werden. Bitte nicht erschrecken, denn es gibt knapp 140 verschiedene Algorithmen! Diese ähneln sich zwar, sind aber immer im Detail verschieden. Die neuen IBANs sind übrigens ebenfalls mit einer Prüfsumme gesichert. Der Algorithmus ist hier zu finden.

Im Versicherungsbereich sind ebenfalls Prüfsummen üblich – so sind Vertragsnummern oft gesichert. Auch die Registriernummer der IHK für Versicherungsmakler ist mit einer Prüfsumme abgesichert. Aber schauen wir noch einmal zurück auf den EAN-Code. Der EAN hat eine Länge von dreizehn Stellen, die letzte Stelle ist die Prüfziffer. Nun wird die Summe der geraden Stellen gebildet und die der ungeraden Stellen. Letztere Summe wird mit drei multipliziert und zu der ersten Summe addiert. Schauen wir uns das an einen Beispiel an. Die Nummer 4100250006589 entstammt von Kaubonbons. Die 9 in der letzten Stelle ist die Prüfziffer. Nun addieren wird jede gerade Stelle zusammen: 4 + 0 + 2 + 0 + 0 + 5 = 11.

Nun sind noch die ungeraden Stellen dran. Das Ergebnis wird anschließend mit 3 multipliziert: 1 + 0 + 5 + 0 + 6 + 8 = 20 * 3 = 60.

Beide Summen zusammen ergeben 71. Die Differenz zu der nächsten vollen Zehnerstelle ergibt die Prüfziffer. In diesem Fall: 80 – 71 = 9.

Durch die alternativen Wertigkeiten gibt es mehr Sicherheit, wodurch auch Zahldreher erkannt werden können. Gerne werden in diesen Zusammenhang Primzahlen für die Stellenwertigkeiten genutzt.

Datenbanken

Wie wir bereits bei der Prüfziffernvalidierung gesehen haben, ist es sinnvoll, Datenbanken einzusetzen. In diesen Fall waren es die Bankleitzahlen, welche die Bundesbank zur Verfügung stellt. Damit kann zum einen geprüft werden, ob eine Bankleitzahl existiert, und zum anderen konnten Zusatzinformationen genutzt werden. Es gibt sehr viele öffentlich verfügbare Datenbanken. Die Bundesnetzagentur hat die verschiedenen Vorwahlen gut dokumentiert. So können Vorwahlen in Telefon und Faxnummern geprüft werden. Dieses geht hervorragend mit dem Ortskennzahlenverzeichnis. Da wir gerade bei Ortskennzahlen sind – Orte haben auch Postleitzahlen. Die Uni Paderborn stellt ein Postleitzahlenverzeichnis zur Verfügung. Das würde zumindest reichen, um von Adressen die Postleitzahl inklusive dem Ort zu prüfen. Allerdings muss bei Postleitzahlen drauf geachtet werden, dass es Postleitzahlen für Großempfänger gibt. Daher eignet sich diese Datenbank nur für natürliche Adressen. Die Kür der Adressprüfung bekommt man mit einer vollständigen Adressprüfung inkl. Straße, Hausnummer und Postleitzahl. Hier bietet es sich an, einen Datenbestand von einen Brief und Paketdienstleister zu kaufen. Man könnte zwar auch die Daten aus der OpenStreetMap (OSM) nutzen, doch die sind teilweise etwas problematisch in der Nutzung. Auf einige Probleme wird hier eingegangen. Die Datenbanken oder auch das Live-API zum Zugriff auf die Geodaten können hier gefunden werden.

Fehlermeldungen

Nachdem nun durch die Validierung ein Fehler festgestellt wurde, sollte der Benutzer benachrichtig werden. Wenn das geschieht, sollte ihm keine Fehlermeldung mit einem allgemeinen Text präsentiert werden. Eine Fehlermeldung im Sinne von „Es ist ein Fehler aufgetreten“ hilft dem Benutzer nicht. Es sollte daher immer eine Fehlermeldung angezeigt werden, die erklärt, warum etwas falsch ist und wie der Benutzer den Fehler beheben kann. Es sollte auch immer geprüft werden, ob das Programm den Fehler selbst beheben kann. Das kann geschehen, wenn eine Summe einen Maximalwert überschreitet und stattdessen dieser eingesetzt werden muss. Allerdings sollte bei solchen Anpassungen der Eingaben der Benutzer dennoch informiert werden. Ebenfalls sollte es vermieden werden, einen MessageDialog oder eine MessageBox mit der Fehlermeldung zu präsentieren. Denn die muss der Benutzer dann bestätigen, was aber auch gleichzeitig das Problem ist. Wenn viele Meldungen auf einmal angezeigt werden, müsste er mehrfach bestätigen. Würden alle Meldungen in einer Box angezeigt werden, dann müsste der Benutzer die Meldung auswendig lernen, damit er in einen Schritt seine Eingaben korrigieren kann. Besser ist es, die Meldungen direkt im Eingabeformular anzuzeigen. Auf Webseiten empfiehlt es sich, auf jeder Fehlermeldung einen Link anzubringen, der zur fehlerhaften Eingabe bzw. dessen Eingabefeld springt. Zusätzlich sollten die Eingabefelder hervorgehoben werden, die eine fehlerhafte Eingabe enthalten. Die Farbgebung sollte auch beachtet werden: Benutzer sind es gewohnt, wichtige Dinge in Signalfarben zu sehen – Rot eignet sich daher sehr gut für Fehlermeldungen. Hinweise können mit passiveren Farben in die Oberfläche gebracht werden.

Fazit

Datenvalidierung ist eine wichtige Vorgehensweise in Anwendungen. Mit einer guten Validierung lassen sich nicht nur technische Fehler vorbeugen – wie unter anderem Ausnahmen aus der Datenbankschicht, wenn Feldgrößen überschritten werden. Fachliche Überprüfungen sind ebenfalls wichtig, gerade im Bereich der Messwerterfassung oder bei Versicherungsleisten, die in Verbindung zueinander stehen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -