Ehsan Ghanbari

Experience, DotNet, Solutions

Cookie stealing in asp.net

You know that the cookie could be stolen by attackers. As nothing is secure until it's designed to be so, to make your cookie safe, firstly use HTTPS and make it required in your Web.config :

 

<httpCookies httpOnlyCookies="true" requireSSL="true" />

 

Personally, I don't store important data in the cookie and I just store a key in the cookie and by using that key I fetch the target data from a database but it doesn't work everywhere and it's not a solution for all the problems at all! Anyway, if you can't use Https, You can config the SSL by code for some sensitive cookies like below:

 

 protected void ForceCookieToBeHttpOnly(string cookieName, string cookieValue)

        {

            HttpCookie myHttpCookie = new HttpCookie(cookieName, cookieValue);

            Response.Cookies.Add(myHttpCookie);

            myHttpCookie.HttpOnly = true;

        }

 

But remember that, the only effective solution is Https.



Protecting the CDN application files from unauthorized requests

Recently I faced an issue about the protection of the files which were in a separated Web Application as a CDN. The files would be protected not only from external requests but also the internal unauthorized requests. The only way I had, was determining the valid request from the authorized IP addresses. I mean that not every request from the application for CDN was not valid. So I created a cryptography helper class with encrypting and decrypt methods to send the request between two applications:

 

 

static class Cryptography
    {
        private const int Keysize = 256;
        private const int DerivationIterations = 1000;

        public static string Encrypt(string plainText, string passPhrase)
        {
            var saltStringBytes = Generate256BitsOfRandomEntropy();
            var ivStringBytes = Generate256BitsOfRandomEntropy();
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                            {
                                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                                cryptoStream.FlushFinalBlock();
                                var cipherTextBytes = saltStringBytes;
                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Convert.ToBase64String(cipherTextBytes);
                            }
                        }
                    }
                }
            }
        }

        public static string Decrypt(string cipherText, string passPhrase)
        {
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                var plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }

        private static byte[] Generate256BitsOfRandomEntropy()
        {
            var randomBytes = new byte[32];

            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                rngCsp.GetBytes(randomBytes);
            }

            return randomBytes;
        }
    }

 

Then in the first application which was the main production, I modified the GetFile HtmlHeler like this:

 

 

public static MvcHtmlString GetFile(this HtmlHelper htmlHelper, string url, string extension, string contentName)
        {
            if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(extension) || string.IsNullOrEmpty(contentName))
            {
                return MvcHtmlString.Empty;
            }

            extension = extension.Replace(".", "");

            if (!url.StartsWith("http://"))
            {
                url = string.Format("http://" + url);
            }

            url = url.Replace('\\', '/').Replace("~/", string.Empty) + "/File/Browse/";
            var filePathWithoutCdnAddress = $"{string.Format(extension)}/{Path.GetFileNameWithoutExtension(contentName)}{string.Format("." + extension)}";
            var userIpAddress = HttpContext.Current.Request.UserHostAddress;
            var saltKey = "E88CA429-406A-47B1-BBD7-8F9668B74DA8";
            var encryptedDate = Cryptography.Encrypt(filePathWithoutCdnAddress, userIpAddress + saltKey);
            return MvcHtmlString.Create(url + encryptedDate);
        }

 

As the direction of files is extension>contentName in CDN, so I had to send them separately to the GetFile HtmlHelper by the information about the file I had to get from the database of application. And saltKey is just for making the key more secure by the combination of user IP address. And finally in CDN application which also was an MVC template, I created an action named Browse with a parameter to get the encrypted request:

 

 

  public class FileController
    {
        public ActionResult Browse(string encryptedDate)
        {
            var userIpAddress = System.Web.HttpContext.Current.Request.UserHostAddress;
            var saltKey = "E88CA429-406A-47B1-BBD7-8F9668B74DA8";
            var decryptedDate = Cryptography.Decrypt(encryptedDate, userIpAddress + saltKey);
            var extension = decryptedDate.Split('/')[1];
            var contentName = decryptedDate.Split('/')[2];
            var dir = Server.MapPath(string.Format("/" + extension + "/" + contentName));
            var path = Path.Combine(dir);
            return base.File(path, extension);
        }
    }

 

 

As you can see, only the authenticated and authorized user can get the file from CDN and not everybody can make a request from the browser because the IP address is Unique. You can handle the unauthorized requests and redirect them to page in CDN! I would appreciate if you suggest another solution if you know, thanks!



Open redirection attack in asp.net MVC

In the simplest definition, Any web application that redirects to a URL by a request (querystring) can tamper to an external, malicious URL by hackers. This operation is called an open redirection attack. In asp.net MVC, in server side, you can use the following HTML helper to check if the requested URL is local or not:

 

 

  public static bool IsLocalUrl(this HtmlHelper htmlHelper, string url)

        {

            var request = HttpContext.Current.Request;

 

            if (string.IsNullOrEmpty(url))

            {

                return false;

            }

 

            Uri absoluteUri;

            if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri))

            {

                return String.Equals(request.Url.Host, absoluteUri.Host,

                            StringComparison.OrdinalIgnoreCase);

            }

            else

            {

                bool isLocal = !url.StartsWith("http:", StringComparison.OrdinalIgnoreCase)

                    && !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase)

                    && Uri.IsWellFormedUriString(url, UriKind.Relative);

                return isLocal;

            }

        }

 

You can create your own function in JavaScript too, but I think server side one is safer!



About Me

Ehsan Ghanbari

Hi! my name is Ehsan. I'm a developer, passionate technologist, and fan of clean code. I'm interested in enterprise and large-scale applications architecture and design patterns and I'm spending a lot of my time on architecture subject. Since 2008, I've been as a developer for companies and organizations and I've been focusing on Microsoft ecosystem all the time. During the&nb Read More

Post Tags
Pending Blog Posts
Strategic design
Factory Pattern
time out pattern in ajax
Selectors in Jquery
Peridic pattern
How to use PagedList In asp.net MVC
How to query over Icollection<> of a type with linq
Domain driven design VS model driven architecture
Redis as a cache server
What's the DDD-lite?