简体   繁体   English

查询字符串参数混淆

[英]query string parameter obfuscation

I want to obfuscate one query string parameter in ASP.NET. 我想在ASP.NET中混淆一个查询字符串参数。 The site will have a high volume of request, so the algorithm shouldn't be too slow. 该站点将有大量请求,因此算法不应该太慢。

My problem is that all the algorithms I found result in unwanted characters (like +/=) 我的问题是我找到的所有算法都会产生不需要的字符(比如+ / =)

Here is an example of what i want to achieve: 这是我想要实现的一个例子:

www.domain.com/?id=1844

to

www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ

The obfuscated param should only include az and AZ and 0-9 characters. 混淆的参数应该只包括az和AZ以及0-9个字符。

I know I can encrypt using base64, but this will generate unwanted characters such as / or = or + . 我知道我可以使用base64进行加密,但这会生成不需要的字符,例如/=+

Any idea what algorithm can be used? 知道可以使用什么算法吗?

Update: I'm aware of UrlEncoding , i want to avoid encoding the string. 更新:我知道UrlEncoding,我想避免编码字符串。 because that will generate charaters like %F2 or %B2 in the url. 因为这会在网址中生成%F2或%B2等字符。

You can use HttpServerUtility.UrlTokenEncode and HttpServerUtility.UrlTokenDecode 你可以使用HttpServerUtility.UrlTokenEncodeHttpServerUtility.UrlTokenDecode

Encode uses base64 encoding, but replaces URL unfriendly characters. Encode使用base64编码,但替换URL不友好的字符。

There's a similar answer in a previous SO question . 之前的SO问题中有类似的答案。 See the accepted answer. 看到接受的答案。

You can use triple DES to encode the value using a narow block cipher. 您可以使用三重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);
        }
    }
}

The output should be something like this: 输出应该是这样的:

Number to encode = 873435734.
Key to encode with is 38137b6a7aa49cc6040c4297064fdb4461c79a895f40b4d1.
The encoded value is 43ba3fb809a47b2f.
The decoded result is 873435734.

Note that the encoded value is only 16 characters wide. 请注意,编码值仅为16个字符宽。

If you're really conserned about abuse, then AES can be used in a similar manner. 如果您真的对滥用行为有所了解,那么可以以类似的方式使用AES。 In the next example I switch in AES and write the 64 bit id number into both sides of the block. 在下一个例子中,我切换AES并将64位id号写入块的两侧。 If it doesn't decode with the same value on both sides then it is rejected. 如果它没有在两侧以相同的值进行解码,那么它将被拒绝。 This can prevent people from writing in random numbers. 这可以防止人们以随机数写入。

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

The result should now look something like this: 结果应该看起来像这样:

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.

Also note that since we have now used a wider block cipher the encoded value is now 32 characters wide. 另请注意,由于我们现在使用了更宽的分组密码,因此编码值现在为32个字符宽。

So here's a working example that I put together from a few different examples that takes an integer ID and converts it to a hexidecimal formatted encrypted string. 所以这是一个工作示例,我将几个不同的示例放在一起,这些示例采用整数ID并将其转换为十六进制格式的加密字符串。 This encrypted string should not include URL-unfriendly characters and will not include escaped characters either. 此加密字符串不应包含URL不友好的字符,也不包括转义字符。

Here's the entire working console app. 这是整个工作控制台应用程序。 Please note that it's a prototype and definitely not for production -- this just illustrates a solution and definitely needs to be refactored. 请注意,这是一个原型,绝对不适合生产 - 这只是说明了一个解决方案,肯定需要重构。

When you run the code, your output should be this: 运行代码时,输​​出应为:

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

Sources: 资料来源:
byte to hex article on SO: Encryption to alphanumeric in System.Security.Cryptography SO上的字节到十六进制文章: 在System.Security.Cryptography中加密到字母数字
Crypto helper class: Encrypt and decrypt a string (4th answer) 加密助手类: 加密和解密字符串 (第四个答案)

Program2.cs 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;
        }
    }

}

The problem with obfuscating the id, is that you need a way to de-obfuscicate. 混淆id的问题是你需要一种去模糊的方法。 This requires either: 这需要:

  1. Fullblown encryption, which if it's any good will require a pretty large value. Fullblown加密,如果它有任何好处将需要相当大的价值。
  2. Storing the value along with the id number, so it becomes an alternative identifier. 存储该值以及ID号,因此它成为替代标识符。
  3. Something that depends on security-by-obscurity. 取决于默默无闻的东西。

Alternatively, keep the id clear, but use a check as well. 或者,保持身份清楚,但也要使用支票。

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

The higher the value given for reduce, the smaller the result (until at 6 it keeps producing the empty string). 给予reduce的值越高,结果越小(直到6时它继续产生空字符串)。 A low value (or 0) gives better security, at the cost of a longer URI. 较低的值(或0)以较长的URI为代价提供更好的安全性。

The string "This is an arbitrary salt" needs to be secret for best security. 为了最好的安全性,字符串"This is an arbitrary salt"需要保密。 It can be hardcoded in some uses, but would want to be obtained from a secure source for others. 它可以在某些用途中进行硬编码,但是希望从其他用户的安全源获得。

With the above, an id of 15 and a reduce of 3 produces a result of 05469B1E. 如上所述, id为15且reduce为3会产生05469B1E的结果。 We can then use this as: 然后我们可以将其用作:

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

In the handler that would look up whatever 15 is, we do the same thing again, and if the result is different to 05469B1E we can either return a 403 Forbidden or arguably more reasonable 404 Not Found (on the basis that we've received a URI that as a whole doesn't identify anything). 在查找15是什么的处理程序中,我们再次做同样的事情,如果结果与05469B1E不同,我们可以返回403 Forbidden或可以说更合理的404 Not Found(基于我们收到的整个URI不识别任何东西)。

Have you tried URL encoding your query string text? 您是否尝试过查询字符串文本进行URL编码 It's part of the HttpUtility class which: 它是HttpUtility类的一部分:

Provides methods for encoding and decoding URLs when processing Web requests. 提供处理Web请求时编码和解码URL的方法。

and should allow you to pass your base64 encoded text in the query string. 并且应该允许您在查询字符串中传递base64编码的文本。

进行加密,然后使用HttpServerUtility.UrlTokenEncode()对字节数组进行编码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM