Skip to content

User SecureString for Basic and Proxy passwords #3806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2019

Conversation

russcam
Copy link
Contributor

@russcam russcam commented Jun 10, 2019

This commit changes the BasicAuthenticationCredentials and ProxyPassword types
to use SecureString for holding the passwords in memory, as opposed to as a plain text string.

A plain text string is retrieved from SecureString only at the point at which it is required.
This prevents plain text passwords from being automatically emitted in cases where properties
may be enumerated and captured e.g. logging frameworks.

Closes #2505

This commit changes the BasicAuthenticationCredentials and ProxyPassword types
to use SecureString for holding the passwords in memory, as opposed to as a plain text string.

A plain text string is retrieved from SecureString only at the point at which it is required.
This prevents plain text passwords from being automatically emitted in cases where properties
may be enumerated and captured e.g. logging frameworks.
@Mpdreamz
Copy link
Member

Return of SecureString 🍰 we left it out for a reason for a long time no less because the Secure bit is debatable:

https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md was always a reason for us not to implement it.

A plain text string is retrieved from SecureString only at the point at which it is required.
This prevents plain text passwords from being automatically emitted in cases where properties
may be enumerated and captured e.g. logging frameworks.

Is reason enough to add it in though 👍

@russcam russcam merged commit d835462 into 7.x Jun 11, 2019
@Mpdreamz Mpdreamz deleted the feature/auth-securestring branch June 17, 2019 12:24
@RolfDeVries
Copy link

RolfDeVries commented Jun 17, 2019

In some cases the createstring method returns a value that looks like chinese instead of the original string. I still have to investigate why. Is this a known issue?

I think it is a threading issue. Secure string does not seem thread safe, it encrypts and decrypts the buffer in place on Read actions without a lock.
https://referencesource.microsoft.com/#mscorlib/system/security/securestring.cs,77d68ea938f47705,references

Elasticclient is now no longer threadsafe, which does not comply to the docs
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/lifetimes.html

@russcam
Copy link
Contributor Author

russcam commented Jun 18, 2019

@RolfDeVries could you provide more detail about

  1. how you're instantiating ConnectionSettings and ElasticClient
  2. what platform you're running on and framework version
  3. the environment it's running e.g. full trust

There should be just a single instance of SecureString created, with decryption handled by

public static string CreateString(this SecureString secureString)
{
var num = IntPtr.Zero;
if (secureString != null && secureString.Length != 0)
{
try
{
num = Marshal.SecureStringToBSTR(secureString);
return Marshal.PtrToStringBSTR(num);
}
finally
{
if (num != IntPtr.Zero)
Marshal.ZeroFreeBSTR(num);
}
}
return string.Empty;
}

which allocates an unmanaged binary string each time and copies the contents of SecureString.

@russcam
Copy link
Contributor Author

russcam commented Jun 18, 2019

A quick test with

var secureString = "password".CreateSecureString();
var count = 10000;
var tasks = new List<Task<string>>(count);

for (int i = 0; i < count; i++)
	tasks.Add(new Task<string>(() =>
	{
		var s = secureString.CreateString();
		Console.WriteLine(s);
		return s;
	}));

tasks.ForEach(t => t.Start());	
Task.WaitAll(tasks.ToArray());

returns the expected results, but I may be missing something.

@RolfDeVries
Copy link

When i use 4 parallel thread to read the password 1000 times it will fail:

using System;
using System.Security;
using System.Threading;
using Elasticsearch.Net;

namespace SecureStringTest
{
public class Program
{
private const string Password = "password";
private static readonly SecureString SecureString = Password.CreateSecureString();

    public static void Main(string[] args)
    {
        Console.WriteLine(SecureString.CreateString());

        for (var i = 0; i < 4; i++)
        {
            new Thread(ThreadStart).Start();
        }

        Console.ReadLine();
    }

    private static void ThreadStart()
    {
        for (var i = 0; i < 100; i++)
        {
            var actual = SecureString.CreateString();
            Console.WriteLine(actual);
        }
    }
}

}

Running on .net core 2.2 windows server 2016 with 6 virtual processors.

@russcam
Copy link
Contributor Author

russcam commented Jun 18, 2019

Thanks @RolfDeVries, can replicate on .NET Core 2.2.

@russcam
Copy link
Contributor Author

russcam commented Jun 18, 2019

Looks to be related to Marshal.SecureStringToBSTR() et. al. Marshal.SecureStringToGlobalAllocUnicode() works

public static string CreateString(this System.Security.SecureString secureString)
{
    var num = IntPtr.Zero;
    if (secureString != null && secureString.Length != 0)
    {
        try
        {
            num = Marshal.SecureStringToGlobalAllocUnicode(secureString);
            return Marshal.PtrToStringUni(num);
        }
        finally
        {
            if (num != IntPtr.Zero)
                Marshal.ZeroFreeGlobalAllocUnicode(num);
        }
    }

    return string.Empty;
}

@RolfDeVries
Copy link

Looks like its working

russcam added a commit that referenced this pull request Jun 18, 2019
Relates: #3806 (comment)

This commit updates the SecureStrings implementation to use the cross platform
Marshal methods that deal with unmanaged unicode strings.

Implement IDisposable on BasicAuthenticationCredentials and dispose of SecureString
instances when ConnectionConfiguration is disposed.
@russcam
Copy link
Contributor Author

russcam commented Jun 18, 2019

@RolfDeVries I've opened #3825 to address

@RolfDeVries
Copy link

Thanks very much for the quick response!

russcam added a commit that referenced this pull request Jun 19, 2019
* Use Marshal.SecureStringToGlobalAllocUnicode

Relates: #3806 (comment)

This commit updates the SecureStrings implementation to use the cross platform
Marshal methods that deal with unmanaged unicode strings.

Implement IDisposable on BasicAuthenticationCredentials and dispose of SecureString
instances when ConnectionConfiguration is disposed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants