Comment crypter les paramètres d'une Querystring - ASP.NET/C#
Pourquoi encrypter une URL ?
Il peut être intéressant lorsqu’on passe des valeurs en GET dans l’URL1 , où celle-ci pourrait être de la forme http://monsupersite.com/monscript.aspx?user=12&motdepasse=monsuperpasse&droit=2 de ne pas donner accès ou du moins la visibilité des paramètres que l’on passe au script de traitement. Ceci principalement pour des raisons de sécurité : tout internaute a dans l’absolu le moyen de modifier à la main la chaine passée dans la Querystring.
Aussi, pour éviter ça, on va tenter de transformer notre Querystring en une chaîne encryptée, de la forme : http://monsupersite.com/monscript.aspx?qs=YnZEUjZmb0QyY292NnlyQzZQTUo0ek…, ce qui est déjà moins compréhensible par l’être humain. qs contient l’ensemble des paramètres passés à l’origine.
Comment faire ?
Ce dont nous avons besoin :
- de méthodes qui cryptent et décryptent une chaine, nous utiliserons une classe nommée Crypto (fournie par Obviex, la RijndaelSimple renommée en Crypto) qui encapsule l’algorithme (imprononçable) symétrique Rijndael. La clé utilisée pour le calcul est stockée dans un fichier de configuration (dans notre cas, dans le web.config).
- de méthodes statiques cryptQ(string qs) et decryptQ(string qs) qui appellent Crypto et renvoient une chaine fonctionnelle pour le querystring au format Base64.
using System.Configuration;
public class QueryStringHelper
{
/// <summary>
/// cryptage d'un valeur (querystring)+ convertion base64
/// Avec l'objectif de protéger une querystring (paras/valeurs)
/// </summary>
/// <param name="val"></param>
/// <returns>la valeur base64</returns>
public static string cryptQ(string val)
{
// la clé cryptKey est sotckée dans le web.config
Crypto crypt = new Crypto(ConfigurationManager.AppSettings["cryptKey"]);
byte[] b = Encoding.Default.GetBytes(crypt.Encrypt(val));
string bstring = Convert.ToBase64String(b);
// les caractères spéciaux à protéger pour la querystring
// http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
bstring = bstring.Replace("/", "_");
bstring = bstring.Replace("+", "-");
return bstring;
}
/// <summary>
/// désérialisation base64 + décryptage d'une valeur
/// </summary>
/// <param name="val"></param>
/// <returns>la valeur base64</returns>
public static string decryptQ(string val)
{
// on remet en place les caractères protégés dans CryptQ()
val = val.Replace("_", "/");
val = val.Replace("-", "+");
// la clé cryptKey est sotckée dans le web.config
Crypto crypt = new Crypto(ConfigurationManager.AppSettings["cryptKey"]);
byte[] b = Convert.FromBase64String(val);
return crypt.Decrypt(Encoding.Default.GetString(b));
}
}
On peut dès lors utiliser ces 2 méthodes pour sécuriser nos URLs :
Pour l’appel :
string myQS = "user=12&motdepasse";
Response.Redirect(string.Format("monscript.aspx?qs={0}",QueryStringHelper.cryptQ(myQS)));
Pour la réception, dans monscript.aspx :
if(! String.IsNullOrEmpty(Request.QueryString["qs"]) )
string myQSdec = QueryStringHelper.decryptQ(Request.QueryString["qs"]);
myQSdec contiendra la chaine origine. Ce qui serait bien, c’est d’avoir une méthode qui me sépare les clés des valeurs, comme on peut l’avoir avec Request.QueryString, ajoutons une méthode à notre classe :
/// <summary>
/// découpage d'une chaine querystring
/// </summary>
/// <param name="query">chaine de la forme para1=val1¶2=val2&...¶n=valn</param>
/// <returns>un dictionnaire paras/valeurs</returns>
public static IDictionary deFoldQS(string query)
{
IDictionary myparams = new Hashtable();
if (String.IsNullOrEmpty(query))
return myparams;
string[] pairs = query.Split(new char[] { '&' });
foreach (string s in pairs)
{
string[] pair = s.Split(new char[] { '=' });
myparams[pair[0]] = (pair.Length > 1) ? pair[1] : string.Empty;
}
return myparams;
}
On peut alors utiliser nos paramètres simplement lors de la réception dans le script monscript.aspx :
if(! String.IsNullOrEmpty(Request.QueryString["qs"]) )
{
string myQSdec = QueryStringHelper.decryptQ(Request.QueryString["qs"]);
IDictionnary querystring = QueryStringHelper.deFoldQS(myQSdec);
// on utilise les valeurs de nos paramètres
string user = querystring["user"] as string ?? "";
string pwd = querystring["motdepasse"] as string ?? "";
int droit;
if (!int.TryParse((string)p["droit"], out droit))
droit = -1;
}
Ce type de méthode d’encryptage de paramètres d’URL peut s’avérer utile lors de la manipulation critique de valeurs qui ne peuvent être transmises qu’en GET. Bien entendu, on ne s’amusera pas à tout crypter (performances), mais que lorsque cela s’avère nécessaire.
Les fichiers QSUtil.cs et Crypto.cs2 :
1 passées en Querystring, c’est à dire par l’appel d’un script où les paramètres et leur valeur sont visibles dans l’URL
2 auquel j’ai ajouté 2 méthodes utilitaires pour avoir des hash SHA256 et MD5 – SHA256 est préférable car l’algorithme MD5 a été cracké.
Commentaires
Ceci est tout sauf une raison de sécurité.
Si il existe un moyen de casser le site par une query, c'est pas un cryptage côté client qui va arranger les choses, ni les rendre pour autant plus difficile.
"dans l'absolu", c'est un moyen parmi d'autres.
Que proposes-tu pour ce type de problèmatique ?
Je vois ta méthode dans l'absolu comme un moyen de retarder la réussite d'une attaque.
Pour ma part, j'ai pour règle de ne jamais faire confiance à l'utilisateur et donc en début de traitement je check tous les paramètres soumis en GET et POST. S'il y a besoin, je m'aide également des informations de SESSION contenu dans le cookie pour vérifier cette fois si un paramètre avec une telle valeur a le droit d'y être.
Ainsi l'utilisateur aura beau voir ce qu'il envoie et bidouiller, il ne pourra rien faire.
Je suis un adepte du : "tiens v'la le code source, casse-toi les dents dessus et tente de craquer ça".