共用方式為


Ronin Building Blocks - Secure String Helpers

The next building block that I will put together is for secure strings. There are almost always secrets to be stored and keep safe in memory so with a security first mindset making this a common utility library it will reduce the chance of one off security errors.

In order to build this library there, need to be two frameworks I target. The code used by the netstandard1.6 library is different by a line or two than the .NET 451 version. This gives a great chance to try targeting multiple frameworks from a single library.

The first thing I do is to setup the .NET Core class library as I did before, cleanup the solution and setup the project.json as seen below. The important item to note in this JSON is that the "allowUnsafe" build option was set to true for the higher performance techniques that will be implemented.

 {
 "version": "1.0.3-*",
 "name": "Ronin.SecureStrings",
 "description": "Common functions related to the use of SecureStrings.",
 "copyright": "Chris Clayton 2016",
 "title": "Ronin Secure Strings",
 "authors": [ "Chris Clayton" ],
 "language": "en-US",
 "buildOptions": {
 "optimize": true,
 "warningsAsErrors": true,
 "xmlDoc": true,
 "platform": "anycpu",
 "debugType": "portable",
  "allowUnsafe": true
 },
 "frameworks": {
 "netstandard1.6": {
 "dependencies": {
 "NETStandard.Library": "1.6.*",
 "System.Security.SecureString": "4.0.0"
 }
 },
 "net451":  {}
 },
 "dependencies": { "Ronin.Parameters" : "1.*" }
 }

When creating an instance of a secure string that is populated with read-only contents already can be accomplished in two ways. The first way that does not leverage any unsafe code is by creating an instance of the secure string and then iterating through adding them one character at a time. The performance of this technique is not optimal and if a large number of instances will be created it is more appropriate often to use the unsafe version that has a substantial performance increase. It is also important to notice that in both of these techniques the final step is to make the secure string read-only.

When transforming the secure string back into a core CLR string it is done through marshalling which changes for the allocation whether netstandard1.6 or net451 is being targeted changes the marshal class used, so I will use a pre compiler directive as seen in the SecureStringToString method.

 namespace Ronin.SecureStrings
 {
 #region Using Clauses
 using System;
 using System.Runtime.InteropServices;
 using System.Security;
 using Parameters;
 #endregion

/// <summary>
 /// Contains common secure string handling methods.
 /// </summary>
 public static class SecureStringUtilities
 {
 #region Public Methods
 /// <summary>
 /// Creates a secure string based on the contents of the provided string using unsafe code.
 /// </summary>
 /// <param name="value">The string holding the contents of the secure string.</param>
 /// <returns>A populated secure string that contains the value specified in the string parameter.</returns>
 public static unsafe SecureString GetSecureStringUnsafe(string value)
 {
 Guard.NotNull(nameof(value), value);

SecureString results;

fixed (char* pValue = value)
 {
 results = new SecureString(pValue, value.Length);
 results.MakeReadOnly();
 }

return results;
 }

/// <summary>
 /// Creates a secure string based on the contents of the provided string.
 /// </summary>
 /// <param name="value">The string holding the contents of the secure string.</param>
 /// <returns>A populated secure string that contains the value specified in the string parameter.</returns>
 public static SecureString GetSecureString(string value)
 {
 Guard.NotNull(nameof(value), value);

var results = new SecureString();

foreach (var character in value)
 {
 results.AppendChar(character);
 }

results.MakeReadOnly();

return results;
 }

/// <summary>
 /// Creates a CLR string based on the provided secure string value.
 /// </summary>
 /// <param name="value">The secure string that contains the value to be placed in the CLR string.</param>
 /// <returns></returns>
 public static string SecureStringToString(SecureString value)
 {
 Guard.NotNull(nameof(value), value);

string plainValue = null;

if (value != null)
 {
 // Replace with a safe handle in the future when the attribute is present
 var handle = IntPtr.Zero;

try
 {
 #if NET451
 handle = Marshal.SecureStringToGlobalAllocUnicode(value);
 #else
 handle = SecureStringMarshal.SecureStringToGlobalAllocUnicode(value);
 #endif
 plainValue = Marshal.PtrToStringUni(handle);
 }
 finally
 {
 if (handle != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(handle);
 }
 }

return plainValue;
 }
 #endregion
 }
 }

So this is great but we can make it a bit easier to use. I decided to create a class with extension methods to support the secure string interactions. The class is simple but will save some headaches as you can see here.

 namespace Ronin.SecureStrings
{
 #region Using Clauses
 using System.Security;
 #endregion

/// <summary>
 /// A set of extension methods to make it easier working with CLR Secure Strings
 /// </summary>
 public static class SecureStringExtensions
 {
 /// <summary>
 /// Creates a secure string based on the contents of the provided string using unsafe code. 
 /// </summary>
 /// <param name="value">The string holding the contents of the secure string.</param>
 /// <returns>A populated secure string that contains the value specified in the string parameter.</returns>
 public static SecureString GetSecureStringUnsafe(this string value)
 {
 return SecureStringUtilities.GetSecureStringUnsafe(value);
 }

/// <summary>
 /// Creates a secure string based on the contents of the provided string.
 /// </summary>
 /// <param name="value">The string holding the contents of the secure string.</param>
 /// <returns>A populated secure string that contains the value specified in the string parameter.</returns>
 public static SecureString GetSecureString(this string value)
 {
 return SecureStringUtilities.GetSecureString(value);
 }
 
 /// <summary>
 /// Creates a CLR string based on the provided secure string value.
 /// </summary>
 /// <param name="value">The secure string that contains the value to be placed in the CLR string.</param>
 /// <returns></returns>
 public static string GetString(this SecureString value)
 {
 return SecureStringUtilities.SecureStringToString(value);
 }
 }
}

So now that I have a simple reusable helper class for secure strings I can eliminate some of the human error elements that may negatively impact the security of my solution.

<< Previous     Next >>