Calculer et vérifier un checksum pour dialoguer avec l'extérieur

Je vous en parlais à l'occasion de la création de la fonction MD5() : pour les API que je mets en place j'utilise en général des contrôles d'intégrité des paramètres passés. Pour celà j'utilise un paramètre additionnel qui contient le checksum du tout.

Ca ne me sert pas dans les logiciels autonomes mais pour dialoguer avec des sites Internet. Il y a donc un bout de code côté Delphi pour calculer et vérifier le checksum et il y en a un côté serveur.

Du côté de Delphi je me suis créé une unité uChecksumVerif dans laquelle se trouve la classe ChecksumVerif permettant d'en appeler les méthodes de classe sans l'instancier.

Cette technique permet simplement de créer un espace de nom dans lequel caser des fonctions et procédures. Pratique désormais courante sous Delphi et C++ Builder.

Voici le source de l'unité en question:

unit uChecksumVerif;

interface

uses
  system.classes;

type
  ChecksumVerif = class
    // private
    class function get(param: string; key1: string; key2: string; key3: string;
      key4: string; key5: string; public: boolean): string; overload;
    // public
    class function get(param: TStrings; key1: string = ''; key2: string = '';
      key3: string = ''; key4: string = ''; key5: string = ''): string;
      overload;
    class function get(param: string; key1: string = ''; key2: string = '';
      key3: string = ''; key4: string = ''; key5: string = ''): string;
      overload;
    class function check(verif: string; param: TStrings; key1: string = '';
      key2: string = ''; key3: string = ''; key4: string = '';
      key5: string = ''): boolean; overload;
    class function check(verif: string; param: string; key1: string = '';
      key2: string = ''; key3: string = ''; key4: string = '';
      key5: string = ''): boolean; overload;
  end;

implementation

uses u_md5;

class function ChecksumVerif.get(param: string; key1: string; key2: string;
  key3: string; key4: string; key5: string; public: boolean): string;
var
  verif: string;
begin
  verif := MD5(param + key1 + key2 + key3 + key4 + key5);
  if public then
    result := copy(verif, 1 + random(Length(verif) - 10), 10)
  else
    result := verif;
end;

class function ChecksumVerif.get(param: TStrings; key1: string = '';
  key2: string = ''; key3: string = ''; key4: string = '';
  key5: string = ''): string;
var
  i: integer;
  ch: string;
begin
  ch := '';
  for i := 0 to param.Count - 1 do
  begin
    ch := ch + param[i];
  end;
  result := get(ch, key1, key2, key3, key4, key5, true);
end;

class function ChecksumVerif.get(param: string; key1: string = '';
  key2: string = ''; key3: string = ''; key4: string = '';
  key5: string = ''): string;
begin
  result := get(param, key1, key2, key3, key4, key5, true);
end;

class function ChecksumVerif.check(verif: string; param: TStrings;
  key1: string = ''; key2: string = ''; key3: string = ''; key4: string = '';
  key5: string = ''): boolean;
var
  i: integer;
  ch: string;
begin
  ch := '';
  for i := 0 to param.Count - 1 do
  begin
    ch := ch + param[i];
  end;
  result := check(verif, ch, key1, key2, key3, key4, key5);
end;

class function ChecksumVerif.check(verif: string; param: string;
  key1: string = ''; key2: string = ''; key3: string = ''; key4: string = '';
  key5: string = ''): boolean;
var
  verif_: string;
begin
  verif_ := get(param, key1, key2, key3, key4, key5, false);
  result := 0 < pos(verif, verif_);
end;

initialization

randomize;

end.

Le principe est assez simple. On appelle :

  • ChecksumVerif.get() pour générer un checksum à partir d'un ou plusieurs paramètres et de clés de cryptage
  • ChecksumVerif.check() pour s'assurer que le checksum reçu soit bien valide par rapport aux mêmes paramètres.

L'algorithme de génération du checksum est lui aussi très simple : on concatène dans l'ordre d'arrivée les différents paramètres, on en calcule le MD5 puis on ne ressort qu'une partie du résultat.

Pour la vérification, on calcule la clé et on vérifie que le checksum reçu est bien présent dans la chaine complète.

Je l'ai dit, MD5 n'est pas un algorithme sécurisé pour des mots de passe. Il a cependant l'avantage de toujours générer la même chose avec les mêmes infos d'entrée quel que soit le langage de développement utilisé. L'utilisation des mêmes paramètres donnera donc toujours la même chose à condition de les passer dans le même ordre pour le calcul et la vérification du checksum.

Pour compliquer les choses, plutôt que de sortir l'intégralité du code généré qui pourrait donner lieu à investigations de la part de hackers, on en ressort une partie qui en plus n'est pas toujours la même. Il devient ainsi totalement impossible de deviner la ou les clés de cryptage utilisées ni de les remplacer par un truc donnant le même résultat avec l'algorithme MD5.

Alors bien sûr, les mêmes paramètres donnant toujours le même résultat, cette technique ne peut servir que pour s'assurer de la cohérence d'informations, ni de leur provenance, ni de l'authenticité de leur émetteur. Pour ça il faut une donnée partagée entre les deux côtés qui ne soit visible nulle part et change à chaque fois, comme un identifiant de session ou une clé unique par exemple calculée avec Log'n Pass qui servirait de tiers de confiance.


Mug Toucan DX dans la baie de RioMug Chinese New Year 2023 : year of the rabbit