Comment programmer une fonction de hash non décodable ?

WRInaute accro
Bonjour

Voilà, je me suis mis en tête, de programmer une fonction de hashing impossible à décoder.

J'envisage de mettre l'équivalent d'un salt avant la string à coder, mais ce salt devrait lui-même provenir ( sous forme codée ), de cette string, avec éventuellement une combinaison entre le salt codé , et la string non codée.

Par exemple, fusionner le salt codé et la string de manière spécifique fixée.

Ou bien, combinaison de suites de caractères codées et non codées.

Le salt devrait obligatoirement être déduit de la string à hasher.

Quel serait l'algorithme le plus approprié, pour que la string ne puisse pas être décodée même par brute force ?

Merci beaucoup de votre aide.




,
 
WRInaute accro
Pour plus de précisions.

Un algorithme de décodage brute force, pourrait éventuellement restaurer une suite de caractères, mais rien dans son apparence ne pourrait suggérer l'algorithme de production de cette suite de caractères à partir de la string de départ.

Surtout si ces algorithmes sont à plusieurs étages.

Le degré de complexité de l'algorithme entier, serait alors plus que linéairement, proportionnel aux nombre d'étages.
 
Dernière édition:
WRInaute discret
un hash ne se décode jamais. Contrairement au chiffrement (qui est bidirectionnel), le hashing est une fonction à sens unique. Le défi n'est donc pas d'empêcher le "décodage", mais de résister aux attaques par dictionnaire ou par force brute.
 
WRInaute accro
Oui ?

Encore faut-il que l'attaque force brute sache quelle fonction de hashing utiliser.

Là, avec plusieurs fonctions de hash successives, et des résultats intermédiaires non interprétables?

Merci beaucoup de ton aide.
 
WRInaute accro
Voilà voilà :

PHP:
  $str1 = hash1($string);

  $salt = F1($str1);             // F1() éventuellement type hash

  $str3 = F2($str1, $salt);      // F2() fonction de fusion/combinaison type permutation

  $str_cible = hash2($str3);


Merci beaucoup de votre aide et suggestions.
 
WRInaute discret
Solution A : L'ajout d'un "Pepper" (Poivre)


Au lieu que le salt vienne uniquement de la string, mélangez-y une clé secrète (le poivre) stockée sur un serveur sécurisé.


• Votre logique modifiée : salt = F_1(string, secret\_key)


• Résultat : Même si l'attaquant vole votre base de données de hashs, il ne peut rien faire sans la clé secrète restée sur le serveur.
 
WRInaute accro
Bonjour voyante

Voilà :

PHP:
function hash_code($str) {

        $str1 = hash('algo1', $str);

        $salt = hash('algo2', $str1);             // F1() éventuellement type hash

        $str3 = fusion($str1, $salt);      // F2() fonction de fusion/combinaison

        $str_cible = hash('algo3', $str3);

        return $str_cible;
}


Les trois algos sont masqués.

La fonction fusion() est aussi masquée.

Cette fusion est variable suivant les longueurs des paramètres.

Mêmes paramètres => même fusion.

Merci beaucoup de vos suggestions.
 
WRInaute discret
function hash_code($str) {
// 1. On crée une empreinte de base
$str1 = hash('sha512', $str);

// 2. Votre Salt déduit (F1)
// On peut utiliser une partie du hash pour complexifier
$salt = hash('whirlpool', str_rot13($str1));

// 3. Votre Fusion personnalisée (F2)
$str3 = fusion($str1, $salt);

// 4. LE VERROU (Algo 3)
// On utilise Argon2id (standard mondial actuel)
// C'est ici que l'on rend le brute-force "impossible"
$options = [
'memory_cost' => 65536, // 64MB de RAM par essai
'time_cost' => 4, // 4 itérations
'threads' => 2, // Utilise 2 coeurs
];

return password_hash($str3, PASSWORD_ARGON2ID, $options);
}
 
WRInaute discret
function hash_code($str) {
// 1. On crée une empreinte de base
$str1 = hash('sha512', $str);

// 2. Votre Salt déduit (F1)
// On peut utiliser une partie du hash pour complexifier
$salt = hash('whirlpool', str_rot13($str1));

// 3. Votre Fusion personnalisée (F2)
$str3 = fusion($str1, $salt);

// 4. LE VERROU (Algo 3)
// On utilise Argon2id (standard mondial actuel)
// C'est ici que l'on rend le brute-force "impossible"
$options = [
'memory_cost' => 65536, // 64MB de RAM par essai
'time_cost' => 4, // 4 itérations
'threads' => 2, // Utilise 2 coeurs
];

return password_hash($str3, PASSWORD_ARGON2ID, $options);
}
 
WRInaute accro
Bonjour voyante

La fonction password_hash() avec PASSWORD_ARGON2ID, ne rend-t-elle pas une chaîne de caractère avec au début le type de cryptage ?

Je suggère d'utiliset cette fonction en premier, puis les deux autres algos après.

Merci beaucoup de ton aide.
 
WRInaute discret
function hash_code_ultime($str) {
// 1. La fondation (Argon2id)
// On crée un hash ultra-robuste mais "identifiable"
$base_hash = password_hash($str, PASSWORD_ARGON2ID);

// 2. Votre Salt dérivé (F1)
// On déduit un salt de la chaîne initiale pour varier la transformation
$internal_salt = hash('sha3-256', $str);

// 3. Votre Fusion personnalisée (F2)
// On mélange le hash Argon2 (qui contient les $...) avec le salt interne
$mixed = fusion($base_hash, $internal_salt);

// 4. Le Masquage final (Algo 3)
// On termine par un hash rapide mais complexe (ex: Keccak/SHA3)
// pour uniformiser le résultat et supprimer toute trace du $argon2id$
$final_code = hash('sha3-512', $mixed);

return $final_code;
}
 
WRInaute accro
Bonjour voyante

Peut-être le salt dérivé avec sha3-512 au lieu de sha3-256 ?

Il me semble que Argon2ID rend un hash de longueur variable ?

Et $base_hash est de longueur fixe.

Ma fonction fusion() mêle ses paramètres par groupes à la file.

Le paramètre le plus long dépasse.

Il est souhaitable que les deux paramètres soient de même taille.

Sinon il faudrait que je change l'implémentation de fusion() ?

Merci beaucoup de ton aide.
 
WRInaute accro
Plus généralement...

La CNIL décrit deux modes de cryptage : La pseudonymisation et l'anonymisation.

Le hashing est censé être seulement de la pseudonymisation, parce qu'en testant en brute force toutes les possibilités, on pourrait retrouver le token caché.

Celà sous-entend, que cette recherche est possible, donc que l'agorithme de hashing est connu.

Si on implémente un hashing à plusieurs étages, l'algo à chaque étage est d'autant plus inconnu, que le nombre d'algorithmes est grand ( nombre d'étages ).

On en revient donc à un algorithme global inconnu, donc à de l'anonymisation.

Maintenant, la CNIL ne pourrait pas recommander tel ou tel type de séquences d'algorithmes de hashing, sans du même coup dévoiler les algorithmes, donc rendre possible les recherches brute force.

Celà veut simplement dire, que les implémentations individuelles, ne peuvent quasiment pas être légiférées.

Merci beaucoup de vos suggestions.
 
WRInaute discret
function hash_code_ultime($str) {
// 1. Générer un Salt de 64 octets (512 bits)
$salt_derivé = hash('sha3-512', $str); // Sortie hexadécimale de 128 car.

// 2. Générer un hash de base de la même longueur exacte (128 car.)
// On utilise ici une fonction de dérivation (KDF) pour la lenteur
// hash_pbkdf2 permet de choisir la longueur de sortie
$base_hash = hash_pbkdf2("sha512", $str, $salt_derivé, 100000, 64);
// Le '64' ici génère 64 octets bruts, soit 128 caractères en hexa.

// Désormais : strlen($base_hash) == strlen($salt_derivé) == 128

// 3. Fusion parfaite (puisque les tailles sont identiques)
$mixed = fusion($base_hash, $salt_derivé);

// 4. Masquage final
return hash('sha3-512', $mixed);
}
 
WRInaute accro
Bonjour voyante

Avec ta fonction et le salt en 128 octets :


Code:
STR = ABRARACOURCIX

SALT = a89af00da71f508211ff5501557f86c4ae7589b99d58f996bccacecae83757d061dff3073e483e9529881a922c49e5427c77a92598932c8245ad961c3e49cd59

HASH = 845950026200ab117e2a308e44f1e14a033da8eadf9a4fa7840de4f48309cedc16bdefac7a46de03cc4e305c50cd4b40d96b49e766482872a0bbacfc22859f05

MIXED = 84595002a89a6200ab11f00d7e2a308ea71f44f1e14a5082033da8ea11ffdf9a4fa75501840de4f4557f8309cedc86c416bdefacae757a46de0389b9cc4e305c9d5850cd4b40f996d96b49e7bcca66482872cecaa0bbacfce83722859f0557d061dff3073e483e9529881a922c49

RESULTAT = 4911989d93d92b4db0d8e15183556e75df4057fe2dd240e78848d9db7ba78340f0347e0e811462761cb0a9d81201347271539c721358dce11dcb58ed1504c39f

STRLEN(STR) = 128
 
WRInaute discret
Si vous utilisez ce code en production, assurez-vous que vos fonctions algo1, algo2, algo3 et fusion sont bien dans un fichier protégé. Si un pirate accède au code source, il ne pourra toujours pas "inverser" le hash (car c'est impossible), mais il pourra commencer à faire tourner ses propres serveurs pour tester des milliards de mots de passe par seconde.


Pour contrer cela, n'oubliez pas d'ajouter un "Work Factor" (comme les itérations dans PBKDF2 ou Argon2) pour que chaque calcul de RESULTAT prenne au moins 100 ou 200 millisecondes sur votre serveur.
 
WRInaute accro
Bonjour voyante

J'ai amélioré la fonction fusion().

Le bug c'est que ma fonction de cryptage est destinée à crypter les noms de Jockeys sur mon site.

Cà fait quelques mois que j'ai enlevé les jockeys de la base de données de mon site de turf.

La CNIL prône une approche sécurisée mais vague par rapport au cryptage des données personnelles.

Cà a l'air d'être au "cas par cas".

Donc prudence.

Voilà.


Code:
STR = ABRARACOURCIX

SALT = a89af00da71f508211ff5501557f86c4ae7589b99d58f996bccacecae83757d061dff3073e483e9529881a922c49e5427c77a92598932c8245ad961c3e49cd59

HASH = d286993e12c65b70f6247e07fec4eec101bfee75036b63a27ad17225e9e6e2f28e04ecb74074ded5d3c0a3c6f211aa30e4f98152fe6e2f54364d780f362397b8

MIXED = d2869a89a93e1f00da2c65b71f570f608211247e0ff557fec015574eec1f86c01bf4ae75ee75089b936b69d58f3a27a996bd172ccace25e9ecae86e2f3757d28e04061decb7ff3074074d3e48ed5d3e9523c0a39881c6f2a922c11aa349e50e4f427c7981527a92fe6e598932f5432c8264d745ad980f3661c32397e49cdb859

STR_CIBLE = 873c1660d6405cd5cb5b3019b6c3bf5f502ba0d71c131abff445ab7280a26799

ELAPSED = 216.97998046875 milli-secondes
 
WRInaute discret
Une suggestion pour le "Cas par Cas" de la CNIL


Pour être totalement inattaquable juridiquement, vous pourriez ajouter une variable "Clé Secrète" (Pepper) dans votre fusion() qui n'est pas stockée en base de données, mais dans un fichier de configuration sur votre serveur.
 
WRInaute accro
Bonsoir voyante

Dans un fichier de configuration chmod 640 en dehors de l'arborescence du $DOCUMENT_ROOT ?

Celà suffirait ?

Avec en plus un audit de Maître Muriel CAHEN, avocate NTIC ?

Merci beaucoup beaucoup de votre réponse.


PHP:
$bytes = random_bytes(64);
var_dump(bin2hex($bytes));
 
Dernière édition:
WRInaute accro
Merci beaucoup voyante

Si la variable "Clé Secrète" est associée à chaque jockey crypté, pourrais-je mettre ces clés en troisième colonne dans la Table MySQL : JOCKEYS_HASHED ?

CREATE TABLE JOCKEYS_HASHED(NUMJO INTEGER PRIMARY KEY NOT NULL AUTO INCREMENT, NOMJO VARCHAR(130) UNIQUE KEY NOT NULL DEFAULT '', KEY VARCHAR(130) NOT NULL DEFAULT '');

Ou bien s'il y a une seule clé secrète, ce ne serait pas suffisant ?
 
WRInaute accro
Pardon voyante

Où avais-je la tête.

Je ne pourrais pas retrouver l'enreg de JOCKEY_SHASHED parce que je ne saurais pas la clé privée avant de lire l'enreg.

Seule solution : Une clé privée fixe dans fusion(), et cryptage de la fusion avec cette clé privée.
 
WRInaute discret
// Exemple de ce que deviendrait votre logique
$private_key = "UNE_CLE_TRES_LONGUE_ET_FIXE_SUR_LE_SERVEUR";

$str1 = hash('algo1', $str);
$salt = hash('algo2', $str1);

// On mélange la clé privée dans la fusion
$mixed = fusion($str1, $salt, $private_key);

$str_cible = hash('algo3', $mixed);
 
WRInaute accro
Bonjour voyante

Plutôt, crypter la sortie de fusion() avec la clé privée ?

Quelque chose de non décryptable ?

Merci beaucoup de ton aide.
 
WRInaute accro
Rebonjour voyante

Avec une fonction openssl , générer une clé privée et publique.

Chiffrer fusion() avec la clé publique après avoir jeté la clé privée.

La clé privée n'existe plus.

Seule possibilité : chiffrement.

Je n' ai pas besoin de déchiffrer, puisque un nouveau Jockey peut être chiffré, et cherché ou inséré dans JOCKEYS_HASHED.

Leurs index auto incrément sont seuls insérés dans la table COURSES.

Merci beaucoup de ton aide.
 
WRInaute discret
// 1. Générer la paire de clés
$res = openssl_pkey_new([
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);

// 2. Extraire la clé publique
$pubKeyDetails = openssl_pkey_get_details($res);
$pubKey = $pubKeyDetails["key"];

// 3. ICI : On détruit $res et on ne sauvegarde JAMAIS la clé privée.
// On affiche ou on stocke uniquement $pubKey dans votre code.
 
WRInaute discret
function hash_jockey_ultime($str, $pubKey) {
// Vos étapes précédentes
$str1 = hash('sha3-512', $str);
$salt = hash('sha3-512', $str1);
$mixed = fusion($str1, $salt); // Votre entrelacement

// Chiffrement asymétrique (Trappe à sens unique)
// On utilise la clé publique pour "sceller" la donnée
openssl_public_encrypt($mixed, $encrypted, $pubKey);

// Comme le résultat chiffré peut être binaire et très long,
// on le repasse dans un hash final pour avoir un index propre.
return hash('sha3-256', $encrypted);
}
 
WRInaute discret
Important : Pour que votre fonction reste déterministe (même nom = même hash), vous devez utiliser le flag OPENSSL_NO_PADDING. Cependant, cela impose que votre chaîne $mixed ait une taille spécifique correspondant à la taille de la clé RSA (ex: 256 octets pour une clé 2048 bits)
 
WRInaute accro
Bonjour voyante

Il me faudrait une fonction pour rendre $encrypted insérable dans un VARCHAR() indexable.


PHP:
<?php

function encrypt(string $data, $pubKey = null) {

    if(strlen($data) != 256) {

        throw new Exception("Erreur : Longueur param = " . strlen($data) . " != 256.\n");

        exit(-1);
    }

    $config = array(
        "private_key_bits" => 2048,
        "private_key_type"    => OPENSSL_KEYTYPE_RSA
    );
   
    if($pubKey === null) {

        // Create the private and public key
        $res = openssl_pkey_new($config);

        // Extract the private key from $res to $privKey
        openssl_pkey_export($res, $privKey);

        // Extract the public key from $res to $pubKey
        $pubKey = openssl_pkey_get_details($res);
        $pubKey = $pubKey["key"];
    }

    // Encrypt the data to $encrypted using the public key
    openssl_public_encrypt($data, $encrypted, $pubKey, OPENSSL_NO_PADDING);

    return array($pubKey, $encrypted);
}

?>
 
WRInaute discret
function encrypt_jockey(string $data, string $pubKey) {
// RSA 2048 bits nécessite exactement 256 octets (2048 / 8)
if(strlen($data) !== 256) {
throw new Exception("Erreur : La donnée doit faire exactement 256 octets.");
}

// Utilisation de OPENSSL_NO_PADDING pour garantir que le même nom
// produise toujours le même résultat (indispensable pour vos index)
$success = openssl_public_encrypt($data, $encrypted, $pubKey, OPENSSL_NO_PADDING);

if (!$success) {
throw new Exception("Échec du chiffrement OpenSSL.");
}

// On retourne un hash du bloc chiffré pour l'indexation SQL
return hash('sha256', $encrypted);
}
 
WRInaute accro
Super çà marche. ;)

Merci beaucoup voyante.

Deux appels avec la même clé publique donnent le même résultat.

Je redonne le code si tu veux.


Code:
STR = ABRARACOURCIX

SALT = a89af00da71f508211ff5501557f86c4ae7589b99d58f996bccacecae83757d061dff3073e483e9529881a922c49e5427c77a92598932c8245ad961c3e49cd59

HASH = d286993e12c65b70f6247e07fec4eec101bfee75036b63a27ad17225e9e6e2f28e04ecb74074ded5d3c0a3c6f211aa30e4f98152fe6e2f54364d780f362397b8

MIXED = d2869a89a93e1f00da2c65b71f570f608211247e0ff557fec015574eec1f86c01bf4ae75ee75089b936b69d58f3a27a996bd172ccace25e9ecae86e2f3757d28e04061decb7ff3074074d3e48ed5d3e9523c0a39881c6f2a922c11aa349e50e4f427c7981527a92fe6e598932f5432c8264d745ad980f3661c32397e49cdb859

STR_CIBLE = 9eb1a4b6c5bca17ec50879d049e6ffa63871b743bd82380dae2d6d68903da0d1

ELAPSED = 229.97689247131 milli-secondes
 
WRInaute accro
C'est simple.

La CNIL différencie la pseudonymisation et l'anonymisation.

Elle range le hashing dans la première catégorie, et le chiffrement asymétrique ( je crois ), dans la deuxième catégorie.

Le hashing n'est pas suffisant ( d'après la CNIL ) pour masquer des données personnelles.

Le chiffrement est suffisant et rend nécessaire une clé secrète inconnue.

Mon idée est d'abord de complexifier beaucoup le début du hashing, puis de chiffrer le résultat du hashing avec une clé publique, sans aucune clé privée, c'est-à-dire impossibilité de déchiffrer ( seulement de chiffrer ), suivi du dernier hashing.

Ainsi cette méthode passe de la catégorie pseudonymisation à la catégorie anonymisation.

Tout le problème serait de savoir si la CNIL accepterait ce mode de chiffrage bijectif non réversible.

C'est-à-dire si elle le rangerait dans la catégorie anonymisation.

Merci beaucoup de ton aide.
 
WRInaute occasionnel
Ce que tu essaies de faire ne sert à rien. Ceci dit, pour alimenter tes expérimentations, tu devrais te pencher sur les fonctions OpenSSL incluses avec PHP. Là, tu as tout ce qu'il te faut pour encrypter des données. Ça, avec un hashage Argon2, et hop.

edit: https://www.php.net/manual/fr/book.openssl.php
 
Dernière édition:
WRInaute accro
Bonjour kartyr

Je n'ai besoin que de chiffrer, pas décrypter.

Argon2ID produit un code crypté différent à chaque fois.

Dans mon cas, les noms en clair sont cryptés de manière bijective pour identifier le jockey par son code crypté.

C'est équivalent je le reconnais à du hashing, mais le recours à une clé publique secrète, me permet d'éviter le brute force, dans la mesure où l'algorithme de cryptage est inconnu, car la chaine cryptée n'indique pas l'algorithme.

Le problème là, se heurte à la possibilité de perte de la clé publique, ce qui obligerait à refaire entièrement le cryptage.

Merci beaucoup de ton aide.
 
WRInaute discret
Pour éviter que la perte de la clé ne soit fatale, vous pourriez intégrer la clé publique directement dans une constante PHP dans un fichier de configuration sauvegardé sur votre propre machine de développement.

define('JOCKEY_ENCRYPTION_KEY', "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----");
 
WRInaute accro
Bonjour voyante

Je suis en train d'updater ma bdd new_turf sur mon ordinateur.

J'ai fait Janvier 2010 entièrement.

Environ 250 millisecondes par cheval et par course

312 mois encore jusqu'à Janvier 2026.

Le nombre quotidien de réunions augmente progressivement.

A terme j'aurai une database strictement personnelle avec jockeys cryptés, et l'actuelle bdd turf.

J'en ai pour longtemps avant de terminer le cryptage.

Le fichier avec la clé publique est sauvegardé sur mes clés usb.

Merci beaucoup beaucoup pour ton aide.
 

➡️ Offre MyRankingMetrics ⬅️

pré-audit SEO gratuit avec RM Tech (+ avis d'expert)
coaching offert aux clients (avec Olivier Duffez ou Fabien Faceries)

Voir les détails ici

coaching SEO
Discussions similaires
Haut