Шифрование идентификатора в URL-адресе в ASP.NET MVC
Я пытаюсь закодировать зашифрованный идентификатор в Url. Например: http://www.calemadr.com/Membership/Welcome/9xCnCLIwzxzBuPEjqJFxC6XJdAZqQsIDqNrRUJoW6229IIeeL4eXl5n1cnYapg+N
Однако он либо неправильно кодирует, и я получаю косые черты "/" в шифровании, или я получаю и ошибки от IIS: Модуль фильтрации запросов настроен на отказ в запросе, который содержит двойную escape-последовательность.
Я пробовал разные кодировки, каждый из них терпит неудачу:
- HttpUtility.HtmlEncode
- HttpUtility.UrlEncode
- HttpUtility.UrlPathEncode
- HttpUtility.UrlEncodeUnicode
Обновление
Проблема была в том, что когда я зашифровал Guid и преобразовал его в строку base64, он будет содержать небезопасные URL-адреса. Конечно, когда я попытался перейти к URL-адресу, содержащему небезопасные символы, IIS (7.5/windows 7) взорвется. Url. Кодировка зашифрованной строки base64 будет повышаться и ошибка в IIS (модуль фильтрации запросов настроен на отказ от запроса, который содержит двойную escape-последовательность). Я не уверен, как он обнаруживает строки с двойной кодировкой, но это произошло.
После попытки вышеуказанных методов кодировать зашифрованную строку base64. Я решил удалить кодировку base64. Однако это оставляет зашифрованный текст байтом []. Я попробовал UrlEncoding byte [], это одна из перегрузок, зависающих от метода httpUtility.Encode. Опять же, хотя он был закодирован в URL, IIS не понравилось и подал "страницу не найдена".
После копания в сети я столкнулся с классом HexEncoding/Decoding.
Применение Hex Encoding к зашифрованным байтам сделало трюк. Выходной сигнал является безопасным. С другой стороны, у меня не было никаких проблем с расшифровкой и расшифровкой шестнадцатеричных строк.
Ответы
Ответ 1
Я написал короткий блог 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()));
}
В действии, которое мы перенаправляем, вам нужно будет иметь тот же самый ключ и значение строки запроса, чтобы расшифровать его. Имейте в виду, что если у вас нет правильного ключа или если вы попытаетесь расшифровать значение после истечения срока действия класс выдает исключение.
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();
}
Я не потратил много времени, объясняя источник в глубину, потому что он так давно я его написал. Также имейте в виду, что это было задолго до мой тест в первые дни... (но он действительно работает)
Как всегда, исходный код для этого примера доступен для загрузки.
Ответ 2
Используйте HttpServerUtility.UrlTokenEncode
и HttpServerUtility.UrlTokenDecode
для преобразования массива байтов в безопасную строку.
См. С# Byte [] для Url Friendly String.
Ответ 3
Есть разница между шифрованием и кодировкой; эти методы не предназначены для шифрования.
Поскольку шифрование трудно получить правильно, и невероятно легко ошибиться (при этом он выглядит так же, как "зашифровано" как правильное решение), я рекомендую вместо этого использовать идентификаторы GUID:
http://www.calemadr.com/.../ {6F0184E4-809F-4e30-8A5B-4DC144135A54}
SQL-сервер имеет тип uniqueidentifier только для этого случая.
Ответ 4
Это сообщение может быть старым, но здесь у вас есть другое решение...
Когда вы собираетесь шифровать .ToBase64String, кодировка/декодирование url изменяет зашифрованную строку.
Попробуйте это в своей библиотеке (или функции) регистрации перед выполнением декодирования:
Myencodedid.Replace(' ', '+')
И тогда, идите для дешифрования!..
Ответ 5
Я удивлен, что UrlEncode не работает. Как выглядит вывод вашего шифрования?
После того, как вы зашифруете свой Guid, попробуйте его кодировать на Base64 с помощью метода Convert.ToBase64String. Затем UrlEncode строку Base64, чтобы сделать ее приемлемой для включения в ваш URL.
Ответ 6
Hmmm... Это, вероятно, не будет иметь никакого значения, но вы можете попробовать библиотеку AntiXSS и метод URLEncode().
http://www.codeplex.com/AntiXSS
HTHS,
Чарльз
Ответ 7
Не знаю, имеет ли это значение для вас, но я просто решил эту проблему самостоятельно. Мне пришлось удвоить urlencode.
Например
Server.UrlEncode(Server.UrlEncode(строка для кодирования))
Проблема заключается в том, что Request.Querystring(закодированная строка) автоматически выполняет декодирование, которое зашифровывает шифрование. Хотел бы я лучше объяснить, но я все еще немного смущен.
Ответ 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;
}
}
В контроллере добавьте ссылку для этого класса 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>
}
Надеюсь, это полезно.