2009-05-21 16 views
10

Estoy intentando codificar la identificación cifrada en la URL. De esta manera: http://www.calemadr.com/Membership/Welcome/9xCnCLIwzxzBuPEjqJFxC6XJdAZqQsIDqNrRUJoW6229IIeeL4eXl5n1cnYapg+NEncriptación de una identificación en una URL en ASP.NET MVC

Sin embargo, tampoco no codifica correctamente y consigo una barra '/' en el cifrado o recibo y el error de IIS: El módulo de filtrado de solicitudes está configurado para rechazar una solicitud que contenga una secuencia de escape doble

He intentado diferentes codificaciones, cada falla:

  • HttpUtility.HtmlEncode
  • HttpUtility.UrlEncode
  • HttpUtility.UrlPathEncode
  • HttpUtility.UrlEncodeUnicode

actualización

El problema era que cuando encriptaba un Guid y lo convertía en una cadena base64, contenía caracteres de URL inseguros. Por supuesto, cuando intenté navegar a una url que contenía caracteres inseguros, IIS (7.5/windows 7) explotaría. Url La codificación de la cadena cifrada en base64 generaría un error en IIS (El módulo de filtrado de solicitudes está configurado para denegar una solicitud que contiene una secuencia de escape doble.). No estoy seguro de cómo detecta cadenas de doble codificación, pero lo hizo.

Después de probar los métodos anteriores para codificar la cadena encriptada base64. Decidí eliminar la codificación base64. Sin embargo, esto deja el texto encriptado como un byte []. Intenté UrlEncoding the byte [], es una de las sobrecargas que cuelga del método httpUtility.Encode. Nuevamente, mientras estaba codificado en la URL, a IIS no le gustó y le mostró una "página no encontrada" .

Después de cavar en la red me encontré con una clase HexEncoding/Decoding. La aplicación de la codificación hexadecimal a los bytes cifrados hizo el truco. La salida es url segura. Por otro lado, no he tenido ningún problema con la decodificación y el descifrado de las cadenas hexagonales.

+1

Su actualización es mucho mejor que la respuesta. Y si desea que las URL codificadas en Base64 sean seguras, lea esto: http://stackoverflow.com/a/10858198/237858 – kape123

Respuesta

7

Escribí un breve blog post sobre este mismo tema, incluyendo el código fuente completo.

le permite cifrar y descifrar los datos almacenados en forma de cadena de consulta utilizando una clave de 16 Char:

me encontré con un gran conjunto de clases base para resolver esto, pero para el parte más todo se reduce a una clase. Esta clase requiere una clave de 16 caracteres de para hacer el cifrado y un valor para encriptar. También puede establecer un valor de caducidad si es necesario.

using System.Collections.Specialized; 
using System.Security; 
using System.Text; 
using System.Web; 
using EncryptionMVC.Security.Encryption.Utility.Interfaces; 
using EncryptionMVC.Security.Encryption.Utility; 
namespace Security.Encryption.QueryString 
{ 
    /// 
    /// Provides a secure means for transfering data within a query string. 
    /// 
    public class SecureQueryString : NameValueCollection 
    { 

     private string timeStampKey = '__TS__'; 
     private string dateFormat = 'G'; 
     private IEncryptionUtility mEncryptionUtil; 
     private DateTime m_expireTime = DateTime.MaxValue; 

     /// 
     /// Creates an instance with a specified key. 
     /// 
     /// The key used for cryptographic functions, required 16 chars in length. 
     public SecureQueryString(string key) : base() 
     { 
      mEncryptionUtil = new EncryptionUtility(key); 
     } 

     /// 
     /// Creates an instance with a specified key and an encrypted query string. 
     /// 
     /// The key used for cryptographic functions, required 16 chars in length. 
     /// An encrypted query string generated by a instance. 
     public SecureQueryString(string key, string queryString) : this(key) 
     { 
      Deserialize(DecryptAndVerify(queryString)); 
      CheckExpiration(); 
     } 

     /// 
     /// Returns a encrypted query string. 
     /// 
     /// 
     public override string ToString() 
     { 
      return EncryptAndSign(Serialize()); 
     } 

     private void Deserialize(string queryString) 
     { 
      string[] nameValuePairs = queryString.Split('&'); 
      for (int i = 0; i <= nameValuePairs.Length - 1; i++) { 
       string[] nameValue = nameValuePairs(i).Split('='); 
       if (nameValue.Length == 2) { 
        base.Add(nameValue(0), nameValue(1)); 
       } 
      } 

      if (base.GetValues(timeStampKey) != null) { 
       string[] strExpireTime = base.GetValues(timeStampKey); 
       m_expireTime = Convert.ToDateTime(strExpireTime(0)); 
      } 
     } 

     private string Serialize() 
     { 
      StringBuilder sb = new StringBuilder(); 
      foreach (string key in base.AllKeys) { 
       sb.Append(key); 
       sb.Append('='); 
       sb.Append(base.GetValues(key)(0).ToString()); 
       sb.Append('&'); 
      } 

      sb.Append(timeStampKey); 
      sb.Append('='); 
      sb.Append(m_expireTime.ToString(dateFormat)); 

      return sb.ToString(); 
     } 

     private string DecryptAndVerify(string input) 
     { 
      return mEncryptionUtil.Decrypt(input); 
     } 

     private string EncryptAndSign(string input) 
     { 
      return mEncryptionUtil.Encrypt(input); 
     } 

     private void CheckExpiration() 
     { 
      if (DateTime.Compare(m_expireTime, DateTime.Now) < 0) { 
       throw new ExpiredQueryStringException(); 
      } 
     } 

     /// 
     /// Gets or sets the timestamp in which this string should expire 
     /// 
     public DateTime ExpireTime { 
      get { return m_expireTime; } 
      set { m_expireTime = value; } 
     } 
    } 
} 

Para cifrar un cierto valor y pasarlo a otra acción en el MVC le hacer algo como el siguiente.

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(FormCollection collection) 
{ 
    SecureQueryString qs = new SecureQueryString(mKey); 

    qs('YourName') = collection('name'); 
    qs.ExpireTime = DateTime.Now.AddMinutes(2); 

    Response.Redirect('Home.aspx/About?data=' + HttpUtility.UrlEncode(qs.ToString())); 
} 

En la acción que redirigimos a, usted tendría que tener esta misma clave y el mismo valor de cadena de consulta para descifrarlo. Tenga en cuenta que si no tiene la clave correcta o si intenta descifrar el valor después de la caducidad, la clase emitirá una excepción.

public ActionResult About() 
{ 
    if (Request('data') != null) { 
     try { 
      SecureQueryString qs = new SecureQueryString(mKey, Request('data')); 

      ViewData('Message') = 'Your name is ' + qs('YourName'); 
     } 
     catch (Exception ex) { 

     } 
    } 
    return View(); 
} 

que no pasamos mucho tiempo explicando la fuente en profundidad, ya que tiene pasado tanto tiempo desde que lo escribí. También tenga en cuenta que esto fue mucho antes de mis primeros días de prueba ... (pero parece que funciona)

Como siempre, el source code para este ejemplo está disponible para su descarga.

2

Hay una diferencia entre cifrado y codificación; esos métodos no fueron diseñados para el cifrado.

Debido a que el cifrado es difícil de hacerlo bien, y muy fácil equivocarse (mientras que todavía parece simplemente como "cifrado" como la solución derecha), le recomiendo que en vez de utilizar los identificadores GUID:

http://www.calemadr.com/.../ {6F0184E4- 809F-4e30-8A5B-4DC144135A54}

El servidor SQL tiene el tipo de identificador único para este caso.

+0

Secundado. No tiene sentido utilizar el cifrado en las identificaciones si el propósito es solo ofuscación. – womp

+2

Mis disculpas, no fui lo suficientemente detallada en la pregunta. Estoy tratando de codificar una cadena encriptada para que sea URL segura. Irónicamente, estoy usando Guids, el cifrado anterior es un Guid. El problema es codificar una cadena encriptada que no causará que IIS arroje una advertencia de seguridad o parezca ser una estructura de directorio (contiene barras inclinadas '/') @Womp -Debido a la naturaleza del sistema, tengo que encriptar la identificación . –

+1

Hmm. ¿Por qué estás encriptando el GUID, entonces? Si le preocupa alguien que no está autorizado a reutilizar una URL, también podría reutilizar la URL que proporcionó como ejemplo. Si te preocupa que alguien * adivine * el GUID de otra persona ... nunca va a suceder. –

1

Me sorprende que UrlEncode no funcione. ¿Cómo se ve el resultado de tu encriptación?

Después de encriptar su Guid, intente codificarlo en Base64 con el método Convert.ToBase64String. Luego urlEncode la cadena Base64 para que sea una cadena aceptable para ser incluida en su URL.

+2

Podría ser mejor reemplazar el/y + con - y _ para las URL, en lugar de urlcocodificarlas. –

+0

Pero también necesita descodificar, entonces, ¿cómo sabría cuál es un guión y cuál es una barra inclinada? – CoderDennis

1

Hmmm ... Esto probablemente no hará ninguna diferencia, pero podría probar la biblioteca AntiXSS y su método URLEncode().

http://www.codeplex.com/AntiXSS

HTHS, Charles

+0

He usado esta biblioteca antes. Lo intentaré. –

1

No sé si importa más a usted, pero yo sólo resuelve este problema por mi cuenta. Tuve que duplicar urlencode.

Por ejemplo

Server.URLEncode (Server.URLEncode (cadena para codificar))

El problema parece ser que Request.Querystring (cadena codificada) hace automáticamente una decodificación que se atornilla el cifrado . Ojalá pudiera explicar mejor, pero todavía estoy un poco confundido

10

Utilice HttpServerUtility.UrlTokenEncode y HttpServerUtility.UrlTokenDecode para convertir la matriz de bytes en una cadena segura para URL.

Ver C# Byte[] to Url Friendly String.

+0

+1 Gracias por el enlace. –

2

Esta publicación puede ser anterior, pero aquí tiene otra solución ... Cuando vaya a cifrar .ToBase64String, la codificación/descodificación de la URL cambia la cadena cifrada.

Pruebe esto en su biblioteca de encriptación (o función) antes de realizar la decodificación:

Myencodedid.Replace(' ', '+') 

Y entonces, ir a por el descifrado ..

1

En primer lugar crear una clase como esta:

public class Encryption 
{ 
    public static string Encrypt(string clearText) 
    { 
     string EncryptionKey = "MAKV2SPBNI99212"; 
     byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); 
     using (Aes encryptor = Aes.Create()) 
     { 
      Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); 
      encryptor.Key = pdb.GetBytes(32); 
      encryptor.IV = pdb.GetBytes(16); 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) 
       { 
        cs.Write(clearBytes, 0, clearBytes.Length); 
        cs.Close(); 
       } 
       clearText = Convert.ToBase64String(ms.ToArray()); 
      } 
     } 
     return clearText; 
    } 

    public static string Decrypt(string cipherText) 
    { 
     string EncryptionKey = "MAKV2SPBNI99212"; 
     byte[] cipherBytes = Convert.FromBase64String(cipherText); 
     using (Aes encryptor = Aes.Create()) 
     { 
      Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); 
      encryptor.Key = pdb.GetBytes(32); 
      encryptor.IV = pdb.GetBytes(16); 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) 
       { 
        cs.Write(cipherBytes, 0, cipherBytes.Length); 
        cs.Close(); 
       } 
       cipherText = Encoding.Unicode.GetString(ms.ToArray()); 
      } 
     } 
     return cipherText; 
    } 
} 

En controlador, en agregar referencia para esta clase Ecription así:

using testdemo.Models 

public ActionResult Index() { 
      return View(); 
     } 
     [HttpPost] 
     public ActionResult Index(string text) 
     { 
      if (Request["txtEncrypt"] != null) 
      { 
       string getEncryptionCode = Request["txtEncrypt"]; 
       string DecryptCode = Encryption.Decrypt(HttpUtility.UrlDecode(getEncryptionCode)); 
       ViewBag.GetDecryptCode = DecryptCode; 
       return View(); 
      } 
      else { 
       string getDecryptCode = Request["txtDecrypt"]; 
       string EncryptionCode = HttpUtility.UrlEncode(Encryption.Encrypt(getDecryptCode)); 
       ViewBag.GetEncryptionCode = EncryptionCode; 
       return View(); 
      } 

     } 

En Vista:

<h2>Decryption Code</h2> 
@using (Html.BeginForm()) 
{ 
    <table class="table-bordered table"> 
     <tr> 
      <th>Encryption Code</th> 
      <td><input type="text" id="txtEncrypt" name="txtEncrypt" placeholder="Enter Encryption Code" /></td> 
     </tr> 
     <tr> 
      <td colspan="2"> 
       <span style="color:red">@ViewBag.GetDecryptCode</span> 
      </td> 
     </tr> 
     <tr> 
       <td colspan="2"> 
        <input type="submit" id="btnEncrypt" name="btnEncrypt"value="Decrypt to Encrypt code" /> 
       </td> 
      </tr> 
    </table> 
} 
    <br /> 
    <br /> 
    <br /> 
    <h2>Encryption Code</h2> 
@using (Html.BeginForm()) 
{ 
    <table class="table-bordered table"> 
     <tr> 
      <th>Decryption Code</th> 
      <td><input type="text" id="txtDecrypt" name="txtDecrypt" placeholder="Enter Decryption Code" /></td> 
     </tr> 

     <tr> 
      <td colspan="2"> 
       <span style="color:red">@ViewBag.GetEncryptionCode</span> 
      </td> 
     </tr> 
     <tr> 
      <td colspan="2"> 
       <input type="submit" id="btnDecryt" name="btnDecryt" value="Encrypt to Decrypt code" /> 
      </td> 
     </tr> 
    </table> 
} 

espero que esto útil.

+0

El OP preguntó sobre el cifrado de URI, no el cifrado de campo de formulario general – Fandango68

Cuestiones relacionadas