Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen (https://www.delphipraxis.net/178030-xml-datei-zu-stream-%7C-stream-zu-hashwert-%7C-xml-gegen-manipulation-schuetzen.html)

Headbucket 12. Dez 2013 10:19

Delphi-Version: XE5

XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Guten Tag,

ich lese schon sehr lange hier im Forum und habe oft Antworten auf meine Fragen gefunden. Aus diesem Grund habe ich mich heute angemeldet und muss direkt mal eine erste Frage stellen.

Hintergrund:
Ich möchte XML-Dateien gegen Manipulation schützen. Diese XML-Datei enthält Messdaten, welche nicht verändert werden sollen. Die XML-Datei soll aber trotzdem lesbar bleiben. Es genügt beim öffnen der Datei eine Überprüfung, ob die Datei verändert wurde.

In einer ersten Version habe ich lediglich die Werte aller Attribute eingelesen, als Strings aneinander gehängt und daraus dann einen Hashwert bestimmt, welchen ich ebenso in der XML speicher:
Delphi-Quellcode:
procedure TForm1.SchreibeXML;
var
  xmlDoc : IXMLDocument;
  iNode: IXMLNode;
  s: string;
  Hashwert: string;
begin
  ErstelleXML;

  xmlDoc := newXMLDocument;
  XMLDoc.LoadFromFile(PATH + Dateiname);
  xmlDoc.Active := true;

  //Neue Knoten anlegen
  XMLDoc.DocumentElement.AddChild('messdaten');
  XMLDoc.DocumentElement.AddChild('hashdaten');

  //Attribute zuweisen - Messwerte
  iNode := XMLDoc.DocumentElement.ChildNodes['messdaten'].AddChild('messwerte');
  iNode.Attributes['text'] := TextEdt.Text;
  iNode.Attributes['messwert1'] := Messwert1Edt.Text;
  iNode.Attributes['messwert2'] := Messwert2Edt.Text;
  iNode.Text := '';

  s := TextEdt.Text + Messwert1Edt.Text + Messwert2Edt.Text;
  Hashwert := getMd5HashString(s);

  //Attribute zuweisen - Hashwert
  iNode := XMLDoc.DocumentElement.ChildNodes['hashdaten'].AddChild('hash');
  iNode.Attributes['wert'] := Hashwert;
  iNode.Text := '';

  //Speichern
  xmlDoc.SaveToFile(PATH + Dateiname);
Die XML-Datei sieht dann z.B. so aus:
Code:
<?xml version="1.0"?>
<Root>
   <messdaten>
      <messwerte text="Titel" messwert1="2.5" messwert2="7.6"/>
   </messdaten>
   <hashdaten>
      <hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/>
   </hashdaten>
</Root>
Beim Einlesen haben ich diesen Hashwert dann analog berechnet und ihn mit dem Wert in der XML verglichen. Das funktioniert schonmal sehr gut.

Nun möchte ich aber generelle Manipulationen in der XML-Datei erkennen können. Ich möchte somit alles bis auf den Knoten "hashdaten" hashen.
Ich scheitere jedoch schon bei der Berechnung des Hashwertes. Mein Ansatz:
Delphi-Quellcode:
procedure TForm1.SchreibeXML;
var
  xmlDoc : IXMLDocument;
  iNode: IXMLNode;
  s: string;
  Hashwert: string;
  Stream: TStream;
begin
  ErstelleXML;

  xmlDoc := newXMLDocument;
  xmlDoc.LoadFromFile(PATH + Dateiname);
  xmlDoc.Active := true;

  //Neuen Knoten anlegen - Messdaten
  xmlDoc.DocumentElement.AddChild('messdaten');

  //Attribute zuweisen - Messwerte
  iNode := XMLDoc.DocumentElement.ChildNodes['messdaten'].AddChild('messwerte');
  iNode.Attributes['text'] := TextEdt.Text;
  iNode.Attributes['messwert1'] := Messwert1Edt.Text;
  iNode.Attributes['messwert2'] := Messwert2Edt.Text;
  iNode.Text := '';

  Stream := TMemoryStream.Create;  //Stream.size = 0 (logischerweise)

  xmlDoc.SaveToStream(Stream);     //Stream.size steigt z.B. auf 132

  Hashwert := getMd5StreamToStr(Stream);
Ich möchte die XML.Datei somit z.B. erstmal in den Hauptspeicher laden (Stream), um diesen Stream dann zu hashen. Danach füge ich einen weiteren Knoten "hashdaten" mit dem Hashwert an. Leider ist der berechnete Hashwert stets der selbe, egal, welche Daten das XML enthält. Habe ich hier vllt noch einen grundsätzlichen Fehler drin? Ich leider bisher noch nie mit Streams gearbeitet.

Für die Berechnung des Hashwertes nutze ich die Indy-Komponente von Delphi: Delphi-Referenz durchsuchenTIdHash.HashStreamAsHex

Wenn dieses Problem geklärt ist bleibt noch die Frage, wie ich das ganze dann beim Einlesen bewerkstellige. Denn wenn ich die XML-Datei wieder einlese habe ich ja auch den Knoten "hashdaten" mit drin. Dieser darf natürlich bei der Berechnung des Hashwertes keine Rolle spielen.

Ich hoffe sehr, dass ich verständlich gemacht habe, wo mein Problem liegt und hoffe auf Rat von euch :-). Wahrscheinlich lassen sich beide Probleme schnell und einfach lösen.

Grüße
Headbucket

Bernhard Geyer 12. Dez 2013 10:27

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Diskussion wurde schon im Entwickler-Forum geführt

Headbucket 12. Dez 2013 10:33

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Im Entwickler-Forum ging es generell um die Frage, wie ich gegen eine Manipulation vorgehe. Diese Frage wurde dort geklärt -> Hashfunktion benutzen.

Hier geht es vorwiegend um die Frage, wie ich einen Stream hashen kann.
Ich habe diese Frage hier gestellt, da es eine Frage zu Delphi ist und mein anderer Beitrag im Entwickler-Forum in der Kategorie XML war und ich mir deshalb wenig Hilfe zu Delphi-Fragen erhofft habe.

Grüße
Headbucket

Sir Rufo 12. Dez 2013 10:36

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Erst mal als Grundsatz:

Die Daten komplett in eine Klasse (Container) schreiben.
Diesen Container dann an eine Klasse (ContainerIO) übergeben, die diesen Container speichern und lesen kann.

Jetzt kann eine Klasse (ContainerIO) die Daten aus dem Container in ein XML schreiben und berechnet aus den Daten auch den Hash. Dazu ist es nicht erforderlich die XML-Ausgabe zu hashen, sondern rein die Daten.

Am Ende fügt ContainerIO noch den ermittelten Hashwert in die XML Datei ein.

Beim Einlesen geht das quasi umgekehrt.

ContainerIO liest die Daten ein, bildet den Hashwert (wie schon vorher beim Speichern) und vergleicht diesen Hashwert mit dem aus der XML-Datei.

Stimmt der Hashwert, dann alles ok, ansonsten Exception werfen, oder was auch immer.

Guido R. 12. Dez 2013 10:43

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Zu Deinem Hashwert-Problem :
Bekommst Du etwas anderes heraus, wenn Du die Position Deines MemoryStreams vor dem ermitteln des Hash-Wertes auf 0 setzt ?
(Kann es gerade nicht selber testen)

Gruß
Guido

Headbucket 12. Dez 2013 10:55

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
@Sir Rufo
Inwiefern unterscheidet sich dieses Konzept von meiner ersten Version? Es werden ja dann wieder nur die Werte der einzelnen Attribute gehasht. Generelle Manipulationen an der Struktur der XML-Datei werden somit dann nicht bzw. durch andere Fehler sichtbar. Vllt habe ich dich aber auch falsch verstanden.

Zitat:

Zitat von Guido R. (Beitrag 1239517)
Zu Deinem Hashwert-Problem :
Bekommst Du etwas anderes heraus, wenn Du die Position Deines MemoryStreams vor dem ermitteln des Hash-Wertes auf 0 setzt ?
(Kann es gerade nicht selber testen)

Gruß
Guido

Tatsache. Wenn ich die Position vor dem Ermitteln auf 0 setze bekomme ich für unterschiedliche Werte auch unterschiedliche Hashwerte. Was hat er denn dann vorher immer gehasht? Das ist schonmal super - vielen Dank! Dann werde ich jetzt mal versuchen das Einlesen der Daten zu bewerkstelligen.

himitsu 12. Dez 2013 11:18

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Das mit dem Hash in der XML hatte ich auch mal gemacht.

Es gibt da zwei grundsätzliche Wege:

- den XML-Text als kompletten String/Stream haschen
(hierbei muß der Hash vorher durch einen "Dummy"-Wert ersetzt oder entfernt werden, da er ja mitgehasht wird)

- das XML im DOM hashen, also alle Nodes, Values und Attributes durchgehen und deren Werte haschen (dabei den Hash-Node ignorieren)
(hier ist dann die Formatierung des XML-Textes egal und man kann "unwichtige" Werte überspringen, bzw. Teile einzeln/getrennt haschen)

Bernhard Geyer 12. Dez 2013 11:27

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Zitat:

Zitat von Headbucket (Beitrag 1239511)
Im Entwickler-Forum ging es generell um die Frage, wie ich gegen eine Manipulation vorgehe. Diese Frage wurde dort geklärt -> Hashfunktion benutzen.

Nicht destotrotz ist es sinnvoll diesen Vorarbeits-Thread auch zu kennen.

Sir Rufo 12. Dez 2013 11:33

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Die Frage ist doch, welche Manipulationen man erkennen möchte:

Original
XML-Code:
<?xml version="1.0"?>
<Root>
   <messdaten>
      <messwerte text="Titel" messwert1="2.5" messwert2="7.6"/>
   </messdaten>
   <hashdaten>
      <hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/>
   </hashdaten>
</Root>
Veränderung (Formatierung) - ist egal und kann durchgewunken werden
XML-Code:
<?xml version="1.0"?>
<Root>
<messdaten><messwerte text="Titel" messwert1="2.5" messwert2="7.6"/></messdaten>
<hashdaten><hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/></hashdaten>
</Root>
Veränderung (Attributnamen) - wird erkannt
XML-Code:
<?xml version="1.0"?>
<Root>
<mAssdaten><mAsswerte tAxt="Titel" mAsswert1="2.5" mAsswert2="7.6"/></mAssdaten>
<hashdaten><hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/></hashdaten>
</Root>
Veränderung (Werte) - wird erkannt
XML-Code:
<?xml version="1.0"?>
<Root>
<messdaten><messwerte text="Titel1" messwert1="2.55" messwert2="7.65"/></messdaten>
<hashdaten><hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/></hashdaten>
</Root>
Veränderung (Metadaten anhängen) - ist für die Messwerte egal - kann durchgewunken werden
XML-Code:
<?xml version="1.0"?>
<Root>
<messdaten><messwerte text="Titel" messwert1="2.5" messwert2="7.6"/></messdaten>
<hashdaten><hash wert="4b0fbe5ec8db72fe0ffaf8e1cbc9f50f"/></hashdaten>
<metadaten><metawert Autor="Ich"/></metadaten>
</Root>
Wenn nur die Messdaten interessant sind, dann braucht man auch nur diese zu hashen, und nicht auch noch die Struktur darum, denn ohne gültige Struktur können die Daten nicht gelesen werden.

Headbucket 12. Dez 2013 12:03

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Zitat:

Zitat von himitsu (Beitrag 1239533)
Es gibt da zwei grundsätzliche Wege:

- den XML-Text als kompletten String/Stream haschen
(hierbei muß der Hash vorher durch einen "Dummy"-Wert ersetzt oder entfernt werden, da er ja mitgehasht wird)

- das XML im DOM hashen, also alle Nodes, Values und Attributes durchgehen und deren Werte haschen (dabei den Hash-Node ignorieren)
(hier ist dann die Formatierung des XML-Textes egal und man kann "unwichtige" Werte überspringen, bzw. Teile einzeln/getrennt haschen)

Genau diese Dinge werde ich wohl intern nochmal mit dem Entwicklungsleiter klären müssen. Als ich heute morgen kurz mit ihm gesprochen habe war er mit meiner ersten Version, wo nur die Werte gehasht werden nicht zufrieden und wollte alles gehasht haben. Da werde ich nochmal die Vor- und Nachteile mit ihm abklären.
Zitat:

Zitat von Bernhard Geyer (Beitrag 1239534)
Nicht destotrotz ist es sinnvoll diesen Vorarbeits-Thread auch zu kennen.

Das stimmt natürlich :-).

@Sir Rufo
Meiner Meinung nach hast du recht. Der Aufbau der Struktur sollte keinen Einfluss haben. Es macht dann zwar mehr Arbeit bei der Auswertung aber mal sehen.

Der Vollständigkeit halber hier nochmal beide Prozeduren mit Streams:
Delphi-Quellcode:
procedure TForm1.LiesXML;
var
  xmlDoc: IXMLDocument;
  HashDatei, HashBerechnet: string;
  Stream: TStream;
begin
  //XMLDocument erzeugen und xml-Datei laden
  xmlDoc := newXMLDocument;
  xmlDoc.LoadFromFile(PATH + Dateiname);
  xmlDoc.Active := true;

  TextEdt.Text := xmlDoc.DocumentElement.ChildNodes['messdaten'].ChildNodes['messwerte'].Attributes['text'];
  Messwert1Edt.Text := xmlDoc.DocumentElement.ChildNodes['messdaten'].ChildNodes['messwerte'].Attributes['messwert1'];
  Messwert2Edt.Text := xmlDoc.DocumentElement.ChildNodes['messdaten'].ChildNodes['messwerte'].Attributes['messwert2'];

  HashDatei := xmlDoc.DocumentElement.ChildNodes['hashdaten'].ChildNodes['hash'].Attributes['wert'];

  xmlDoc.DocumentElement.ChildNodes.Remove(xmlDoc.DocumentElement.ChildNodes.FindNode('hashdaten'));

  Stream := TMemoryStream.Create;

  xmlDoc.SaveToStream(Stream);
  Stream.Position := 0;

  HashBerechnet := getMd5StreamToStr(Stream);

  if HashDatei = HashBerechnet then
  begin
    StatusBar1.Panels[1].Text := 'Hash ok!';
  end
  else
  begin
    StatusBar1.Panels[1].Text := 'Hash nicht ok!';
  end;
end;

procedure TForm1.SchreibeXML;
var
  xmlDoc : IXMLDocument;
  iNode: IXMLNode;
  Hashwert: string;
  Stream: TStream;
begin
  ErstelleXML;

  xmlDoc := newXMLDocument;
  xmlDoc.LoadFromFile(PATH + Dateiname);
  xmlDoc.Active := true;

  //Neuen Knoten anlegen - Messdaten
  xmlDoc.DocumentElement.AddChild('messdaten');

  //Attribute zuweisen - Messwerte
  iNode := XMLDoc.DocumentElement.ChildNodes['messdaten'].AddChild('messwerte');
  iNode.Attributes['text'] := TextEdt.Text;
  iNode.Attributes['messwert1'] := Messwert1Edt.Text;
  iNode.Attributes['messwert2'] := Messwert2Edt.Text;
  iNode.Text := '';

  Stream := TMemoryStream.Create;

  xmlDoc.SaveToStream(Stream);
  Stream.Position := 0;

  Hashwert := getMd5StreamToStr(Stream);

  //Neuen Knoten anlegen - Hashdaten
  xmlDoc.DocumentElement.AddChild('hashdaten');

  //Attribute zuweisen - Hashwert
  iNode := XMLDoc.DocumentElement.ChildNodes['hashdaten'].AddChild('hash');
  iNode.Attributes['wert'] := Hashwert;
  iNode.Text := '';

  //Speichern
  xmlDoc.SaveToFile(PATH + Dateiname);
end;
Ich werde mich ggf. nochmal melden, wenn ich das Verfahren geklärt habe. Vielen Dank schonmal für die schnelle und gute Hilfe!

Grüße
Headbucket

himitsu 12. Dez 2013 12:22

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Man kann vor dem Haschen die XML nochmal "neu" formatiert in einen String/Stream kopieren und da hashen (den Hash entfernt/ersetzt).
Da wäre die Formatierung auch fast egal, abgesehn von der Groß-/Kleinschreibung, denn XML ist grundsätzlich erstmal CaseInsensitiv und auch die Reihenfolge der Parameter könnte interessant sein, welche ja praktisch auch egal ist, aber beim Haschen kommt es dann wieder auf die Reihenfolge drauf an.

Je nach Auswertung könnte auch die Reihenfolge der Nodes egal sein.

Auch kann ein Node so
Delphi-Quellcode:
<node></node>
oder
Delphi-Quellcode:
<node />
gespeichert sein, was aber vom Inhalt, bzw. aus Sicht des DOMs nahezu egal ist.

Und womöglich kann für die Auswertung auch egal sein, ob ein leerer Node vorhanden ist oder nicht.

p80286 12. Dez 2013 12:56

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Wäre es bei diesem aufwand nicht vllt. einfacher einen (verschlüselte) Zip-Datei mit den XML-Daten zu erstellen und vor Programmstart, diese zu entpacken?

oder aber die Daten gleich Binär speichern, und dort einen Hash dazu packen. GGf. kann man ja für Neugierige dann noch eine xml-Datei generieren.

Gruß
K-H

Sir Rufo 12. Dez 2013 13:03

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Zitat:

Zitat von p80286 (Beitrag 1239561)
Wäre es bei diesem aufwand nicht vllt. einfacher einen (verschlüselte) Zip-Datei mit den XML-Daten zu erstellen und vor Programmstart, diese zu entpacken?

oder aber die Daten gleich Binär speichern, und dort einen Hash dazu packen. GGf. kann man ja für Neugierige dann noch eine xml-Datei generieren.

Gruß
K-H

Das ist doch durch die Brust ins Auge und wieder zum Ohr rein.

Wenn ich aus einer wie auch immer gearteten Datei eine Datenstruktur auslesen kann, die vom Daten-Inhalt her identisch ist, dann sind die Daten unverändert.

So kann diese Datei dann auch in ein JSON, YAML, Ini, etc. Format überführt werden (inkl. dem Hashwert) und die Daten können zuverlässig auf Unversehrtheit/Änderung geprüft werden.

Darum würde ich in einen Hash niemals die Speicherstruktur aufnehmen, wenn es mir um die Daten geht.

Bei einem EAN13 Barcode wird ja auch nicht die Strichbreite, Höhe, Druckfarbe oder Papierart in der Prüfziffer berücksichtigt. Auch nicht der Karton, wo der dran draufklebt ;)

madas 12. Dez 2013 13:34

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Nur mal so am Rande: Wenn jemand eine Stelle im Hash ändert und die Messwerte nicht manipuliert, dann schlägt Deine Prüfung trotzdem fehl, obwohl die Messwerte noch korrekt sind.

Ich würde es daher auch wie p80286 machen und die XML-Datei in eine passwortgeschützte ZIP-Datei schmeißen und Ruhe ist. Das Passwort ist in geeigneter Form (nicht MD5) zu hinterlegen.

madas

Sir Rufo 12. Dez 2013 13:49

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Zitat:

Zitat von madas (Beitrag 1239567)
Nur mal so am Rande: Wenn jemand eine Stelle im Hash ändert und die Messwerte nicht manipuliert, dann schlägt Deine Prüfung trotzdem fehl, obwohl die Messwerte noch korrekt sind.

Ich würde es daher auch wie p80286 machen und die XML-Datei in eine passwortgeschützte ZIP-Datei schmeißen und Ruhe ist. Das Passwort ist in geeigneter Form (nicht MD5) zu hinterlegen.

madas

Wenn jemand den Hashwert ändert oder die Daten, dann ist genau der Fall eingetreten, dass man diesen Daten nicht mehr vertrauen kann.

Und der wird also zuverlässig erkannt.

Was soll denn jetzt die ZIP-Datei noch bringen? Ist die gegen Veränderungen geschützt?
Kann ich da etwa keine Bytes ändern?

:roll:

Und jetzt kennt einer das Hashverfahren, baut die Datei neu auf mit falschen Werten und löscht auch noch die ZIP-Datei ...

Und es fällt ein Klavier vom Himmel und alle Häuser fallen um, alle Kühe fallen um und es gibt kein Strom mehr ... ja, was denn dann?

Headbucket 12. Dez 2013 14:14

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
:-D
Bevor die Diskussion in dieser Richtung ausartet möchte ich doch kurz auf diesen Thread verweisen:
Zitat:

Zitat von Bernhard Geyer (Beitrag 1239510)
Diskussion wurde schon im Entwickler-Forum geführt

Zippen kommt somit nicht in Frage. Auch soll die XML-Speicherung die bisherige binäre Speicherung ablösen.

In der Regel soll eigentlich Niemand etwas an den Daten verändert. Deshalb wäre es im Prinzip auch nicht schlimm, wenn man die Struktur im Hashwert mit berücksichtigt.
Es soll rein informativ jedoch möglich sein, sich die XML-Datei anzuschauen.

Möchte man wirklich Messwerte manipulieren, dann würde sich das auch deutlich leichter realisieren lassen als durch das "knacken" meines Hashwertes. So könnte man z.B. einfach "falsch" Messen, bis man seine Wunschwerte hat. Aus diesem Grund ist bereits MD5 als Hashfunktion mehr als ausreichend. Es soll somit lediglich gegen eine offensichtliche Manipulation schützen.

Es wird wohl nun auf die Methode mit den Streams und einer eventuellen Neuformatierung dieser Streams hinauslaufen.

generic 12. Dez 2013 14:29

AW: XML-Datei zu Stream | Stream zu Hashwert | XML gegen Manipulation schützen
 
Da gibt es u.a. auch einen Standard, welcher sich mit XML-Signaturen beschäftigt.
http://www.w3.org/TR/xmldsig-core/


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:33 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz