How to securely send/receive key params in an .NET Core WebAPI and Javascript Application
Tags: csharp, javascript, api, security
Published: 2021-07-14 08:05:41

In an api its common to have some identifier keys to identify user details and process data accordingly. Its also common to take these details from either query parameters or headers or route path from client side.
However these apis will be rejected in the security audit process as they expose user data and has insecure object reference vulnerability, which can be maliciously exploited to get information of other users by tweaking these api parameters.
There are many ways to handle this security issue, however in this tutorial i will explain how to handle this by encrypting these params together into a single key and sending that in a header. I will also explain how to handle it at the api level using a middleware in .NET Core Api. Encryption is done in javascript to use in a client application and decryption is done in C# at the API level. 
I have used AES Key-Based Encryption algorithm to achieve encryption and decryption.

Lets assume we have an salaries api with employeeid as param

<code> api/salaries/getbyemployeeid?employeeid=1031 </code>

in this api, we are exposing an important key identifier employeeid and hence its exposed because anyone with some basic authentication can check details of another employee to avoid this first we will remove the query parameter.

<code> api/salaries/getbyemployeeid </code>

next we will generate an encrypted key using AES Encryption and then we will send that key as header.

<b> Encryption in JavaScript </b>

install the package `npm install crypto-js`

<code>
const CryptoJS = require('crypto-js');
function Encrypt(str) {
                 var KEY = "12345678900000001234567890000000";//32 bit
                 var IV = "1234567890000000";//16 bits
        var key = CryptoJS.enc.Utf8.parse(KEY);
        var iv = CryptoJS.enc.Utf8.parse(IV);
 
        var encrypted = '';
 
        var srcs = CryptoJS.enc.Utf8.parse(str);
        encrypted = CryptoJS.AES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
 
        return encrypted.ciphertext.toString();
    }
var encryptedEmployeeId = Encrypt("1031");
console.log(encryptedEmployeeId);
//result would be EF082204BF6F804099396A96CC7733F4
</code>

<b> Decryption in C# </b>

<code>
public class EncryptDecrypt
{
    public static string AESDecryption(string input)
    {
        string AES_IV = "1234567890000000";//16 bits 
        string key = "12345678900000001234567890000000"; //32 bits
        byte[] inputBytes = HexStringToByteArray(input);
            byte[] keyBytes = Encoding.UTF8.GetBytes(key.Substring(0, 32));
            using AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider();
            aesAlg.Key = keyBytes;
            aesAlg.IV = Encoding.UTF8.GetBytes(AES_IV.Substring(0, 16));

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            using MemoryStream msEncrypt = new MemoryStream(inputBytes);
            using CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read);
            using StreamReader srEncrypt = new StreamReader(csEncrypt);
            return srEncrypt.ReadToEnd();
    }

private static byte[] HexStringToByteArray(string s)
        {
            s = s.Replace(" ", "");
            byte[] buffer = new byte[s.Length / 2];
            for (int i = 0; i < s.Length; i += 2)
                buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
            return buffer;
        }
}
</code>

<b> Send encrypted param in the header </b>
I added a header named Request-Id 
`Request-Id : EF082204BF6F804099396A96CC7733F4`

<b> Adding a Request Middleware to grab the header value and Decrypt it. </b>

<code>
public class RequestMiddleware
    {
        private readonly RequestDelegate _next;
        public RequestMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Headers.TryGetValue("Request-Id", out var requestid))
            {
                var employeeid = EncryptDecrypt.AESDecryption(requestid);     
            }

            await _next(context);
        }
    }
</code>

Configure the Middleware before the middleware used for other apis to make it available and can be saved in a static variable.

`app.UseMiddleware(typeof(RequestMiddleware));`

<b> Conclusion </b>
So, in this tutorial i explained how can we send key based encrypted parameter in an api request in the header instead of sending directly and modified the api as shown initially.