J'ai implémenté un cryptage au repos dans l'un de nos composants Joomla, mais je continue à avoir quelques erreurs lors du décryptage des valeurs lorsqu'elles sont écrites et lues à partir d'une base de données.
J'ai donc écrit une classe d'emballage basique pour crypt ...
/**
* This class provides functions related to encryption
*
* @since 2.5
*/
class crypt
{
public $cipher = FALSE;
public function __construct()
{
# test private key
$private_key_string = "51111110024135111761721919212413";
# test public key
$public_key_string = "76172191921241351111110024135111";
# get the cipher object
$cipher = new JCryptCipherSodium;
# generate a key
$key = $cipher->generateKey();
# set the nonce
$cipher->setNonce(\Sodium\randombytes_buf(\Sodium\CRYPTO_BOX_NONCEBYTES));
# set the key values
$key->private = $private_key_string;
$key->public = $public_key_string;
$this->key = $key;
$this->cipher = $cipher;
return TRUE;
}
public function en($string){
#echo "\n\nencrypt\n";
$key = $this->key;
$encrypted = $this->cipher->encrypt($string, $key);
#echo "encrypted: " . $encrypted . "\n";
return $encrypted;
}
public function de($string){
#echo "\n\ndecrypt\n";
$key = $this->key;
$decrypted = $this->cipher->decrypt($string, $key);
#echo "decrypted: " . $decrypted . "\n";
return $decrypted;
}
}
Je sais que ce n'est pas la meilleure pratique, avec les clés, mais je commençais tout juste comme une preuve de concept.
Cela fonctionne très bien avec un simple cryptage puis décryptage, comme ...
JLoader::register("crypt","cli/crypt_helper.php");
$crypt = new crypt();
$enc = $crypt->en("hello world, how you doing");
$de = $crypt->de($enc);
echo "de: " . $de . "\n";
... mais quand j'écris et relis dans une base de données, j'obtiens les erreurs mentionnées ci-dessus. Donc, avec un code comme ...
# database test
$db = JFactory::getDbo();
$query = $db->getQuery(true);
# Insert columns.
$columns = array('f1',);
# Insert values.
$values = array($db->quote( $crypt->en( $test_value ) ) );
# Prepare the insert query.
$query
->insert($db->quoteName('#__piota_results'))
->columns($db->quoteName($columns))
->values(implode(',', $values));
# Set the query using our newly populated query object and execute it.
$db->setQuery($query);
$db->execute();
# Create a new query object.
$query = $db->getQuery(true);
# Order it by the ordering field.
$query->select($db->quoteName(array('f1')));
$query->from($db->quoteName('#__piota_results'));
$query->order('id DESC');
$query->setLimit('1');
# Reset the query using our newly populated query object.
$db->setQuery($query);
# Load the results as a list of stdClass objects (see later for more options on retrieving data).
$result = $db->loadResult();
echo "result: " . $result . "\n";
echo "result: " . $crypt->de( $result ) . "\n";
J'ai essayé sur deux systèmes différents, avec des erreurs légèrement différentes.
Système 1
Message d'erreur système 1: MAC invalide il s'agit, je pense, de https://github.com/joomla/joomla-cms /blob/9ae0742f28faa1d4b5d4ae4a84619f115ed3f9ea/libraries/vendor/paragonie/sodium_compat/src/Crypto.php#L1004 =
Système 2
Message d'erreur système 2: L'argument 1 doit être au moins CRYPTO_BOX_MACBYTES long , ce qui, je pense, provient de https://github.com /joomla/joomla-cms/blob/9ae0742f28faa1d4b5d4ae4a84619f115ed3f9ea/libraries/vendor/paragonie/sodium_compat/src/Compat.php#L1069 =
Le champ de base de données est du type "longtext" avec le classement "utf8mb4_bin".
J'ai lu un peu, mais comme libsodium est une nouveauté dans Joomla, il n’ya pas beaucoup de documentation ou d’informations à ce sujet.
J'ai essayé d'utiliser base64_encode et base64_decode au cas où MySQL gâcherait les données, mais ce serait tout de même.
J'ai regardé...
... mais sans chance.
Toute idée, pensée ou pointeur serait grandement apprécié. Merci
Une fois que tout cela fonctionne, je vais essayer d’écrire une page wiki sur libsodium sur https://docs.joomla.org
Cela fonctionne très bien avec un simple cryptage puis décryptage, comme ...
... mais quand j'écris et relis dans une base de données, j'obtiens les erreurs mentionnées ci-dessus.
C'est le comportement attendu, car le nonce n'est pas stocké avec le texte chiffré. Vous pouvez le vérifier par comment SodiumCipher est défini .
Essayez de stocker le nonce à côté du texte chiffré et de l’utiliser lors du déchiffrement. (Ne réutilisez pas les nonces pour plusieurs lignes.)
En outre, vous voudrez encoder avant de stocker et de décoder après avoir récupéré (comme indiqué dans votre question), en utilisant base64 ou hex.
Les messages d'erreur Invalid MAC
Et Argument 1 must be at least CRYPTO_BOX_MACBYTES long
Sont définitivement trompeurs.
Je devais l'essayer moi-même afin de constater que les deux erreurs apparaissent de manière aléatoire en fonction du contenu de la chaîne.
Malgré ce que je pensais, $db->quote()
ne code PAS correctement la chaîne renvoyée par crypt. base64_encode/decode
Suffisent: voir tout mon code (je suis passé à la table #__banners
, Pour faciliter les tests)
$helperPath = dirname(__FILE__) ."/crypt_helper.php";
JLoader::register("crypt",$helperPath);
$test_value = 'the <b>quick brown fox jumps</b> over the lazy dog<br>';
$crypt = new crypt();
# database test
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$columns = array('name','params');
$values = array($db->quote('test'), $db->quote(
base64_encode( $crypt->en( $test_value ) )
));
# Prepare the insert query.
$query
->insert($db->quoteName('#__banners'))
->columns($db->quoteName($columns))
->values(implode(',',$values));
# Set the query using our newly populated query object and execute it.
echo $query .'<br>';
$db->setQuery($query);
$db->execute();
# Create a new query object.
$query = $db->getQuery(true);
# Order it by the ordering field.
$query->select($db->quoteName('params'));
$query->from($db->quoteName('#__banners'));
$query->order('id DESC');
$query->setLimit('1');
# Reset the query using our newly populated query object.
$db->setQuery($query);
# Load the results as a list of stdClass objects (see later for more options on retrieving data).
$result = $db->loadResult();
echo "result: " . $result . "\n";
echo "result: " . $crypt->de( base64_decode($result )) . "\n";
die();
Voici la sortie vidéo de la requête, des données insérées et extraites:
INSERT INTO `#__banners`
(`name`,`params`) VALUES
('test','pKihow6a5Dfx/7p+dlGc5qOnBgOvoJ3+TcZu82sCQU/EWYxnsdxRiAJ6cjB2l433LlSOgTWT0i1Sn/uAWdy/hzwRlRUKaQ==')
result: pKihow6a5Dfx/7p+dlGc5qOnBgOvoJ3+TcZu82sCQU/EWYxnsdxRiAJ6cjB2l433LlSOgTWT0i1Sn/uAWdy/hzwRlRUKaQ==
result: the <b>quick brown fox jumps</b> over the lazy dog<br>
Note 1: toute tentative sans base64_encode échoue. Je l'ai essayé avec 10 octets ou 1K de texte et cela fonctionne très bien.
Note 2:$db->insertObject()
est beaucoup plus facile à utiliser que votre approche, il gère les appels $db->quote()
pour vous, mais requiert toujours base64_encode =:
$db = JFactory::getDbo();
$object = new stdClass();
$object->name = 'test';
$object->params = base64_encode($crypt->en( $test_value ));
$db->insertObject('#__banners', $object);