Skip to content

Datenbankbasierende Web- Applikationen unter Perl

 
Andreas Grupp, Elektronikschule Tettnang

Erstellung dynamischer HTML-Seiten unter Verwendung eines Datenbankservers
                        Einführung

Ein generelles Problem aller Programmiersprachen ist der Zugriff auf externe Datenquellen, die nicht direkt durch das Betriebssystem zur Verfügung gestellt werden. Der Umgang mit Dateien ist hier weniger betroffen, da diese Funktionalität in kooperativer Form mit dem darunterliegenden Betriebssystem erledigt wird. Insbesondere für Datenbankzugriffe gilt dies aber erst einmal nicht mehr. Datenbanken sind Programme, die im allgemeinen nicht nahtlos in die jeweiligen Betriebssysteme eingebunden sind. Sie stellen ihre Dienste im allgemeinen als Serverdienste oder über eine eigene proprietäre Schnittstelle zur Verfügung. Aus diesem Grund wurden in der Vergangenheit diverse Konzepte erarbeitet um möglichst einfach - und das heißt unabhängig von der verwendeten Datenbanksoftware - auf verschiedenste Datenquellen zugreifen zu können (DBI, ODBC, JDBC, ...). Grundgedanke bei diesen Konzepten ist es den verschiedenen Programmiersprachen einen universellen Datenbankserver "vorzugaukeln". Die reale "Kleinarbeit" beim Umgang mit einem Datenbankserver wird jeweils von Subkomponenten (Treibern) erledigt, die für einen bestimmten Datenbankserver maßgeschneidert sind (siehe auch Einführungsartikel ".com - where business meets the net).

Nachfolgend soll das unter Perl verwendete Konzept näher erläutert werden.

Das Database Interface (DBI) Konzept

Perl verwendet ein datenbankunabhängiges Zugriffskonzept um auf die verschiedensten Datenquellen zuzugreifen. Der eigentliche Zugriff wurde deshalb in ein Zweischichtenmodell aufgetrennt. Perlskripte (Shellskripte oder -programme, Administrationsskripte, CGI-Skripte, ...) verwenden zum Datenzugriff ein Perl-Modul namens DBI (Database Interface). Dieses Modul bietet seinen Benutzern einen Satz wohldefinierter Methoden (Funktionen) um auf Datenquellen zugreifen zu können. Ähnlich wie andere DB-Zugriffskonzepte ist es dabei im Großen und Ganzen egal auf welchen realen Datenbankserver zugegriffen wird.

Abbildung 1: Die Schichtenmodelle bei DBI und ODBC

Das Database Interface reicht die Zugriffswünsche wiederum über eine standardisierte Softwareschnittstelle an sogenannte "Database Driver (DBD)" weiter. Diese DBDs sind nun jeweils speziell für einen bestimmten Datenbankserver programmiert worden und kennen desen Eigenheiten und "Umgangsformen" genauestens. Sie sind nun für eine Umsetzung der allgemeinen Zugriffsbefehle des DBI-Moduls auf die speziellen Zugriffsfunktionen ihres Datenbankservers verantwortlich.

Database Driver (DBD) existieren für eine Vielzahl von Datenbanken. Als Beispiele seien hier die folgenden SQL-Server genannt:

* MySQL

* Oracle

* Informix

* PostgreSQL

* DB2 von IBM

* Adabas von der Software AG, Darmstadt

* Sybase

* ...

Eine Liste weiterer Datenbanken können Sie unter [2] abrufen.

Leider gibt es auch Ausnahmen. Insbesondere für den Microsoft-SQL-Server existiert kein eigener DBD. Hier kann man jedoch über einen Database Driver, der auf ODBC-Datenquellen zugreifen kann, (DBD::ODBC) Abhilfe schaffen da diese Schnittstelle auch beim MS-SQL-Server zur Verfügung steht. Da unter Microsoft-Windows auch eine Microsoft-Access-Datenbank als ODBC-Datenquelle konfiguriert werden kann, funktioniert dieser DBD::ODBC-Zugriff auch für Access [3].

Falls der Webserver auf einem anderen Rechner läuft als der MS-SQL-Server und womöglich noch ein anderes Betriebssystem fährt (Novell, Unix, Linux, ...), so kann mittels des Pseudo-Treibers DBD::Proxy eine Verbindung zwischen den beiden Rechnern hergestellt werden und dann über den DBD::ODBC auf den SQL-Server zugegriffen werden [3].

Ein letzter DBD sei hier auch noch erwähnt. Er ist insbesondere dann interessant, wenn man gar keinen SQL-Server im Hintergrund hat. Mit Hilfe des DBD::CSV ist es möglich auf *.csv-Dateien (Comma Seperated Values können beispielsweise aus Microsoft-Excel heraus erzeugt und auch wieder eingelesen werden) zuzugreifen und dabei SQL-Befehle zu verwenden. Die CSV-Datei wird damit in einer Art SQL-Server-Emulationsmodus betrieben. Damit kann man mittels "SELECT ..." auf Datensätze zugreifen und den ganzen Auswahlkomfort von SQL benutzen. Auch das Einfügen von Datensätzen funktioniert wie bei einem echten SQL-Server. Hat man evtl. später einen echten DB-Server, reicht das Austauschen weniger Zeilen, und alle Skripte funktionieren weiter.

Übersicht zum SQL-Server-Zugriff via Perl und DBI/DBD

Das DBI-Modul stellt eine Fülle von Funktionen für die unterschiedlichsten Einsatzszenarien bereit. Die meisten Anwendungen beschränken sich jedoch auf SQL-SELECT- oder INSERT-Anweisungen. Für die Realisierung dieser Aufgaben reicht eine absolut überschaubare Anzahl von Funktionen aus. So benötigt man als Programmierer für einen SELECT nur sechs Funktionen. Auch deren Aufruf läßt sich recht einfach darstellen (siehe Abb. 1).

Abbildung 2: Struktogramm des SQL-Server-Zugriffs via Perl und DBI/DBD

Die einzelnen Schritte sehen dabei wie folgt aus:

1. Zuerst wird unter Verwendung der connect()-Funktion eine Verbindung zum SQL-Server hergestellt. Dabei findet auch die obligatorische Benutzer-Authentifizierung statt.

2. Anschließend kann man bereits eine SQL-Anweisung zur Ausführung vorbereiten. Dazu dient die prepare()-Funktion.

3. Ein anschießendes execute() sorgt für die Ausführung der im vorigen Schritt vorbereiteten SQL-Anweisung. Als Ergebnis erhält man das Abfrageergebnis.

4. Eine gängige Variante zur Verarbeitung des Abfrageergebnisses ist die Bearbeitung der einzelnen Datensätze (z.B. tabellarische HTML-Ausgabe) in Form einer while()-Schleife. Bei jedem Schleifendurchgang wird mittels

fetchrow_array() ein neuer Datensatz aus dem Abfrageergebnis extrahiert. Im Schleifenkörper findet die eigentliche Datensatz-Verarbeitung statt.

5. Wird das Abfrageergebnis nicht mehr benötigt, werden die belegten Ressourcen (RAM, Handels, ...) über die finish()-Funktion freigegeben.

6. Innerhalb einer bestehenden Server-Verbindung könnten sich nun beliebig viele weitere SQL-Anweisungen wiederholen. Die Spiegelstriche 2. bis 5. sind dann entsprechend zu wiederholen. Wird generell kein Datenbankzugriff mehr benötigt, wird mittels der disconnect()-Funktion die Verbindung zum SQL-Server abgebaut.

Das Ganze als normales Perlskript (nicht unter CGI) sieht dann beispielsweise so aus:

#!/usr/bin/perl -w

use strict;

use DBI;

# Verbindungsaufbau zum DB-Server

my $dbh=DBI->connect(`dbi:mysql:perllfb', `username', `passwort')

    || die "Fehler beim Connect: $DBI::errstr\n";

# Vorbereiten einer SQL-Abfrage

my $sth = $dbh->prepare(`SELECT * FROM testtabelle')

    || die "Fehler beim Prepare: $DBI::errstr\n";
 

# Vorbereitete SQL-Abfrage ausführen

$sth->execute

    || die "Fehler beim Execute: $DBI::errstr\n";
 

# Abfrageergebnis abarbeiten

while ( my @ergebnis = $sth->fetchrow_array ){

print $ergebnis[1] . "" . $ergebnis[2] ."\n";

}

# Abfrageressourcen freigeben

$sth->finish;

# Verbindungsabbau der bestehenden Datenbankverbindung

$dbh->disconnect;

In den nächsten Abschnitten werden nun die einzelnen Funktionen näher betrachtet, ihre Parameter erläutert und ihr Rückgabewert erklärt. Dabei wird eine durchgängige webbasierende Datenbankanwendung auf Basis des CGI-Mechanismus entwickelt.

Erste Verbindung zum SQL-Datenbankserver

Sicherheitshalber wird wie immer das Perl-Compiler-Pragma strict verwendet. Wie bereits weiter oben bemerkt, wird für die Datenbankanbindung das Perl-Modul DBI.pm verwendet.

1. Der erste neue Befehl ist connect() . Damit wird eine Verbindung zwischen dem Programm und einer Datenbank aufgebaut. Der Befehl verwendet als Argumente die Datenquelle, den MySQL-User-Name und das zughörige Password.

$dbh = DBI->connect($data_source, $username, $password)

wobei für die Datenquelle gilt:

dbi:DriverName:database=database_name;host=hostname;port=port

DriverName gibt den Namen des DBD-Treibers an, database ist der Name der Datenbank (hier dbi_demo), host ermöglicht die Benutzung eines anderen Rechners als Datenbankserver (hier ist DB-Server und WWW-Server auf demselben Rechner) und port ermöglicht die Angabe einer von der Standardinstallation abweichenden Portnummer. In diesem Beispiel lautet $data_source also: dbi:mysql:dbi_demo

2. Eigentlich würde nun die eigentliche Datenbank-Applikation programmiert. Dies verschieben wir aber auf die nächsten Abschnitte

3. Nachdem die Datenbank nicht mehr benötigt wird, sollte auf jeden Fall mit Hilfe der disconnect-Methode die Verbindung zur Datenbank beendet werden. Obwohl es auch ohne diesen expliziten Verbindungsabbau geht, erspart man sich damit wenigstens die unschöne Fehlermeldung "Database handle destroyed without explicit disconnect".
 

Damit ergibt sich als erstes Beispiel der nachfolgende Code (nicht abtippen, Sie können weiter unten alle Beispiele herunterladen):

#!/usr/bin/perl -w

#

# Dateiname: script1.pl

#     Autor: A. Grupp
 

# Compilerpragma strict um keine Fehler zu machen :-)

use strict;

# Benutzung der Perl-Module CGI und DBI

use CGI;

use DBI;

# Neues CGI-Objekt erzeugen

my $cgi_obj = new CGI;
 

# Datenbank-Verbindung aufbauen

my $dbh = DBI->connect( `dbi:mysql:dbi_demo', `root', `') ||      die "Kann keine Verbindung zum MySQL-Server aufbauen: $DBI::errstr\n";

# Datenbank-Verbindung beenden
 

$dbh->disconnect;

Dieses erste Beispiel erzeugt natürlich noch keinen HTML-Output. Es zeigt nur den Datenbank-Verbindungsaufbau. Wenn es von Hand gestartet wird, dürfte es aber zumindest keine Fehlermeldungen verursachen. Die beim manuellen Start auftretende Nachfrage nach Startparametern (kommt vom CGI-Modul)

(offline mode: enter name=value pairs on standard input)

quittieren sie einfach mit "Ctrl+D".

Ausführen einer Abfrage

Nachdem die Verbindug zu einer Datenbank aufgebaut wurde, können Abfragen ausgeführt werden. Die einzelnen Schritte hierfür sind:
 

1.     Vorbereiten der Abfrage mit Hilfe des pepare()-Befehls. Damit wird der eigentliche Befehl noch nicht ausgeführt, sondern vom Datenbanktreiber zur Ausführung vorbereitet. Der MySQL-Treiber unterstützt meines Wissens nach diesen Mechanismus nicht. Die Ausführung des obigen Statements bewirkt bei ihm nur eine Zwischenspeicherung des Abfrage-Statements zur späteren Ausführung.

 
Syntax des prepare-Statements:

$sth = $dbh->prepare($statement)

wobei das Argument ein gültiges SQL-Statement ist. Falls Sie mit SQL (Structured Query Language) noch nicht so vertraut sind (auswendig schaffe ich es bei schwierigen Abfragen auch nicht so ohne weiteres :-), können Sie die Abfrage z.B. erst einmal mit der grafischen Entwurfsoberfläche eines anderen Datenbanksystems erstellen. Dort haben Sie dann häufig die Möglichkeit später in eine SQL-Ansicht umzuschalten und sich das SQL-Statement anzuschauen. Nach und nach schafft man die einfacheren Statements dann aus eigener Kraft.

Ergebnis dieser Prozedur ist ein sogenannter Statement-Handle, mit dessen Hilfe Sie nachfolgend die Abfrage auch tatsächlich ausführen und auf das Ergebnis zugreifen können. Der Variablenname für den Handle kann übrigens frei gewählt werden.
 

2.     Das vorbereite Statement kann nun mit Hilfe der execute-Methode ausgeführt werden. Die zugehörige Syntax lautet:

$sth->execute

Sie können sich bei Bedarf die Anzahl der gefundenen Datensätze auch gleich in einer Variablen merken. Selbiges ist nämlich eines der Ergebnisse der execute-Methode.

$dsnum = $sth->execute

3.     Anschließend kann man mit diversen weiteren Methoden auf das Abfrageergebnis zugreifen. Dazu jedoch im nächsten Abschnitt.

4.    Wird das Abfrageergebnis nicht mehr benötigt, werden die zugehörigen Ressourcen freigegeben.

$sth->finish;

Die Verwendung der finish-Methode ist nicht unbedingt nötig, trägt aber sicherlich zur besseren Übersicht des betreffenden Codes bei (Zeigefinger ON :-).

# Befehl fuer Ausfuehrung vorbereiten. Referenz auf Statement

# Handle Objekt wird zurueckgeliefert

my $sth = $dbh->prepare( `SELECT * FROM tbl_art' ) ||die

"Kann Statement nicht vorbereiten: $DBI::errstr\n";
 

# Vorbereitetes Statement (Abfrage) ausfuehren

$sth->execute ||die

"Kann Abfrage nicht ausfuehren:  $DBI::errstr\n";

# Statement-Handle Resources freigeben

$sth->finish;

Wie bereits beim vorigen Beispiel wird immer noch kein HTML-Output erzeugt. Wir sind jetzt aber bereits im Besitz eines Abfrageergebnisses und können bald daran gehen, auch eine HTML-Seite zu erzeugen.

Zugriff auf das Abfrageergebnis

Der Abfragehandle ist nach geglückter Ausführung der Abfrage das Tor zum Abfrageergebnis. Durch Anwendung der fetchrow_array-Methode auf den Statement-Handle wird nämlich ein Datensatz aus dem Abfrageergebnis geholt und in einem Array als Rückgabewert geliefert. Die einzelnen Datenfelder sind die Arrayelemente, welche nun für für eine weitere Be-/Verarbeitung zur Verfügung stehen. Außerdem wird auf den nächsten Datensatz im Ergebnis positioniert. Ein weiteres fetchrow_array liefert dann den nächsten Datensatz und so fort. Wird nach letzten Datensatz erneut fetchrow_array angewendet, so wird "undef" zurückgeliefert.

Nachfolgend ein Codebeispiel zur Anwendung von "fetchrow_array":

# Bearbeitung des Abfrageergenisses

while ( my @ergebnis = $sth->fetchrow_array ){

# Im Array @ergebnis steht nun ein Datensatz

print $ergebnis[0] . "" . $ergebnis[1] . "\n";

}

Wird das Skript nun von "Hand" ausgeführt, erhält man das folgende Ergebnis:

> script3.pl

(offline mode: enter name=value pairs on standard input)

1 Zweibeiner

2 Vierbeiner

3 Keinbeiner

4 Mehrbeiner

>

Das Abfrageergebnis als CGI-Skript mit HTML-Output

Damit das Ganze jetzt ein CGI-Skript mit HMTL-Output wird, müssen noch die entsprechenden Ausgabeteile um das vorige Beispiel gruppiert werden. Das ist jetzt aber bereits die Kür und hat mit dem eigentlichen Datenbank-Kontakt nichts mehr zu tun (siehe dazu die Erläuterung zum Perl5-Modul CGI.pm bzw. benutzen Sie den Befehl "perldoc CGI").

#==================================================================
# Bearbeitung des Abfrageergenisses und Ausgabe als HTML-CGI #==================================================================
# HTTP-Header ausgeben (Perl 5 CGI Library)

print $cgi_obj->header

(         -type=>'text/html',

          -expires=>'+1h');

# HTML-Header ausgeben (Perl 5 CGI Library)

print $cgi_obj->start_html

(         -title=>'Wieviele Beine hast den du?',

          -author=>'grupp@lbs.bw.schule.de',

           -BGCOLOR=>'#FFFFFF',

           -LINK=>'#0000AA',

           -VLINK=>'#0000AA',

           -ALINK=gt;'#CC0000',

           -DTD=gt;'-//W3C//DTD HTML 3.2//EN'
            );

print $cgi_obj->h2(`Zu welcher Sorte gehörst denn du?');

print "<UL>\n";
 

while ( my @ergebnis = $sth->fetchrow_array ){

        # Im Array @ergebnis steht nun ein Datensatz

        print `<LI>' . $ergebnis[1] . "\n";

}

print "</UL>\n";

# HTML-Dokument beenden

print $cgi_obj->end_html;

#==================================================================

Und hier das ganze Skript nochmal im Überblick

#!/usr/bin/perl -w

#

# Dateiname: script4.pl

#     Autor: A. Grupp

# Compilerpragma strict um keine Fehler zu machen :-)

use strict;

# Benutzung der Perl-Module CGI und DBI

use CGI;

use DBI;
 

# Neues CGI-Objekt erzeugen my $cgi_obj = new CGI;

# Datenbank-Verbindung aufbauen
 

my $dbh = DBI->connect( `dbi:mysql:dbi_demo', `root', `')

        || die "Kann keine Verbindung zum MySQL-Server aufbauen:

$DBI::errstr\n";
 

# Befehl fuer Ausfuehrung vorbereiten. Referenz auf Statement

# Handle Objekt wird zurueckgeliefert

my $sth = $dbh->prepare( `SELECT * FROM tbl_art' )

        ||die "Kann Statement nicht vorbereiten: $DBI::errstr\n";

# Vorbereitetes Statement (Abfrage) ausfuehren

$sth->execute ||

        die "Kann Abfrage nicht ausfuehren: $DBI::errstr\n";

#==================================================================
# Bearbeitung des Abfrageergebisses und Ausgabe als HTML-CGI #==================================================================

# HTTP-Header ausgeben (Perl 5 CGI Library)

print $cgi_obj->header(

        -type=>'text/html',

        -expires=>'+1h');
 

# HTML-Header ausgeben (Perl 5 CGI Library)

print $cgi_obj->start_html(

        -title=>'Wieviele Beine hast du?',

        -author=>'grupp@lbs.bw.schule.de',

        -BGCOLOR=>'#FFFFFF',

        -LINK=>'#0000AA',

        -VLINK=>'#0000AA',

        -ALINK=>'#CC0000',

        -DTD=>'-//W3C//DTD HTML 3.2//EN'

        );

print $cgi_obj->h2(`Zu welcher Sorte gehörst denn du?');

print "<UL>\n";
 

while ( my @ergebnis = $sth->fetchrow_array ){

        # Im Array @ergebnis steht nun ein Datensatz

        print `<LI>' . $ergebnis[1] . "\n";

}

print "</UL>\n";
 

# HTML-Dokument beenden

print $cgi_obj->end_html;

#==================================================================
# Statement-Handle Resources freigeben

$sth->finish;
 

# Datenbank-Verbindung beenden

$dbh->disconnect;
 

Und das Ganze mit ein paar Anchors und einer weiteren

Abfrage

Die Beispiel-Datenbank besteht aus zwei Tabellen. Bisher haben wir nur die erste Tabelle abgefragt. Das Ergebnis dieser Bemühungen kann man nun aber gleich als Hyperlink gestalten. Hierzu sind geringe Änderungen bei der Ausgabe nötig.

while ( my @ergebnis = $sth->fetchrow_array ){

        # Im Array @ergebnis steht nun ein Datensatz

        print `<LI><A HREF="script6.pl?art=' . $ergebnis[0]

        . `">' . $ergebnis[1] . "</A>\n";

}

Das erste Datenfeld $ergebnis[0] enthät die Kennziffer, um in der zweiten Tabelle die verwandten Datensätze aufzufinden. In die Ausgabe wird nun ein Hyperlink integriert, der auf das Perlskript "script6.pl" verweist. Diesem Skript wird aber noch ein Parameter mit auf die Reise gegeben. Dies geschieht durch Anhängen von z.B. "?art=1". Den "?art="-Teil findet man ja noch ganz einfach. Die Nummer soll aber flexibel von Hyperlink zu Hyperlink wechseln und die richtige Kennziffer beinhalten. Durch Stringverkettung gelingt dies mit Hilfe des Datenfelds $ergebnis[0]. Innerhalb des Hyperlinks wird der Obergriff aus Datenfeld $ergebnis[1] angezeigt.

Das Perlskript "script6.pl" muß nun auf die übergebene Kennziffer "art=x" zugreifen. Hierfür gibt es ja die elegante Variante via CGI.pm.

my $art;

if ( defined($cgi_obj->param('art')) ){

   $art = cgi_obj->param('art');

}

else{    die "Dies Skript muß anders aufgerufen werden!";

}

Nun haben wir es schon fast. Mit Hilfe der Variablen "$art" können wir unsere ursprüngliche Abfrage etwas modifizieren. Es wird natürlich die zweite Tabelle abgefragt und es wird eine kleine WHERE-Klausel an die Abfrage angehängt.

# Befehl fuer Ausfuehrung vorbereiten. Referenz auf Statement

# Handle Objekt wird zurueckgeliefert

        my $sth = $dbh->prepare( "SELECT * FROM tbl_bsp WHERE bsp_artID=$art" ) ||      die "Kann Statement 
        nicht   vorbereiten: $DBI::errstr\n";
 

Einfügen von Datensätzen

In eine Datenbank lassen sich auf diese Art und Weise auch neue Datensätze einfügen. So könnte es im obigen Beispiel z.B. den Wunsch geben, zusätzliche "Füßler" in die Datenbank zu schreiben. Dazu muß natürlich der Wert für den neuen Eintrag beim Benutzer mit Hilfe eines Formulars abgefragt werden. Das Formular wird dabei zwei Dinge abfragen:

1. Die Sorte des neuen "Füßlers" (Feldname sorte mit einem Wertebereich von 1-5).

2. Die Bezeichnung des neuen "Füßlers" (Feldname bezeichnung als freies Textfeld).
 

Gegenüber den bisherigen Skripten ändert sich nicht allzu viel. Die vom Formular übergebenen Werte werden mit Hilfe des CGI.pm-Moduls in eine Variable des Skripts eingelesen.

my $sorte;

if ( defined($cgi_obj->param(`sorte')) ){

        $sorte = $cgi_obj->param(`sorte');

}

my $bezeichnung;

if ( defined($cgi_obj->param(`bezeichnung')) ){

$bezeichnung = $cgi_obj->param(`bezeichnung');

}

Die Hauptänderung betrifft den SQL-String der Abfrage. Bislang hatten wir hier immer Abfragen im eigentlichen Sinn. Der nun verwendete SQL-String ist eher als höfliche Anfrage an den Datenbankserver zu verstehen. Würde man sie in Worte fassen, so könnte man dies so versuchen:

Lieber Datenbankserver, könntest du mir in die Tabelle tbl_bsp einen neuen Datensatz mit den Werten ... eintragen?

In SQL-Syntax sieht das etwas nüchterner aus. Soll beispielsweise für die Sorte 2 (Vierbeiner) eine "Kuh" eingetragen werden, so ist das in SQL folgendermaßen zu formulieren:

INSERT INTO tbl_bsp SET bsp_artID='2', bsp_text='Kuh';

Anstelle der beiden festen Werte (2 und Kuh) werden einfach die beiden obigen Variablen $sorte und $bezeichnung eingesetzt. Das entsprechende Statement hat damit dieses Aussehen:

my $sql = "INSERT INTO tbl_bsp SET bsp_artID='$sorte', bsp_text='$bezeichnung'";

my $sth = $dbh->prepare( $sql ) ||

        die "Kann Statement nicht vorbereiten: $DBI::errstr\n";

Das Ganze wird noch mit ein bißchen Ausgabekosmetik versehen.

Bei Verwendung des obigen SQL-Statements kann es jedoch noch gravierende Probleme geben. Stellen Sie sich einfach vor, jemand versucht über das Formular den Begriff "Has'" einzugeben. Beachten Sie hierbei das einzelne Hochkomma an der schwäbischen Bezeichnung für einen "Hase" :-). Wenn Sie nun diesen Begriff in Ihren SQL-Befehl einfügen, dann sieht der so aus:

INSERT INTO tbl_bsp SET bsp_artID='2', bsp_text='Has'';

Am Ende des Befehls sehen Sie die zwei aufeinander folgenden Hochkommas. Das eine Hochkomma gehört zum Begriff und soll in die Datenbank übernommen werden. Das zweite Hochkomma schließt den Sollinhalt des Datenfelds ab. Leider ist das aber ein SQL-Syntaxfehler und das bisherige Erfassungsskript stürzt hier mit einem Fehler ab. Je nach Datenbank und SQL-Know-How des "Benutzers" ist er auf diese Art und Weise sogar in der Lage Ihren SQL-Befehl so abzuändern, dass er ganz andere Dinge tut! In diesem Fall wird der "Benutzer" ganz einfach zum "Angreifer" :-(! Deshalb sollten sie DRINGEND alle Parameter, die Ihnen von Außen in Ihr Skript gereicht werden, nach Zeichen "abklopfen" die solche Effekte erzielen könnten (' " % * ...). Diesen Zeichen ist dann z.B. in MySQL ein \ (Backslash) voranzustellen! Beispiel: \'. Netterweise liefert das DBI-Modul bereits eine entsprechende Funktion! Sie heißt $dbh->quote() und

- ersetzt alle Sonderzeichen durch ihre ungefährliche Variante

- und umgibt den ganzen Begriff gleich mit den notwendigen Hochkommas.

Sie sollten prinzipiell jeden von Außen gelieferten Parameter auf diese Weise quoten. Diese Aufforderung betrifft auch Parameter, die Sie in Ihrem Formular fest vorgegeben haben oder die sogar Hidden-Felder sind. Es gibt nämlich auch die Möglichkeit, Ihrem Skript die Daten unter Umgehung Ihres Formulars "unterzuschieben". Dabei lassen sich dann beliebige Daten an das Skript schicken. Achtung: Das gilt auch wenn Sie nur die POST-Methode zulassen und zusätzlich den Referer auswerten!

Das Quoten könnte beispielsweise so in unser bisheriges Skript eingefügt werden:

my $bezeichnung = $dbh->quote($bezeichnung);

my $sorte = $dbh->quote($sorte);

my $sql = "INSERT INTO tbl_bsp SET bsp_artID=$sorte, bsp_text=$bezeichnung";

my $sth = $dbh->prepare( $sql ) ||

        die "Kann Statement nicht vorbereiten: $DBI::errstr\n";

Beachten Sie, dass im SQL-Befehl die Hochkommas um die beiden Variablen $sorte und $bezeichnung entfernt wurden. Die Hochkommas befinden sich ja, wie bereits erwähnt, im Variableninhalt! Dieses korrigierte Skript kann dann auch mit Hochkommas umgehen.

Quellenverzeichnis

[1]   Perl-CGI

[2]   Übersicht zu verfügbaren DBDs

[3]   Hinweise zum Zugriff auf Microsoft-SQL-Server und Micro- soft Access via DBD::ODBC, DBD::Proxy, ...