2010-08-25 14 views
7

Voglio offuscare un parametro stringa di query in ASP.NET. Il sito avrà un volume elevato di richieste, quindi l'algoritmo non dovrebbe essere troppo lento.parametro stringa stringa offuscamento

mio problema è che tutti gli algoritmi che ho trovato risultato in caratteri indesiderati (come +/=)

Ecco un esempio di quello che voglio ottenere:

www.domain.com/?id=1844 

a

www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ 

Il parametro offuscato deve includere solo az e AZ e 0-9 caratteri.

So di poter crittografare utilizzando base64, ma questo genererà caratteri indesiderati come / o = o +.

Qualche idea di quale algoritmo può essere utilizzato?

Aggiornamento: Sono consapevole di urlencoding, voglio evitare che codifica per la stringa. perché questo genererà caratteri come% F2 o% B2 nell'URL.

+1

Perché si desidera crittografare questo valore? Evitare di indovinare o oscurare? –

+1

evitando una sorta di abuso. – RuSh

+1

Base64 è la codifica e la codifica non è crittografia – Aillyn

risposta

5

È possibile utilizzare il DES triplo per codificare il valore utilizzando un codice a blocchi narow.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 

namespace ConsoleApplication1 { 
    class Program { 
     static string ToHex(byte[] value) { 
      StringBuilder sb = new StringBuilder(); 
      foreach (byte b in value) 
       sb.AppendFormat("{0:x2}", b); 
      return sb.ToString(); 
     } 
     static string Encode(long value, byte[] key) { 
      byte[] InputBuffer = new byte[8]; 
      byte[] OutputBuffer; 
      unsafe { 
       fixed (byte* pInputBuffer = InputBuffer) { 
        ((long*)pInputBuffer)[0] = value; 
       } 
      } 
      TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider(); 
      TDes.Mode = CipherMode.ECB; 
      TDes.Padding = PaddingMode.None; 
      TDes.Key = key; 

      using (ICryptoTransform Encryptor = TDes.CreateEncryptor()) { 
       OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 8); 
      } 
      TDes.Clear(); 

      return ToHex(OutputBuffer); 
     } 
     static long Decode(string value, byte[] key) { 
      byte[] InputBuffer = new byte[8]; 
      byte[] OutputBuffer; 

      for (int i = 0; i < 8; i++) { 
       InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16); 
      } 

      TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider(); 
      TDes.Mode = CipherMode.ECB; 
      TDes.Padding = PaddingMode.None; 
      TDes.Key = key; 

      using (ICryptoTransform Decryptor = TDes.CreateDecryptor()) { 
       OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 8); 
      } 
      TDes.Clear(); 

      unsafe { 
       fixed (byte* pOutputBuffer = OutputBuffer) { 
        return ((long*)pOutputBuffer)[0]; 
       } 
      } 
     } 
     static void Main(string[] args) { 
      long NumberToEncode = (new Random()).Next(); 
      Console.WriteLine("Number to encode = {0}.", NumberToEncode); 
      byte[] Key = new byte[24]; 
      (new RNGCryptoServiceProvider()).GetBytes(Key); 
      Console.WriteLine("Key to encode with is {0}.", ToHex(Key)); 
      string EncodedValue = Encode(NumberToEncode, Key); 
      Console.WriteLine("The encoded value is {0}.", EncodedValue); 
      long DecodedValue = Decode(EncodedValue, Key); 
      Console.WriteLine("The decoded result is {0}.", DecodedValue); 
     } 
    } 
} 

L'output dovrebbe essere qualcosa di simile:

Number to encode = 873435734. 
Key to encode with is 38137b6a7aa49cc6040c4297064fdb4461c79a895f40b4d1. 
The encoded value is 43ba3fb809a47b2f. 
The decoded result is 873435734. 

Si noti che il valore codificato è largo solo 16 caratteri.

Se si è veramente preoccupati dell'abuso, quindi AES può essere utilizzato in modo simile. Nel prossimo esempio accendo AES e scrivo il numero id a 64 bit su entrambi i lati del blocco. Se non decodifica con lo stesso valore su entrambi i lati, viene rifiutato. Questo può impedire alle persone di scrivere in numeri casuali.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 

namespace ConsoleApplication1 { 
    class Program { 
     static string ToHex(byte[] value) { 
      StringBuilder sb = new StringBuilder(); 
      foreach (byte b in value) 
       sb.AppendFormat("{0:x2}", b); 
      return sb.ToString(); 
     } 
     static string Encode(long value, byte[] key) { 
      byte[] InputBuffer = new byte[16]; 
      byte[] OutputBuffer; 
      unsafe { 
       fixed (byte* pInputBuffer = InputBuffer) { 
        ((long*)pInputBuffer)[0] = value; 
        ((long*)pInputBuffer)[1] = value; 
       } 
      } 
      AesCryptoServiceProvider Aes = new AesCryptoServiceProvider(); 
      Aes.Mode = CipherMode.ECB; 
      Aes.Padding = PaddingMode.None; 
      Aes.Key = key; 

      using (ICryptoTransform Encryptor = Aes.CreateEncryptor()) { 
       OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 16); 
      } 
      Aes.Clear(); 

      return ToHex(OutputBuffer); 
     } 
     static bool TryDecode(string value, byte[] key, out long result) { 
      byte[] InputBuffer = new byte[16]; 
      byte[] OutputBuffer; 

      for (int i = 0; i < 16; i++) { 
       InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16); 
      } 

      AesCryptoServiceProvider Aes = new AesCryptoServiceProvider(); 
      Aes.Mode = CipherMode.ECB; 
      Aes.Padding = PaddingMode.None; 
      Aes.Key = key; 

      using (ICryptoTransform Decryptor = Aes.CreateDecryptor()) { 
       OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 16); 
      } 
      Aes.Clear(); 

      unsafe { 
       fixed (byte* pOutputBuffer = OutputBuffer) { 
        //return ((long*)pOutputBuffer)[0]; 
        if (((long*)pOutputBuffer)[0] == ((long*)pOutputBuffer)[1]) { 
         result = ((long*)pOutputBuffer)[0]; 
         return true; 
        } 
        else { 
         result = 0; 
         return false; 
        } 
       } 
      } 
     } 
     static void Main(string[] args) { 
      long NumberToEncode = (new Random()).Next(); 
      Console.WriteLine("Number to encode = {0}.", NumberToEncode); 
      byte[] Key = new byte[24]; 
      (new RNGCryptoServiceProvider()).GetBytes(Key); 
      Console.WriteLine("Key to encode with is {0}.", ToHex(Key)); 
      string EncodedValue = Encode(NumberToEncode, Key); 
      Console.WriteLine("The encoded value is {0}.", EncodedValue); 
      long DecodedValue; 
      bool Success = TryDecode(EncodedValue, Key, out DecodedValue); 
      if (Success) { 
       Console.WriteLine("Successfully decoded the encoded value."); 
       Console.WriteLine("The decoded result is {0}.", DecodedValue); 
      } 
      else 
       Console.WriteLine("Failed to decode encoded value. Invalid result."); 
     } 
    } 
} 

Il risultato dovrebbe essere simile a questo:

Number to encode = 1795789891. 
Key to encode with is 6c90323644c841a00d40d4407e23dbb2ab56530e1a4bae43. 
The encoded value is 731fceec2af2fcc2790883f2b79e9a01. 
Successfully decoded the encoded value. 
The decoded result is 1795789891. 

Si noti inoltre che, poiché ora abbiamo utilizzato un blocco più ampio cifra il valore codificato è ora ampia 32 caratteri.

+0

grazie per la risposta, sembra grande, mal provarlo. qualche idea sulle prestazioni? è un grande sovraccarico? – RuSh

+0

Ciò non dovrebbe comportare un sovraccarico molto significativo. Se si utilizza HTTPS, questa operazione esatta viene eseguita alcune centinaia di volte solo per quella per una singola pagina HTML, quindi questa dovrebbe essere banale. –

+0

è un must usare non sicuri e puntatori? quelli non sono realmente sul mio menu giornaliero :) – RuSh

1

Hai provato URL encoding il testo della stringa di query? E 'parte della classe HttpUtility cui:

fornisce i metodi per la codifica e la decodifica URL durante l'elaborazione Web richieste.

e dovrebbe consentire di passare il testo codificato Base64 nella stringa di query.

+0

per risposta, io non voglio i caratteri come% F2 o% B2 nell'URL. – RuSh

0

Eseguire la crittografia e quindi utilizzare HttpServerUtility.UrlTokenEncode() per codificare l'array di byte.

+0

provato, l'url includerà ancora i caratteri "-" e "_". – RuSh

+0

Siamo spiacenti, ho perso il requisito aggiuntivo che tutti i caratteri dovevano essere alfanumerici. Se questo non è un requisito assoluto, è sicuro includere "-" e "_" in un parametro url ... – Peter

4

Quindi ecco un esempio funzionante che ho messo insieme da alcuni esempi diversi che prende un ID intero e lo converte in una stringa crittografata esadecimale. Questa stringa crittografata non dovrebbe includere caratteri URL-unfriendly e non includerà caratteri di escape.

Ecco l'intera app della console di lavoro. Si prega di notare che si tratta di un prototipo e sicuramente non per la produzione: questo illustra solo una soluzione e deve essere sicuramente rifatto.

Quando si esegue il codice, l'output dovrebbe essere questo:

1234 get encrypted as ZaB5GE/bWMJcNaeY/xJ6PQ== 
ZaB5GE/bWMJcNaeY/xJ6PQ== encrypted is this in hex 5a61423547452f62574d4a634e6165592f784a3650513d3d 
5a61423547452f62574d4a634e6165592f784a3650513d3d gets dehexed as ZaB5GE/bWMJcNaeY/xJ6PQ== 
ZaB5GE/bWMJcNaeY/xJ6PQ== got decrypted as 1234 

Fonti:
byte per articolo esadecimale su SO: Encryption to alphanumeric in System.Security.Cryptography
Crypto classe helper: Encrypt and decrypt a string (4 ° risposta)

Program2.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

using System.Security.Cryptography; 
using System.IO; 

namespace ConsoleApplication1 
{ 
    class Program2 
    { 
     static void Main(string[] args) 
     { 
      int theId = 1234; //the ID that's being manipulated 
      byte[] byteArray; //the byte array that stores 

      //convert the ID to an encrypted string using a Crypto helper class 
      string encryptedString = Crypto.EncryptStringAES(theId.ToString(), "mysecret"); 
      Console.WriteLine("{0} get encrypted as {1}", theId.ToString(), encryptedString); 

      //convert the encrypted string to byte array 
      byteArray = ASCIIEncoding.Default.GetBytes(encryptedString); 
      StringBuilder result = new StringBuilder(); 

      //convert each byte to hex and append to a stringbuilder 
      foreach (byte outputByte in byteArray) 
      { 
       result.Append(outputByte.ToString("x2")); 
      } 

      Console.WriteLine("{0} encrypted is this in hex {1}", encryptedString, result.ToString()); 

      //now reverse the process, and start with converting each char in string to byte 
      int stringLength = result.Length; 
      byte[] bytes = new byte[stringLength/2]; 

      for (int i = 0; i < stringLength; i += 2) 
      { 
       bytes[i/2] = System.Convert.ToByte(result.ToString().Substring(i, 2), 16); 
      } 

      //convert the byte array to de-"hexed" string 
      string dehexedString = ASCIIEncoding.Default.GetString(bytes); 

      Console.WriteLine("{0} gets dehexed as {1}", result, dehexedString); 

      //decrypt the de-"hexed" string using Crypto helper class 
      string decryptedString = Crypto.DecryptStringAES(dehexedString, "mysecret"); 
      Console.WriteLine("{0} got decrypted as {1}", dehexedString, decryptedString); 

      Console.ReadLine(); 
     } 
    } 

    public class Crypto 
    { 
     private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5"); 

     /// <summary> 
     /// Encrypt the given string using AES. The string can be decrypted using 
     /// DecryptStringAES(). The sharedSecret parameters must match. 
     /// </summary> 
     /// <param name="plainText">The text to encrypt.</param> 
     /// <param name="sharedSecret">A password used to generate a key for encryption.</param> 
     public static string EncryptStringAES(string plainText, string sharedSecret) 
     { 
      if (string.IsNullOrEmpty(plainText)) 
       throw new ArgumentNullException("plainText"); 
      if (string.IsNullOrEmpty(sharedSecret)) 
       throw new ArgumentNullException("sharedSecret"); 

      string outStr = null;      // Encrypted string to return 
      RijndaelManaged aesAlg = null;    // RijndaelManaged object used to encrypt the data. 

      try 
      { 
       // generate the key from the shared secret and the salt 
       Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); 

       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged(); 
       aesAlg.Key = key.GetBytes(aesAlg.KeySize/8); 
       aesAlg.IV = key.GetBytes(aesAlg.BlockSize/8); 

       // Create a decrytor to perform the stream transform. 
       ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 

       // Create the streams used for encryption. 
       using (MemoryStream msEncrypt = new MemoryStream()) 
       { 
        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
        { 
         using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
         { 

          //Write all data to the stream. 
          swEncrypt.Write(plainText); 
         } 
        } 
        outStr = Convert.ToBase64String(msEncrypt.ToArray()); 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      // Return the encrypted bytes from the memory stream. 
      return outStr; 
     } 

     /// <summary> 
     /// Decrypt the given string. Assumes the string was encrypted using 
     /// EncryptStringAES(), using an identical sharedSecret. 
     /// </summary> 
     /// <param name="cipherText">The text to decrypt.</param> 
     /// <param name="sharedSecret">A password used to generate a key for decryption.</param> 
     public static string DecryptStringAES(string cipherText, string sharedSecret) 
     { 
      if (string.IsNullOrEmpty(cipherText)) 
       throw new ArgumentNullException("cipherText"); 
      if (string.IsNullOrEmpty(sharedSecret)) 
       throw new ArgumentNullException("sharedSecret"); 

      // Declare the RijndaelManaged object 
      // used to decrypt the data. 
      RijndaelManaged aesAlg = null; 

      // Declare the string used to hold 
      // the decrypted text. 
      string plaintext = null; 

      try 
      { 
       // generate the key from the shared secret and the salt 
       Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); 

       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged(); 
       aesAlg.Key = key.GetBytes(aesAlg.KeySize/8); 
       aesAlg.IV = key.GetBytes(aesAlg.BlockSize/8); 

       // Create a decrytor to perform the stream transform. 
       ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); 
       // Create the streams used for decryption.     
       byte[] bytes = Convert.FromBase64String(cipherText); 
       using (MemoryStream msDecrypt = new MemoryStream(bytes)) 
       { 
        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
        { 
         using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 

          // Read the decrypted bytes from the decrypting stream 
          // and place them in a string. 
          plaintext = srDecrypt.ReadToEnd(); 
        } 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      return plaintext; 
     } 
    } 

} 
+0

cool funziona, solo la stringa codificata è veramente lunga. – RuSh

+0

Felice che funzioni! Sfortunatamente è lungo a causa dei caratteri alfanumerici limitati che possono essere utilizzati. :) –

3

Il problema con l'offuscamento dell'ID è che è necessario un modo per de-offuscare. Ciò richiede:

  1. Crittografia fullblown, che se è necessaria richiede un valore piuttosto elevato.
  2. Memorizzando il valore con il numero id, diventa quindi un identificatore alternativo.
  3. Qualcosa che dipende dalla sicurezza per oscurità.

In alternativa, mantenere l'ID libero, ma utilizzare anche un controllo.

public static String ChkSumStr(int id, int reduce) 
{ 
    return string.Concat(ReduceStrength(ChkSum(id), reduce).Select(b => b.ToString("X2")).ToArray()); 
} 
public static byte[] ChkSum(int id) 
{ 
    byte[] idBytes = Encoding.UTF8.GetBytes("This is an arbitrary salt" + id); 
    return SHA256.Create().ComputeHash(idBytes); 
} 
private static byte[] ReduceStrength(byte[] src, int reduce) 
{ 
    byte[] ret = null; 
    for(int i = 0; i != reduce; ++i) 
    { 
    ret = new byte[src.Length/2]; 
    for(int j = 0; j != ret.Length; ++j) 
    { 
     ret[j] = (byte)(src[j * 2]^src[j * 2 + 1]); 
    } 
    src = ret; 
    } 
    return src; 
} 

Maggiore è il valore specificato per ridurre, minore è il risultato (fino a 6 si continua a produrre la stringa vuota). Un valore basso (o 0) offre una sicurezza migliore, al costo di un URI più lungo.

La stringa "This is an arbitrary salt" deve essere segreta per la massima sicurezza. Può essere hardcoded in alcuni usi, ma vorrebbe essere ottenuto da una fonte sicura per gli altri.

Con quanto sopra, un id di 15 e un reduce di 3 produce un risultato di 05469B1E. Possiamo quindi utilizzare questo come:

www.domain.com/?id=15&chk=05469B1E

Nel gestore che guardiamo in alto qualunque sia 15 è, noi facciamo la stessa cosa, e se il risultato è diverso da 05469B1E siamo in grado di restituire un 403 Forbidden o verosimilmente più ragionevole 404 non trovato (sulla base del fatto che abbiamo ricevuto un URI che nel suo insieme non identifica nulla).

Problemi correlati