запутывание параметров строки запроса - PullRequest
7 голосов
/ 26 августа 2010

Я хочу скрыть один параметр строки запроса в ASP.NET.У сайта будет большой объем запросов, поэтому алгоритм не должен быть слишком медленным.

Моя проблема в том, что все найденные мной алгоритмы приводят к нежелательным символам (например, + / =)

Вот пример того, чего я хочу достичь:

www.domain.com/?id=1844

до

www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ

Запутанный параметр должен включать только az и AZ и 0-9

Я знаю, что могу зашифровать с помощью base64, но это приведет к появлению нежелательных символов, таких как / или = или +.

Есть идеи, какой алгоритм можно использовать?

Обновление: Мне известно о UrlEncoding, я хочу избежать кодирования строки.потому что это будет генерировать charaters, такие как% F2 или% B2 в URL.

Ответы [ 6 ]

5 голосов
/ 26 августа 2010

Вы можете использовать тройной DES для кодирования значения, используя блочный шифр 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);
        }
    }
}

Выходные данные должны быть примерно такими:шириной всего 16 символов.

Если вы действительно обеспокоены злоупотреблениями, то AES можно использовать аналогичным образом.В следующем примере я включаю AES и записываю 64-битный номер идентификатора в обе стороны блока.Если он не декодируется с одинаковым значением с обеих сторон, он отклоняется.Это может помешать людям писать случайными числами.

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.");
        }
    }
}

Результат теперь должен выглядеть примерно так:

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.

Также обратите внимание, что поскольку мы теперь использовали более широкий блочный шифркодированное значение теперь имеет ширину 32 символа.

5 голосов
/ 26 августа 2010

Вы можете использовать HttpServerUtility.UrlTokenEncode и HttpServerUtility.UrlTokenDecode

Кодировка использует кодировку base64, но заменяет недружественные символы URL.

Есть аналогичный ответ в предыдущем ТАК вопрос . Смотрите принятый ответ.

4 голосов
/ 26 августа 2010

Итак, вот рабочий пример, который я собрал из нескольких разных примеров, которые берут целочисленный идентификатор и преобразуют его в зашифрованную строку в шестнадцатеричном формате.Эта зашифрованная строка не должна содержать недружественные к URL символы и также не будет экранированных символов.

Вот и все рабочее консольное приложение.Обратите внимание, что это прототип и определенно не для производства - это просто иллюстрирует решение и определенно нуждается в рефакторинге.

Когда вы запускаете код, ваш вывод должен быть таким:

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

Источники: Статья в байтах и ​​шестнадцатириях для SO: Шифрование в буквенно-цифровую форму в System.Security.Cryptography Класс вспомогательного шифрования: Шифрование и дешифрование строки (4-й ответ)

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

}
3 голосов
/ 26 августа 2010

Проблема с запутыванием идентификатора заключается в том, что вам нужен способ обфусцировать.Для этого требуется либо:

  1. Полноценное шифрование, которое, если оно пригодится, потребует довольно большого значения.
  2. Сохранение значения вместе с номером идентификатора, поэтому оно становится альтернативным идентификатором.
  3. Что-то, что зависит от безопасности по незаметности.

В качестве альтернативы, оставьте идентификатор пустым, но используйте также проверку.

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

Чем вышезначение, заданное для Reduce, тем меньше результат (до тех пор, пока на 6 он не выдаст пустую строку).Низкое значение (или 0) обеспечивает лучшую защиту за счет более длинного URI.

Строка "This is an arbitrary salt" должна быть секретной для лучшей безопасности.В некоторых случаях он может быть жестко закодирован, но для других он может быть получен из безопасного источника.

С учетом вышеизложенного, id 15 и reduce 3 дают результат 05469B1E.Затем мы можем использовать это как:

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

В обработчике, который будет искать все 15, мы делаем то же самое снова, и если результат отличается от 05469B1E, мы можемлибо вернуть 403 Запрещено, либо, возможно, более разумно 404 Не найдено (на основании того, что мы получили URI, который в целом ничего не идентифицирует).

1 голос
/ 26 августа 2010

Вы пробовали кодировка URL текст строки запроса? Это часть класса HttpUtility , который:

Предоставляет методы для кодирования и декодирование URL при обработке веб запросы.

и должен позволять передавать кодированный в base64 текст в строке запроса.

0 голосов
/ 26 августа 2010

Выполните шифрование, а затем используйте HttpServerUtility.UrlTokenEncode () для кодирования массива байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...