在ASP.NET MVC中加密URL中的id

时间:2009-05-21 22:30:44

标签: asp.net-mvc encryption

我正在尝试对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 byte [],这是挂起httpUtility.Encode方法的重载之一。同样,虽然它是URL编码的,但IIS并不喜欢它并且提供了一个“找不到的页面。”

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

8 个答案:

答案 0 :(得分:10)

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

请参阅C# Byte[] to Url Friendly String

答案 1 :(得分:7)

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

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

  

我发现了一组很好的基类来解决这个问题,但最重要的是   部分归结为一堂课。这个类需要16个char键   某种加密和加密值。你也可以   如果需要,设置到期值。

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

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

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可供下载。

答案 2 :(得分:2)

加密和编码之间存在差异;这些方法不适用于加密。

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

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

SQL服务器只有这种情况才具有uniqueidentifier类型。

答案 3 :(得分:2)

这篇文章可能已经过时但是你有另一个解决方案...... 当您要加密.ToBase64String时,url encode / decode会更改加密的字符串。

在进行解码之前,请在您的编码库(或函数)上尝试此操作:

Myencodedid.Replace(' ', '+')

然后,去解密!..

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

http://www.codeplex.com/AntiXSS

HTHS, 查尔斯

答案 6 :(得分:1)

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

例如

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

问题似乎是Request.Querystring(编码字符串)自动进行解码,这会破坏加密。我希望我能更好地解释,但我仍然有点困惑

答案 7 :(得分:1)

首先创建一个这样的类:

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

我希望这很有用。