How to Secure the Interface of ASP. NET MVC OR WEBAPI with Signature

  • 2021-11-29 23:38:33
  • OfStack

Directory signature algorithm Parameters of signature Verify signature ApiController base class Prevention of Replay Attack Client call

When we developed an App, App needed to communicate with background services to obtain or submit data. If we don't have a perfect security mechanism, it is easy for people with no intention to forge requests and tamper with data.
Therefore, we need to use some kind of security mechanism to ensure that the request is legal. At present, the most commonly used method is to add a signature to each http request, and the server verifies the legitimacy of the signature. If the signature is legal, it will respond, and if the signature is illegal, it will directly reject the request.

Signature algorithm

Signature algorithm 1 generally uses Hash hash algorithm, commonly used MD5, SHA series algorithm. These algorithms can calculate different results according to different inputs, and the probability of collision is very low.
Signature algorithm and encryption algorithm are not the same thing. Many students will say that using MD5 encryption 1, in fact, this is wrong. The signature algorithm cannot recover the original data because it does not contain the information of the original data.
But the encryption method is different, the encryption method can calculate the original data again according to the encryption result.
As a more secure signature algorithm, HMAC SHA uses an Key to influence the signature results. In this way, the same input with different Key can get different signatures, which is more secure.


 public static string HmacSHA256(string secretKey,string plain)
        {
            var keyBytes = Encoding.UTF8.GetBytes(secretKey);
            var plainBytes = Encoding.UTF8.GetBytes(plain);

            using (var hmacsha256 = new HMACSHA256(keyBytes))
            {
                var sb = new StringBuilder();
                var hashValue = hmacsha256.ComputeHash(plainBytes);
                foreach (byte x in hashValue)
                {
                    sb.Append(String.Format("{0:x2}", x));
                }
                return sb.ToString();
            }
        }

Parameters of signature

With the signature algorithm, where does the content of our signature come from?
1 We use http to request queryString and then add time stamps and random numbers as signature parameters.


 public static string MakeSignPlain(SortedDictionary<string,string> queryString,string time,string random )
        {
            var sb = new StringBuilder();
            foreach (var keyValue in queryString)
            {
                sb.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (sb.Length>1)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append(time);
            sb.Append(random);

            return sb.ToString().ToUpper();
        }

Verify signature

Verifying a signature is simply comparing the signature produced by the server with the signature produced by the client.
One point to note is that it is best to verify the timestamp, which cannot differ by 5 minutes before and after comparing with the server time. This is also a simple means to prevent Replay Attack.


 public static bool Valid(string requestSign,string signPlain,string time, string secretKey)
        {
            if (string.IsNullOrEmpty(time)||string.IsNullOrEmpty(requestSign)||string.IsNullOrEmpty(signPlain))
            {
                return false;
            }
            //is in range
            var now = DateTime.Now;
            long requestTime =0;
            if (long.TryParse(time,out requestTime))
            {
                var max = now.AddMinutes(5).ToString("yyyyMMddHHmmss");
                var min = now.AddMinutes(-5).ToString("yyyyMMddHHmmss");
                if (!(long.Parse(max) >= requestTime && long.Parse(min) <= requestTime))
                {
                    return false;
                }
              
            }
            else
            {
                return false;
            }

            //hashmac
            var sign = Encryption.HmacSHA256(secretKey, signPlain);

            return requestSign.Equals(sign, StringComparison.CurrentCultureIgnoreCase);
        }

ApiController base class

With these foreshadowing, we can complete the signature verification in the base class. The client needs to put the timestamp, random number, signature and client's ID mentioned above into the headers of the http request.
We take out these data in the OnActionExecuting of the base class and combine them into the signature parameters, then obtain the Key of the signature according to the client ID, and then use the same signature algorithm to calculate the signature. And compare the signature of the client with that of the server.
I won't demonstrate it here.

Prevention of Replay Attack

There are two main points to prevent replay attacks:

Verify the range of timestamps

It is considered legal that the time stamp differs from the server time within a reasonable range.

Cache signature

Every time you request, determine whether the signature has appeared. If it appears, it will be regarded as an illegal request.
Because of the existence of time stamps and random numbers, it is theoretically impossible to duplicate the signature of each request.

Client call

Here, we demonstrate the code of C # signing and calling http interface under 1


 [TestMethod()]
        public void GetUserTest()
        {
            string url = "http://localhost:8090/api/test/GetUser";
            string userId = "A39891D4-6CEF-4538-A562-3A422CA9C17A";
            string appId = "100001";
            string secretKey = "M/vkPOWXgBa7GnRd73t7j+jsKfbZtb+f";
            string rumdon = Guid.NewGuid().ToString();
            string time = DateTime.Now.ToString("yyyyMMddHHmmss");

            //make signture plain text
            var sortDict = new SortedDictionary<string, string>()
            {
                {"userId",userId }
            };
            var signPlain = new StringBuilder();
            foreach (var keyValue in sortDict)
            {
                signPlain.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (signPlain.Length > 1)
            {
                //remove last &
                signPlain.Remove(signPlain.Length - 1, 1);
            }
            signPlain.Append(time);
            signPlain.Append(random);

            Console.WriteLine("sign plain:{0}", signPlain.ToString().ToUpper());
            //make sign
            var sign = Encryption.HmacSHA256(secretKey, signPlain.ToString().ToUpper());
            Console.WriteLine("sign:{0}", sign);

            string requestUrl = string.Format("{0}?{1}={2}", url, "userId", userId);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
            request.Method = "GET";
            //add headers
            request.Headers.Add("time", time);
            request.Headers.Add("appId", appId);
            request.Headers.Add("random", random);
            request.Headers.Add("sign", sign);
            //
            //start request
            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();

                            Console.WriteLine(content);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                using (HttpWebResponse response = (HttpWebResponse)ex.Response)
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();
                            Console.WriteLine(content);
                        }
                    }
                }
            }
        }

The above is how to use signatures to secure the interface of ASP. NET MVC OR WEBAPI. For more information about using signatures to secure the interface of ASP. NET MVC OR WEBAPI, please pay attention to other related articles on this site!


Related articles: