Zum Inhalt

Modul: Mopedkennzeichen-Bestellung

Stand

Code-Analyse vom 2026-04-22 gegen sachpool.de, WebEdition 9.2.3, PHP 8.3.

Zweck

Vermittler können über das Portal Versicherungskennzeichen für Kleinkrafträder bestellen (Mopeds, Leichtkraftfahrzeuge, E-Bikes >25 km/h, Krankenfahrstühle, Elektrokleinstfahrzeuge). Das Modul:

  • Zeigt verfügbare Kennzeichen-Nummern aus dem Lager
  • Nimmt Kundendaten + Fahrzeugdaten entgegen
  • Versendet Bestätigungs-Mails (Kunde + Sachpool-Backoffice)
  • Reserviert das Kennzeichen im System

Template-Struktur

sachpool-portal/kennzeichen/
├── frontend/
│   ├── uebersicht-verfuegbare-kennzeichen.tmpl (224)  Liste: verfügbare Kennzeichen
│   ├── kennzeichen-abwicklung.tmpl             (225)  Bestellformular + Abwicklung
│   └── inc/                                          Include-Bausteine
├── backend/
│   └── kennzeichen-uebersicht.tmpl             (238)  Admin-Lagerverwaltung
└── mails/
    ├── kunden-email.tmpl                       (247)  Bestätigung an Kunde
    └── interne-email.tmpl                      (248)  Benachrichtigung an Sachpool

Einstiegspunkte

Doc-ID Pfad Template
39515 /_login/service/kennzeichen/bestellen-mopedkennzeichnen.php 224 + 225
39516 /_login/service/kennzeichen/abwicklung.php 225

Datenmodell

Tabelle Mopedschilder

Alle Kennzeichen aus dem Lager. Aus Queries ableitbare Felder:

Feld Typ Rolle
id int PK Laufende Nummer
bezeichnung varchar(30) Kennzeichentext (z. B. SHI 450, Upper-Case)
bestellstatus int 0 frei / 1 reserviert / 2 bezahlt
kunden_id int FK auf WebEdition-Kunde (User-Makler-ID), 0 = frei
bestell_id int FK auf Moped_bestellungen.id, 0 = keine Bestellung
bestell_datum datetime Zeitstempel der Reservierung
haftpflicht_kasko varchar Deckungstyp (z. B. „Haftpflicht", „Haftpflicht + Teilkasko")
notiz text Admin-Notiz (Freitext)
eingepflegt_am datetime Erfassungs-Zeitpunkt
eingepflegt_von_userid int Backend-User der Erfassung

Tabelle Moped_bestellungen

Kundendaten pro Bestellung. Aus Queries ableitbare Felder:

Feld Inhalt
id, schild_id, besteller_id, makler_kennung, besteller_email Referenzen
anrede, vorname, nachname Kunde
strasse, plz, ort, geburtsdatum Kundenadresse
erstzulassung, fahrzeug_hersteller, hersteller_schluessel_nr, fahrzeug_idz_nr Fahrzeugdaten (FIN)
deckung, beginn, beitrag Versicherungsdetails
moped_art, gewicht, staerke Fahrzeugtyp
bestelldatum Zeitstempel

Frontend-Workflow

1. Verfügbare Kennzeichen (uebersicht-verfuegbare-kennzeichen.tmpl, 224)

Auswahl Blechschild vs Plakette (Radio-Button, filtert per bezeichnung LIKE '%PLAKETTE%'):

SELECT * FROM Mopedschilder
WHERE bestellstatus=0 AND kunden_id=0
  AND bezeichnung LIKE '%PLAKETTE%'  -- oder NOT LIKE

Darstellung als tablesorter-Tabelle (Filter nach Versicherer möglich — „Zum Filtern R+V oder Zurich eingeben"). Jede Zeile hat „Bestellen"-Button, führt per POST mit kennzeichenid=<id> zum Abwicklungsformular.

2. Bestellabwicklung (kennzeichen-abwicklung.tmpl, 225)

~600 Zeilen großes Formular mit:

  • Hidden-Felder für WebEdition-FormMail (crkpfpg=1, sec_eingabe=1)
  • Deckungs-Radios: Haftpflicht / Haftpflicht + Teilkasko 0 € SB / Haftpflicht + TK 150 € SB
  • Beginn (Tag/Monat/Jahr Dropdowns, Jahr date('Y') bis +4)
  • Beitrag (Pattern \d+(,\d{2})?)
  • 18 Fahrzeugtyp-Radios (Moped/Mokick/Roller 45/50/60 km/h, Microcar, Quad, Krankenfahrstuhl diverse, Leichtmofa, Mofa, Kleinkraftrad, Elektrokleinstfahrzeug)
  • Gewicht (kg), Stärke (kW)
  • Personendaten (Anrede, Vor-/Nachname, Adresse, Geburtsdatum)
  • Fahrer unter/ab 23 (Tarifierung)
  • Fahrzeugdaten: Hersteller, HSN (Herstellerschlüsselnummer), FIN (Fahrzeug-Identifikationsnummer), Erstzulassung
  • 3 Pflicht-Checkboxen: Elterneinverständnis (<18), Betriebserlaubnis (ABE), VVG-Informationspflichten

Verarbeitung durch WebEdition-FormMail-Engine

Das Template ist kein Verarbeitungs-Endpoint. Die Mail-Versand- und DB-Update-Logik übernimmt die WebEdition-eigene FormMail-Pipeline (getriggert durch crkpfpg=1 + sec_eingabe=1). Die eigentliche Speicherung erfolgt im eingebundenen Template 245 (abwicklung-formulardaten.tmpl).

2a. Speicherung und Reservierung (abwicklung-formulardaten.tmpl, 245)

Template 245 wird von 225 eingebunden und übernimmt die eigentliche DB-Mutation nach erfolgreicher Formular-Validierung. Wesentliche Schritte in Reihenfolge:

1. Bestellung in Moped_bestellungen speichern:

$sql_bestellunginsert = "INSERT INTO Moped_bestellungen
   (Anrede, Vorname, Nachname, Strasse, Plz, Ort, Geburtsdatum,
    Erstzulassung, Fahrzeug_Hersteller, Hersteller_Schluessel_Nr, Fahrzeug_Identfiz_Nr,
    Deckung, Beginn, Beitrag, Moped_Art, Gewicht, Staerke, ...)
   VALUES ('".$_POST["Anrede"]."','".$_POST["Vorname"]."', ...)";
$wedb->query($sql_bestellunginsert);
$bestell_id = $wedb->insert_id;

SQL-Konkatenation aller POST-Werte ohne Escape. Auch die Gesellschaftsauswahl (R+V vs Zurich), die Beitragsberechnung und der Deckungs-Radio-Wert fließen hier roh in das Statement.

2. Reservierung in Mopedschilder:

$sql_kennzeichenupdate = "UPDATE Mopedschilder SET
  bestellstatus = 1,
  kunden_id    = '$sach_userid',
  bestell_id   = '$bestell_id',
  bestell_datum = NOW()
  WHERE id = '".$_POST["kennzeichenid"]."'";

3. Mail-Versand — doppelt per <we:sendMail>:

<we:sendMail from="webmail@der-sachpool.de"
             recipient="$internalrecipients"
             id="40153"    <!-- interne-email.tmpl (248) -->
             subject="Mopedkennzeichen-Bestellung VM $sach_user" />

<we:sendMail from="webmail@der-sachpool.de"
             recipient="$sach_mail"
             id="40152"    <!-- kunden-email.tmpl (247) -->
             subject="Ihre Mopedkennzeichen-Bestellung" />

SQL-Injection am Bestell-Endpoint

Das Template konkateniert ~30 POST-Felder direkt in SQL-Statements. Ein Angreifer kann über beliebige Form-Felder SQL einschleusen (Stored SQLi, Datenexfiltration). Priorität kritisch — siehe HANDLUNGSEMPFEHLUNGEN.md.

3. E-Mail-Versand

Nach erfolgreichem Submit werden zwei Mails generiert (Templates 247 + 248):

  • kunden-email.tmpl an den Kunden: Bestellbestätigung mit allen Daten + hartkodierte Sachpool-Bankverbindung (IBAN DE15 1203 0000 0011 4155 02 DKB Chemnitz). 14-Tage-Zahlungsfrist, sonst Freigabe.
  • interne-email.tmpl an Sachpool-Backoffice: VM-Bestätigungsformel (VVG §7)
  • alle Daten inkl. VM-Nummer.

Beide Templates geben $_POST-Werte ungeescaped ins HTML aus — siehe HANDLUNGSEMPFEHLUNGEN.md.

Backend-Lagerverwaltung (kennzeichen-uebersicht.tmpl, 238)

Vier Ansichten (Toolbar-Buttons, currentview 1–4):

  1. Alle (ORDER BY id DESC)
  2. Sortiert nach Bestelldatum (ORDER BY bestell_datum DESC)
  3. Reserviert (bestellstatus=1)
  4. Frei (bestellstatus=0)

Admin-Operationen:

  • Neues Kennzeichen erfassen (erfassen=1) — INSERT in Mopedschilder
  • Kennzeichen bearbeiten (bearbeitet=1) — UPDATE Status + Notiz
  • Kennzeichen löschen (loeschen=1) — DELETE nach Bestätigung (OK eingeben)
  • Freigabe (Status → 0) — Zuordnung aufheben (kunden_id='0', bestell_id='0')

Zusammenhang mit WebEdition-Kundenverwaltung

  • Mopedschilder.kunden_id → WebEdition-Customer (tblWebUser.id)
  • Klick auf Kunden-ID im Backend öffnet we_cmd('we_customer_edit', ...): direkter Sprung ins WebEdition-Kundenmanagement.

Status-Lebenszyklus

  ┌─────┐  Bestellung
  │  0  │ ──────────────────▶ ┌─────┐ Zahlung eingegangen ┌─────┐
  │ frei│                     │  1  │ ──────────────────▶ │  2  │
  └─────┘  ◀─────────────── reserviert                    bezahlt
            Freigabe (Admin)  └─────┘                     └─────┘

Es gibt keinen expliziten Storno- oder Rückabwicklungs-Status.

Bekannte Altlasten

→ interne Datei HANDLUNGSEMPFEHLUNGEN.md im Repo-Root

  • 🔴 Template 245 SQL-Konkatenation: ~30 POST-Felder direkt in INSERT/UPDATE ohne Escape
  • 🟡 kunden-email.tmpl + interne-email.tmpl: POST-Werte ungeescaped (HTML-Injection)
  • 🟡 Bankverbindung hartkodiert in kunden-email.tmpl
  • 🟡 Tippfehler-Feldname Fahrzeug_Identfiz_Nr (statt Identifiz)
  • 🟡 18 Radio-Inputs mit identischer ID id="Typenbezeichnung" — HTML-invalid
  • 🟡 Datums-Format-Bug date('Y-m-j h:i:s') im Backend — 12h statt 24h
  • 🟡 4× Code-Duplizierung im Backend (Ansichten 1–4)
  • 🟡 String-Nullen als „leer" statt NULL (kunden_id='0', bestell_id='0')
  • 🟢 VVG-Tippfehler VVG-Informationspflichetenverordnung

Siehe auch