From b76d883fbede75f3f45f1273375e2f4b0b8d0f5a Mon Sep 17 00:00:00 2001 From: Moirtz Wagner Date: Thu, 17 Sep 2026 11:18:29 +0200 Subject: [PATCH] Rechnungen: Betraege mit Tausenderpunkt, XSS in Templates und Suche - formularZahl(): steht ein Komma im Betrag, sind Punkte Tausendertrenner ("1.234,50" -> 1234.50, vorher 1.23); ohne Komma bleibt der Punkt Dezimaltrenner wie bei der Artikel-Autovervollstaendigung ("12.50"). - Rechnungs-Templates (addrechnung, mailrechnung, changeadr, copyrech, umschlagshow, rechshow) geben alle Daten mit |escape aus; Werte aus der URL (id, type, newtype) als int. - Rechnungssuche: Artikelnamen werden escaped statt als HTML ausgegeben (Trennung per Zeilenumbruch in SQL, artikelListeHtml() in PHP). - Suchbegriffe kommen per encodeURIComponent als UTF-8, utf8_encode() entfaellt; vorher gingen Zeichen ausserhalb von Latin-1 verloren. Co-Authored-By: Claude Opus 5 --- ajax/searchkdnrech.php | 9 +- ajax/searchrech.php | 9 +- incs/listfuncs.php | 14 +- incs/rechnung.php | 11 +- js/ajaxlists.js | 4 +- sites/rechnungadd.php | 10 +- templates/addrechnung.tpl | 256 ++++++++++++++++++------------------- templates/changeadr.tpl | 6 +- templates/copyrech.tpl | 6 +- templates/mailrechnung.tpl | 64 +++++----- templates/rechshow.tpl | 4 +- templates/umschlagshow.tpl | 32 ++--- 12 files changed, 219 insertions(+), 206 deletions(-) diff --git a/ajax/searchkdnrech.php b/ajax/searchkdnrech.php index d976098..64b5897 100644 --- a/ajax/searchkdnrech.php +++ b/ajax/searchkdnrech.php @@ -16,7 +16,8 @@ if(!isset($err)) if(!isset($_GET["link"])){ $_GET["link"] =""; } - $fulltag = utf8_encode($_GET["search"]); + // kommt per encodeURIComponent bereits als UTF-8 + $fulltag = isset($_GET["search"]) && is_string($_GET["search"]) ? $_GET["search"] : ""; $src = explode(" ",$fulltag); $combiner = $_GET["link"]; if($combiner != "AND" && $combiner != "OR") @@ -87,14 +88,14 @@ if(!isset($err)) if($res["storno"] == 1){ $listlines[] = array("".$res["drechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), $actions ); } else{ $listlines[] = array("".$res["drechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), $actions ); } @@ -151,7 +152,7 @@ if(!isset($err)) { $speciallines[] = array("".$res["rechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), ""); } $headings = array("Nummer","Datum","Artikel","Aktion"); diff --git a/ajax/searchrech.php b/ajax/searchrech.php index 91abca1..5b8b938 100644 --- a/ajax/searchrech.php +++ b/ajax/searchrech.php @@ -22,7 +22,8 @@ if(!isset($err)) if(!isset($_GET["kdnid"])){ $_GET["kdnid"]=""; } - $fulltag = utf8_encode($_GET["search"]); + // kommt per encodeURIComponent bereits als UTF-8 + $fulltag = isset($_GET["search"]) && is_string($_GET["search"]) ? $_GET["search"] : ""; $src = explode(" ",$fulltag); $combiner = $_GET["link"]; if($combiner != "AND" && $combiner != "OR") @@ -82,14 +83,14 @@ if(!isset($err)) if($res["storno"] == 1){ $listlines[] = array("".$res["drechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), $actions ); } else{ $listlines[] = array("".$res["drechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), $actions ); } @@ -142,7 +143,7 @@ if(!isset($err)) { $speciallines[] = array("".$res["rechnr"]."", $res["date"], - $res["artikel"], + artikelListeHtml($res["artikel"]), ""); } $headings = array("Nummer","Datum","Artikel","Aktion"); diff --git a/incs/listfuncs.php b/incs/listfuncs.php index 7cc7ce5..30d2fe7 100644 --- a/incs/listfuncs.php +++ b/incs/listfuncs.php @@ -195,7 +195,7 @@ function createKdnRechQry($page,$pagesize,$src,$combiner,$id,$typ,$sks=0) $query = "SELECT rechnung.$docnr AS rechnr, rechnung.id, DATE_FORMAT(rechnung.datetime, '%d.%m.%Y') AS date, - GROUP_CONCAT(CONCAT(artikel.bezeichn, ' (', artikel_rechn.wanr, ')') SEPARATOR '
') AS artikel, + GROUP_CONCAT(CONCAT(artikel.bezeichn, ' (', artikel_rechn.wanr, ')') SEPARATOR '\n') AS artikel, adresse_id, storno, rechnung.typ_id @@ -216,7 +216,7 @@ function createKdnRechQry($page,$pagesize,$src,$combiner,$id,$typ,$sks=0) $query = "SELECT rechnung.$docnr AS rechnr, rechnung.id, DATE_FORMAT(rechnung.datetime, '%d.%m.%Y') AS date, - GROUP_CONCAT(CONCAT(artikelsks.bezeichn1, ' ', artikelsks.bezeichn2) SEPARATOR '
') AS artikel, + GROUP_CONCAT(CONCAT(artikelsks.bezeichn1, ' ', artikelsks.bezeichn2) SEPARATOR '\n') AS artikel, adresse_id, rechnung.typ_id FROM rechnung @@ -304,7 +304,7 @@ function createRechQry($page,$pagesize,$src,$combiner,$sks=0) rechnung.$docnr AS rechnr, rechnung.id, DATE_FORMAT(rechnung.datetime, '%d.%m.%Y') AS date, - GROUP_CONCAT(CONCAT(artikel.bezeichn, ' (', artikel_rechn.wanr, ')') SEPARATOR '
') AS artikel, + GROUP_CONCAT(CONCAT(artikel.bezeichn, ' (', artikel_rechn.wanr, ')') SEPARATOR '\n') AS artikel, adresse_id, storno, rechnung.typ_id @@ -328,7 +328,7 @@ function createRechQry($page,$pagesize,$src,$combiner,$sks=0) rechnung.$docnr AS rechnr, rechnung.id, DATE_FORMAT(rechnung.datetime, '%d.%m.%Y') AS date, - GROUP_CONCAT(CONCAT(artikelsks.bezeichn1, ' ', artikelsks.bezeichn2) SEPARATOR '
') AS artikel, + GROUP_CONCAT(CONCAT(artikelsks.bezeichn1, ' ', artikelsks.bezeichn2) SEPARATOR '\n') AS artikel, adresse_id, storno = 0, rechnung.typ_id @@ -461,6 +461,12 @@ function createMsgQry($page,$pagesize,$src,$combiner,$user) return $ret; } +/* Artikelliste aus createRechQry()/createKdnRechQry() als escaptes HTML, eine Zeile je Artikel */ +function artikelListeHtml($artikel) +{ + return implode("
", array_map("htmlspecialchars", explode("\n", (string)$artikel))); +} + function displayPages($pagesize,$currpage,$numentries,$refreshfunc,$ad_params = "") { echo "Seite: "; diff --git a/incs/rechnung.php b/incs/rechnung.php index 4a32ea9..9dcebc8 100644 --- a/incs/rechnung.php +++ b/incs/rechnung.php @@ -12,12 +12,17 @@ function formularText($name) } /* - * Zahl aus $_POST, Komma als Dezimaltrenner. Leere oder ungültige Eingaben - * werden 0 - so hat sie der frühere, nicht strikte Modus gespeichert. + * Zahl aus $_POST in deutscher Schreibweise. Steht ein Komma darin, ist es + * der Dezimaltrenner und Punkte sind Tausendertrenner ("1.234,50"). Ohne + * Komma gilt ein Punkt als Dezimaltrenner ("12.50", so liefert ihn die + * Artikel-Autovervollständigung). Leere oder ungültige Eingaben werden 0 - + * so hat sie der frühere, nicht strikte Modus gespeichert. */ function formularZahl($name) { - $wert = str_replace(",", ".", formularText($name)); + $wert = str_replace(" ", "", formularText($name)); + if(str_contains($wert, ",")) + $wert = str_replace(array(".", ","), array("", "."), $wert); return is_numeric($wert) ? $wert : "0"; } diff --git a/js/ajaxlists.js b/js/ajaxlists.js index c6bef86..cc5826a 100644 --- a/js/ajaxlists.js +++ b/js/ajaxlists.js @@ -146,7 +146,7 @@ function ajax_searchkdnrech(page,skspage,typ,kdnid) search_in_progress = 1; document.getElementById("rechlist-"+typ).innerHTML = "

"; //document.getElementById("kdnsrc_src").disabled = true; - search = escape(document.getElementById("src_rech-"+typ).value); + search = encodeURIComponent(document.getElementById("src_rech-"+typ).value); var url = "ajax/searchkdnrech.php?page=" + page + "&skspage=" + skspage + "&search=" + search+"&typ=" + typ + "&kdnid=" + kdnid; @@ -200,7 +200,7 @@ function ajax_searchrech(page,skspage) search_in_progress = 1; document.getElementById("rechlist").innerHTML = "

"; //document.getElementById("kdnsrc_src").disabled = true; - search = escape(document.getElementById("src_rech").value); + search = encodeURIComponent(document.getElementById("src_rech").value); var url = "ajax/searchrech.php?page=" + page + "&skspage=" + skspage + "&search=" + search; diff --git a/sites/rechnungadd.php b/sites/rechnungadd.php index 58bcc2b..83f03f1 100644 --- a/sites/rechnungadd.php +++ b/sites/rechnungadd.php @@ -17,7 +17,7 @@ elseif($_GET["changeadr"] == 1 && $_GET["id"]) if(!$_GET["adrid"]) { $smarty->assign("felder",array("all"=>"Name, Adresse, Kd.Nr.","kdnr"=>"Kundennummer","name"=>"Name und Firma","str"=>"Straße","plz"=>"PLZ","ort"=>"Ort","tel"=>"Tel","kont"=>"Kontakt")); - $smarty->assign("rechid",$_GET["id"]); + $smarty->assign("rechid",(int)$_GET["id"]); $smarty->display("changeadr.tpl"); } else @@ -32,7 +32,7 @@ elseif($_GET["copy"] == 1 && $_GET["id"]) if(!$_GET["adrid"]) { $smarty->assign("felder",array("all"=>"Name, Adresse, Kd.Nr.","kdnr"=>"Kundennummer","name"=>"Name und Firma","str"=>"Straße","plz"=>"PLZ","ort"=>"Ort","tel"=>"Tel","kont"=>"Kontakt")); - $smarty->assign("rechid",$_GET["id"]); + $smarty->assign("rechid",(int)$_GET["id"]); $smarty->display("copyrech.tpl"); } else @@ -141,7 +141,7 @@ else } else { - $smarty->assign("loadrech",$_GET["id"]); + $smarty->assign("loadrech",(int)$_GET["id"]); $rechnung=makerechnungsarray($dbconn, $_GET["id"], 0); $kommission=makeadressarray($dbconn, $rechnung["kommission"]); $smarty->assign("rechnung",$rechnung); @@ -156,8 +156,8 @@ else $smarty->assign("artikel",makeartikelarray($dbconn, $_GET["draft"], 1)); } - $smarty->assign("type",$_GET["type"]); - $smarty->assign("newtype",$_GET["newtype"]); + $smarty->assign("type",(int)$_GET["type"]); + $smarty->assign("newtype",(int)$_GET["newtype"]); $kdnres=makeadressarray($dbconn, $_GET["adressid"]); diff --git a/templates/addrechnung.tpl b/templates/addrechnung.tpl index 8b84bfa..11a24b0 100644 --- a/templates/addrechnung.tpl +++ b/templates/addrechnung.tpl @@ -23,72 +23,72 @@ {elseif $type == 6} {$typename="Mahnung"} {/if} -

{$typename} {$headstr}:

-
+

{$typename|escape} {$headstr|escape}:

+
-{$typename} an Kunde {$adresse.partner_id} +{$typename|escape} an Kunde {$adresse.partner_id|escape} {if $adresse.firma} - + {/if} {if $adresse.anrede_id > 1} - + {/if} {if $adresse.titel_id > 1} - + {/if} {if $adresse.vname || $adresse.nname} - + {/if}
{if $adresse.anrede2_id > 1} - + {/if} {if $adresse.titel2_id > 1} - + {/if} {if $adresse.vname2 || $adresse.nname2} - + {/if} {if $adresse.str} - + {/if} {if $adresse.plz || $adresse.ort} -
+

-
-
+
+
{$eignr = 0} {foreach $art.eig as $eigensch} {$eignr = $eignr+1} @@ -295,12 +295,12 @@
{else}
- + {/if} - - - + + {/foreach} {$eignr = $eignr+1} @@ -308,50 +308,50 @@
{else}
- + {/if} - - - + +


- + - + {html_options values=$artmwstids output=$artmwsts selected="{$art.art.mwst_id|default:1|escape}"}


- - +

-
+

- % - + % +
{else} + diff --git a/templates/copyrech.tpl b/templates/copyrech.tpl index b320852..1f1fea6 100644 --- a/templates/copyrech.tpl +++ b/templates/copyrech.tpl @@ -3,8 +3,8 @@
Neue Adresse:
-{html_options name=adrsrc_field onChange='ajax_searchadress({$rechid},\'copy\',0)' id=adrsrc_field options=$felder selected=$selfeld class='searchform' title='Bitte wählen Sie die Spalte, die durchsucht werden soll.' onchange='#'} - +{html_options name=adrsrc_field onChange='ajax_searchadress({$rechid|escape},\'copy\',0)' id=adrsrc_field options=$felder selected=$selfeld class='searchform' title='Bitte wählen Sie die Spalte, die durchsucht werden soll.' onchange='#'} +
@@ -14,4 +14,4 @@
- + diff --git a/templates/mailrechnung.tpl b/templates/mailrechnung.tpl index 3cc7275..5dba58f 100644 --- a/templates/mailrechnung.tpl +++ b/templates/mailrechnung.tpl @@ -15,11 +15,11 @@ {elseif $type == 5} {$typename="Brief"} {/if} -

{$typename} {$headstr}:

-
+

{$typename|escape} {$headstr|escape}:

+
-{$typename} an Kunde {$adresse.partner_id} +{$typename|escape} an Kunde {$adresse.partner_id|escape}


@@ -28,9 +28,9 @@ {html_options values=$mails output=$mails}
-
+
-{if $anrs.{$adresse.anrede_id} == "Herrn" || $anrs.{$adresse.anrede_id} == "Herr"} +{if $anrs.{$adresse.anrede_id|escape} == "Herrn" || $anrs.{$adresse.anrede_id|escape} == "Herr"} {strip}
{/strip} -{elseif $anrs.{$adresse.anrede2_id} == "Herrn" || $anrs.{$adresse.anrede_id} == "Herr"} +{elseif $anrs.{$adresse.anrede2_id|escape} == "Herrn" || $anrs.{$adresse.anrede_id|escape} == "Herr"} {strip}
{/strip} -{elseif $anrs.{$adresse.anrede_id} == "Frau"} +{elseif $anrs.{$adresse.anrede_id|escape} == "Frau"} {strip}
{/strip} -{elseif $anrs.{$adresse.anrede2_id} == "Frau"} +{elseif $anrs.{$adresse.anrede2_id|escape} == "Frau"} {strip}
{/strip} @@ -103,7 +103,7 @@ {/if} -{if $anrs.{$adresse.anrede_id} == "Frau" && $anrs.{$adresse.anrede2_id} == "Herr" && $anrs.{$adresse.anrede2_id} == "Herrn"} +{if $anrs.{$adresse.anrede_id|escape} == "Frau" && $anrs.{$adresse.anrede2_id|escape} == "Herr" && $anrs.{$adresse.anrede2_id|escape} == "Herrn"} {strip}
{/strip} -{elseif $anrs.{$adresse.anrede2_id} == "Frau"} +{elseif $anrs.{$adresse.anrede2_id|escape} == "Frau"} {strip}
{/strip} @@ -142,11 +142,11 @@ {/if}