【问题标题】:Encrypting an id in an URL in ASP.NET MVC在 ASP.NET MVC 中的 URL 中加密 id
【发布时间】:2010-10-28 02:11:36
【问题描述】:

我正在尝试对 URL 中的加密 id 进行编码。像这样:http://www.calemadr.com/Membership/Welcome/9xCnCLIwzxzBuPEjqJFxC6XJdAZqQsIDqNrRUJoW6229IIeeL4eXl5n1cnYapg+N

但是,它要么编码不正确,我在加密中得到斜杠“/”,要么我从 IIS 收到错误消息:请求过滤模块被配置为拒绝包含双转义序列的请求。

我尝试了不同的编码,但都失败了:

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

更新

问题是当我加密一个 Guid 并将其转换为 base64 字符串时,它会包含不安全的 url 字符。当然,当我尝试导航到包含不安全字符的 url 时,IIS(7.5/windows 7) 会崩溃。 Url 对 base64 加密字符串进行编码会在 IIS 中引发错误(请求过滤模块被配置为拒绝包含双转义序列的请求。)。我不确定它是如何检测双编码字符串的,但确实如此。

尝试上述方法对base64加密字符串进行编码后。我决定删除 base64 编码。但是,这会将加密文本保留为 byte[]。我尝试对字节 [] 进行 UrlEncoding,它是 httpUtility.Encode 方法的重载之一。同样,虽然它是 URL 编码的,但 IIS 不喜欢它并提供了一个“找不到页面”。

在网上搜索后,我遇到了一个HexEncoding/Decoding 类。 将十六进制编码应用于加密字节就可以了。输出是 url 安全的。另一方面,我在解码和解密十六进制字符串方面没有任何问题。

【问题讨论】:

标签: asp.net-mvc encryption


【解决方案1】:

使用 HttpServerUtility.UrlTokenEncodeHttpServerUtility.UrlTokenDecode 将字节数组转换为 URL 安全字符串。

C# Byte[] to Url Friendly String

【讨论】:

    【解决方案2】:

    我写了一篇关于这个主题的简短博客post,包括完整的源代码。

    它使您能够使用 16 字符密钥加密和解密以查询字符串形式存储的数据:

    我找到了一组很棒的基类来解决这个问题,但大多数情况下 部分归结为一类。这个类需要一个 16 字符的键 某种进行加密和加密的值。你也可以 如果需要,设置过期值。

    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; }
            }
        }
    }
    

    要加密一些值并将其传递给 MVC 中的另一个操作,您可以 执行以下操作。

    [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()));
    }
    

    在我们重定向到的操作中,您需要具有相同的 密钥和查询字符串值本身来解密它。请记住, 如果您没有正确的密钥或者您尝试解密该值 过期后,类会抛出异常。

    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();
    }
    

    我没有花太多时间深入解释源代码,因为它有 好久没写了。还要记住,这是很久以前的事了 我的测试第一天......(但它似乎确实有效)

    与往常一样,此示例的source code 可供下载。

    【讨论】:

      【解决方案3】:

      加密和编码是有区别的;这些方法不适合加密。

      因为加密很难做到正确,而且非常容易出错(虽然看起来仍然像正确的解决方案一样“加密”),所以我建议您改用 GUID ID:

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

      SQL server 具有仅适用于这种情况的 uniqueidentifier 类型。

      【讨论】:

      • 第二。如果目的只是混淆,则对 ID 使用加密是没有意义的。
      • 抱歉,我的问题不够详细。我正在尝试将加密字符串编码为 URL 安全。讽刺的是,我用的是Guids,上面的加密就是一个Guid。问题是对加密字符串进行编码,该字符串不会导致 IIS 引发安全警告或看起来是目录结构(包含斜杠“/”)@Womp - 由于系统的性质,我必须加密 id。
      • 嗯。那你为什么要加密 GUID?如果您担心未经授权的人重用 URL,他们同样可以重用您作为示例提供的 URL。如果您担心有人猜测其他人的 GUID...永远不会发生。
      • @MarcelPopescu:我希望您了解索引 GUID(集群或非集群)的含义...这些索引较慢,因为 SQL 无法仅通过 GUID 确定页面。它必须自动扫描整个索引,当您使用像int identity 这样的连续索引时当然不是这种情况。这是人们避免使用 GUID 的主要原因。
      • @MarcelPopescu 也许他正在使用Sequential GUIDs
      【解决方案4】:

      这篇文章可能很旧,但在这里你有另一个解决方案...... 当您要加密 .ToBase64String 时,url 编码/解码会更改加密的字符串。

      在解码之前在你的加密库(或函数)上试试这个:

      Myencodedid.Replace(' ', '+')
      

      然后,去解密!..

      【讨论】:

        【解决方案5】:

        嗯...这可能不会有任何区别,但您可以尝试 AntiXSS 库和它的 URLEncode() 方法。

        http://www.codeplex.com/AntiXSS

        HTH, 查尔斯

        【讨论】:

        • 我以前用过这个库。我试试看。
        【解决方案6】:

        我很惊讶 UrlEncode 不起作用。您的加密输出是什么样的?

        加密您的 Guid 后,尝试使用 Convert.ToBase64String 方法将其编码为 Base64。然后对 Base64 字符串进行 UrlEncode,使其成为可以包含在 URL 中的可接受字符串。

        【讨论】:

        • 最好只用 - 和 _ 替换 URL 的 / 和 +,而不是对它们进行 urlencoding。
        • 但是你还需要取消编码,那么你怎么知道哪个 - 是破折号,哪个 - 是斜线?
        【解决方案7】:

        不知道这对你来说是否重要,但我只是自己解决了这个问题。我不得不加倍 urlencode。

        例如

        Server.UrlEncode(Server.UrlEncode(要编码的字符串))

        问题似乎是 Request.Querystring(encoded string) 自动进行解码,这会破坏加密。我希望我能解释得更好,但我还是有点困惑

        【讨论】:

          【解决方案8】:

          首先像这样创建一个类:

          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;
              }
          }
          

          在 Controller 中,为这个 Ecription 类添加引用,如下所示:

          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();
                      }
          
                  }
          

          在视图中:

          <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>
          }
          

          我希望这有用。

          【讨论】:

          • OP 询问的是 URI 加密,而不是一般的表单字段加密
          猜你喜欢
          • 2011-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-25
          • 1970-01-01
          • 1970-01-01
          • 2011-05-27
          相关资源
          最近更新 更多