1

In an Asp.net MVC 5 application I have a SecureString being passed into my view via the model. I would now like to write the contents of that SecureString to the Response stream without having to convert it to a string first. How can I achieve this?

Mike Dymond
  • 1,165
  • 9
  • 22
  • 4
    Using a SecureString in an HTTP context doesn't make any sense. The class is only there to prevent the string from being in your application's memory in plain sight. As soon as you want to transfer it over HTTP, it _must_ become a plain string. – CodeCaster Jan 30 '15 at 11:29
  • I am aware that there will be no such thing as 100% security, but I would like to reduce the attack surface as much as possible. Hence I am passing the data around as a SecureString inside my application and now that I actually need to show it to the end user I want to write it directly to the Response stream, rather than convert it to a string first and write that to the stream. – Mike Dymond Jan 30 '15 at 11:36
  • 2
    The .NET HTTP API doesn't provide this. The only entrance you have to the HTTP response body is in the form of a plaintext stream, in which you have to write the strings or bytes that you wish to transmit. So in order to write them to the response, they must be plaintext. – CodeCaster Jan 30 '15 at 11:40
  • 1
    In a web application, there are so many attack surfaces, using SecureString is like closing the window while the door is still open with a neon sign illuminating the way. – George Stocker Jan 30 '15 at 11:46
  • _How do I write a SecureString to the Response stream?_ - Encrypted. – H H Jan 30 '15 at 11:52
  • Assuming I am already aware of all of this but still need to do it, is it possible? How do I write the contents of the SecureString to the Response stream one byte at a time (knowing full well that each byte and the stream are plain text). – Mike Dymond Jan 30 '15 at 12:30
  • @HenkHolterman I like the idea of encrypting the contents of the SecureString. Can you suggest a way of achieving this so that it never exists as a plain text string and so that I can decrypt it client side via Javascript? – Mike Dymond Jan 30 '15 at 12:31
  • You can't get data out of a `SecureString` other than calling `ToString()` (or [`Marshal.SecureStringToBSTR()`](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.securestringtobstr%28v=vs.100%29.aspx)). This will create the unencrypted string in-memory again. As for your encryption: you'll be sending the cyphertext _and_ the key over the same channel, effectively nullifying the purpose. – CodeCaster Jan 30 '15 at 13:22
  • Calling 'ToString()' on a 'SecureString' does not give access tot he contents of the 'SecureString'! And 'Marshal.SecureStringToBSTR()' gives a 'pointer' to a binary string record NOT a 'string'! 'Marshal.SecureStringToGlobalAllocUnicode' seems like a better choice, but I then would rather get each byte individually rather than having to call 'Marshal.PtrToStringUni()'. Any ideas? – Mike Dymond Jan 30 '15 at 14:57

1 Answers1

1

This is the HtmlHelper extension I have come up with. I am sure it can be improved, but it achieves my goal of writing a SecureString to the Response stream without it ever being represented as a string.

public static class SecureStringHelpers
{
    public static void WriteSecureStringToResponse(this HtmlHelper helper, SecureString secureString)
    {
        if (secureString != null)
        {
            IntPtr unmanagedString = IntPtr.Zero;

            var secureByteArray = new byte[2];

            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);

                var offset = 0;
                var endOfString = false;

                do
                {
                    secureByteArray[0] = Marshal.ReadByte(unmanagedString, offset);
                    offset++;
                    secureByteArray[1] = Marshal.ReadByte(unmanagedString, offset);
                    offset++;

                    if (!(secureByteArray[0] == 0 && secureByteArray[1] == 0))
                    {
                        helper.ViewContext.Writer.Write(System.BitConverter.ToChar(secureByteArray, 0));
                    }
                    else
                    {
                        endOfString = true;
                    }

                } while (!endOfString);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
                secureByteArray[0] = 0;
                secureByteArray[1] = 0;
            }
        }
    }
}
Mike Dymond
  • 1,165
  • 9
  • 22