Home - Impressum - E-Mail


2007-04-03

Bestaetigung von Umsatzsteuer-Identifikationsnummern per XML-RPC-Schnittstelle

Bereits seit einigen Jahren ist es deutschen Unternehmern moeglich, Umsatzsteuer-Identifikationsnummern ihrer auslaendischen Geschaeftspartner per Internet bestaetigen zu lassen. Dieser Service wurde anfangs vom Bundesamt für Finanzen (BfF) angeboten und wird seit dessen Aufloesung vom Bundeszentralamt fuer Steuern (BZSt) fortgefuehrt. Grundsaetzlich war die Bestaetigung einer Umsatzsteuer-Identifikationsnummer (USt-ID) dabei immer per Online-Formular moeglich. Zur Ermoeglichung halb- oder vollautomatischer Abfragen wurde aber stets auch eine Schnittstelle angeboten, mit deren Hilfe die USt-ID-Pruefung in eigene Software eingebunden werden konnte. Denkbare Anwendungsbereiche sind etwa Fakturierungssoftware oder Online-Shops.

Aus WDDX wird XML-RPC ...

Urspruenglich erfolgte die Bestaetigung ueber eine WDDX-Schnittstelle, fuer deren Nutzung mit PHP ein Beispiel-Script verfuegbar ist (cURL wird benoetigt). Doch das Ende der WDDX-Schnittstelle steht bevor: Laut BZSt haette die Abschaltung bereits zum 01. April 2007 erfolgen sollen. Bei einem kurzen Test am Folgetag konnte ich zwar noch keine Funktionseinschraenkung feststellen, dennoch ist es sicher ratsam, auf die neue XML-RPC-Schnittstelle umzusteigen.

... und aus PHP wird Python

Auch fuer die XML-RPC-Schnittstelle stellt das BZSt ein Anwendungsbeispiel bereit, diesmal allerdings nicht mehr fuer PHP, sondern fuer Python. Natuerlich kann auch eine XML-RPC-Schnittstelle per PHP angesprochen werden, und so scheint mit dieser Wechsel der von BfF/BZSt favorisierten Skriptsprache doch bemerkenswert. Nachteile sehe ich bei einer Python-Implementierung abgesehen von den damit leicht gestiegenen Systemanforderungen keine, denn auch aus bestehenden PHP-Anwendungen heraus laesst sich ein Python-Script meist problemlos nutzen.

Adressverifikation als Gratis-Bonbon

Bei der Anfrage an die Schnittstelle wird zunaechst die eigene USt-ID und dann die USt-ID des Kunden uebergeben. Zusaetzlich koennen Firmenname, Ort, Postleitzahl und Strasse des Kunden angegeben werden. Waehrend fuer eine einfache Bestaetigung nur die beiden USt-IDs erforderlich sind, muessen fuer eine qualifizierte Bestaetigung zwingend Firmenname und Ort genannt werden. Die Angabe von Postleitzahl und Strasse sind in beiden Faellen freibleibend (siehe dazu auch: Parametertabelle beim BZSt). Sofern verfuegbar, spricht aber nichts dagegen, diese Informationen ebenfalls zu uebermitteln: Die Software des BZSt prueft naemlich jeden einzelnen Parameter auf Korrektheit und kann damit "nebenbei" als kostenfreies Adressverifikationssystem genutzt werden. Zuletzt wird angegeben, ob eine "amtliche Bestaetigungsmitteilung" erstellt werden soll.

Schnittstelle ansprechen und Antwort verarbeiten

Nachfolgend eine von mir erweiterte Version des BZSt-Beispielscripts, welches mit den Parametern in der oben genannten Reihenfolge aufgerufen wird, daraufhin mit der XML-RPC-Schnittstelle kommuniziert und abschliessend den Rueckgabewert "ErrorCode" ausgibt:

#!/usr/bin/python2.5
import sys
import xmlrpclib

uid1, uid2, firma, ort, plz, strasse, druck = '', '', '', '', '', '', 'nein'

if len(sys.argv) > 1:
  uid1      = sys.argv[1]
if len(sys.argv) > 2:
  uid2      = sys.argv[2]
if len(sys.argv) > 3:
  firma     = sys.argv[3]
if len(sys.argv) > 4:
  ort       = sys.argv[4]
if len(sys.argv) > 5:
  plz       = sys.argv[5]
if len(sys.argv) > 6:
  strasse   = sys.argv[6]
if len(sys.argv) > 7:
  if sys.argv[7] == '1':
    druck   = 'ja'

server_url = 'http://evatr.bff-online.de/'
server = xmlrpclib.Server(server_url)
rpc = server.evatrRPC(uid1, uid2, firma, ort, plz, strasse, druck)

rpc = xmlrpclib.loads(rpc)
rpc = rpc[0]
t = {}
for item in rpc:
  t[item[0]] = item[1]

print t['ErrorCode']

Implementierung in bestehende PHP-Scripte

Eine Nutzung der Funktionalitaet dieses Python-Scripts aus einem PHP-Script heraus ist recht einfach moeglich, indem das PHP-Script das Python-Script mit den entsprechenden Parametern aufruft, den vom Python-Script zurueckgegebenen "ErrorCode" empfaengt und diesen dann so verarbeitet, wie es dies frueher bereits mit dem ueber die WDDX-Schnittstelle bezogenen Rueckgabewert tun konnte:


Ein PHP-Beispiel:

$ustid_result = exec('/pfad/zum/pythonscript.py  ');

if($ustid_result == "200")
{
  // USt-ID als gueltig bestaetigt
}
elseif($ustid_result == "206")
{
  // Eigene USt-ID ungueltig
}
// [...] - auf sonstige Rueckgabewerte reagieren
else
{
  // Sonstiger Fehler
  echo $ustid_result;
}

Als Anmerkung zu Zeile 11: Eine Liste aller dokumentierten Rueckgabewerte ist hier abrufbar.

Wichtig: Erfolgt der Aufruf der exec()-Funktion nicht mit statischen oder aus absolut vertrauenswuerdiger Quelle stammenden USt-IDs, sondern mit solchen, die ihrerseits durch Dritte nach Belieben gesetzt werden können, so ist eine Pruefung dieser Parameter aus Sicherheitsgruenden unerlaesslich! Anderenfalls wird Angreifern unter Umstaenden ermoeglicht, beliebige Befehle auszufuehren.

Nehmen wir beispielsweise an, unser PHP-Script bestehe aus folgendem Zweizeiler:

$ustid_result = exec('/pfad/zum/pythonscript.py DE123456789 '.$_GET['ustid']);
echo $ustid_result;

Unser PHP-Script koennte etwa per

http://example.org/ustid/phpscript.php?ustid=ATU12345678
aufgerufen werden und wuerde erwartungsgemaess den empfangenen "ErrorCode" des BZSt anzeigen.

An diesen Aufruf koennten nun aber weitere Befehle angehaengt werden, die von unserem PHP-Script an exec() zur Ausfuehrung uebergeben wuerden. Waehrend ein Aufruf per

http://example.org/ustid/phpscript.php?ustid=ATU12345678echo%20TEST
etwa "nur" zur Ausfuehrung von echo mit dem Parameter "TEST" fuehrt, kann ein Angreifer auf diese Weise grosse Schaeden anrichten. Zwar ist es auch von der jeweiligen Systemkonfiguration (Stichwort: Safe Mode) abhaengig, ob dieses Problem ein rein theoretisches bleibt. Da eine Entschaerfung der Situation jedoch vergleichsweise einfach durch Nutzung der PHP-Funktionen escapeshellarg() und escapeshellcmd() zu erreichen ist, sollten diese Moeglichkeiten unbedingt genutzt werden:|

$ustid = escapeshellcmd($ustid);
$ustid = escapeshellarg($ustid);
$ustid_result = exec('/pfad/zum/pythonscript.py DE123456789 '.$ustid);
echo $ustid_result;

Zu beachten ist ausserdem, dass die Shebang-Zeile ("#!/usr/bin/python2.5") des Python-Scripts gegebenenfalls noch an den systemspezifischen Pfad zum Python-Interpreter angepasst werden muss.

Doch zurueck zum Python-Script. Natuerlich enthaelt das Python-dictionary "t" mehr als nur den Rueckgabewert "ErrorCode", der dann in Zeile 33 ausgegeben wird. Eine Uebersicht ueber alle enthaltenen Eintraege und deren Bedeutung findet sich hier. So laesst sich dann etwa das angesprochene Adressverifikationssystem durch Auslesen der Eintraege "Erg_Name", "Erg_Ort", "Erg_PLZ" und "Erg_Str" nutzen. Die im letzten Satz verlinkte Seite enthaelt dazu Beschreibungen der in diesen Faellen moeglichen Rueckgabewerte "A", "B", "C" und "D".

Links zum Thema:

- USt-ID-Bestaetigungsverfahren des BZSt
- XML-RPC-Schnittstelle des BZSt
- Parametertabelle beim BZSt
- Uebersicht aller Rueckgabewerte der XML-RPC-Schnittstelle
- Beschreibungen aller "ErrorCode"-Werte


Home - Impressum - E-Mail