Secure Token for use in API calls

Posted on

Problem

My team has developed an API and I’ve been tasked with creating an authentication layer that allows partners’ applications to consume it. To that end, I’ve created a data store which houses an Application ID, Login and salted/hashed Password for that application to authenticate against. I want to hand back a Token for them to use (which is a key for session information that is kept on the server). The process to generate the Token is below. I have another process which reverses this to validate its authenticity. It seems to stand up to testing, but I’d like to know if there’s anything else that should be done to make this properly secure.

private const string Password = "nottheactualpasswordthatisused..";

private static readonly byte[] _Salt =
{
    0x00, 0x01, 0x02, 0x03,
    0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b,
    0x0c, 0x0d, 0x0e, 0x0f
};

private static async Task<string> CreateSecureSessionTokenAsync(int applicationId, DateTime createDate)
{
    using (var aes = Aes.Create())
    {
        if (aes == null)
        {
            return null;
        }

        using (var deriveBytes = new Rfc2898DeriveBytes(Password, _Salt))
        {
            aes.Key = deriveBytes.GetBytes(32);
            aes.IV = deriveBytes.GetBytes(16);
        }

        using (var encryptor = aes.CreateEncryptor())
        using (var memoryStream = new MemoryStream())
        {
            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
            using (var writer = new StreamWriter(cryptoStream))
            {
                await writer.WriteLineAsync(Convert.ToString(applicationId)).ConfigureAwait(false);
                await writer.WriteLineAsync(Convert.ToString(createDate, CultureInfo.InvariantCulture))
                    .ConfigureAwait(false);
            }

            return Convert.ToBase64String(memoryStream.ToArray());
        }
    }
}

Solution

Using a string for storing a password is considered insecure because there is no possibility to delete that string from memory. From the SecureString documentation

An instance of the System.String class is both immutable and, when no longer needed, cannot be programmatically scheduled for garbage collection; that is, the instance is read-only after it is created, and it is not possible to predict when the instance will be deleted from computer memory. Because System.String instances are immutable, operations that appear to modify an existing instance actually create a copy of it to manipulate. Consequently, if a String object contains sensitive information such as a password, credit card number, or personal data, there is a risk the information could be revealed after it is used because your application cannot delete the data from computer memory.

so it would be better to use a SecureString but unfortunately that isn’t an option when using Aes because it doesn’t take an SecureString as a parameter. So what other options are there ? Just take a char[] and overwrite its content when the class is disposed.

The CreateSecureSessionTokenAsync() method should ideally return the token along with a random generated salt (maybe as tuple) to be stored in the data store to decrypt it later without the need that the customer will get the salt together with the token.

An implementation of a random salt using a parameterized length can be found in my answer here.

Leave a Reply

Your email address will not be published. Required fields are marked *