C # SecureString Вопрос - PullRequest
       35

C # SecureString Вопрос

25 голосов
/ 26 ноября 2009

Есть ли способ получить значение SecureString без обеспечения безопасности? Например, в приведенном ниже коде, как только вы выполняете PtrToStringBSTR, строка больше не является безопасной, поскольку строки являются неизменяемыми, а сборка мусора является недетерминированной для строк.

IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object);
string value = Marshal.PtrToStringBSTR(ptr);

Что если бы был способ получить символ [] или байт [] неуправляемой строки BSTR? Будет ли это означать, что сборка мусора будет более предсказуемой (поскольку вы будете использовать char [] или byte [], а не строку? Верно ли это предположение, и если да, то как бы вы вернули char [] или byte []?

Ответы [ 5 ]

27 голосов
/ 25 августа 2010

Вот класс, который я написал специально для этой цели. Это полностью, 100% взломостойкий? Нет - вы можете сделать очень мало, чтобы сделать приложение на 100% безопасным, но этот класс идет настолько далеко, насколько вы можете защитить себя, если вам нужно преобразовать SecureString в String.

Вот как вы используете класс:

using(SecureStringToStringMarshaler sm = new SecureStringToStringMarshaler(secureString))
{
    // Use sm.String here.  While in the 'using' block, the string is accessible
    // but pinned in memory.  When the 'using' block terminates, the string is zeroed
    // out for security, and garbage collected as usual.
}

Вот класс

/// Copyright (C) 2010 Douglas Day
/// All rights reserved.
/// MIT-licensed: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace DDay.Base
{
    public class SecureStringToStringMarshaler : IDisposable
    {
        #region Private Fields

        private string _String;
        private SecureString _SecureString;
        private GCHandle _GCH;

        #endregion

        #region Public Properties

        public SecureString SecureString
        {
            get { return _SecureString; }
            set
            {
                _SecureString = value;
                UpdateStringValue();
            }
        }

        public string String
        {
            get { return _String; }
            protected set { _String = value; }
        } 

        #endregion

        #region Constructors

        public SecureStringToStringMarshaler()
        {
        }

        public SecureStringToStringMarshaler(SecureString ss)        
        {
            SecureString = ss;
        }

        #endregion

        #region Private Methods

        void UpdateStringValue()
        {
            Deallocate();

            unsafe
            {
                if (SecureString != null)
                {
                    int length = SecureString.Length;
                    String = new string('\0', length);

                    _GCH = new GCHandle();

                    // Create a CER (Contrained Execution Region)
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try { }
                    finally
                    {
                        // Pin our string, disallowing the garbage collector from
                        // moving it around.
                        _GCH = GCHandle.Alloc(String, GCHandleType.Pinned);
                    }

                    IntPtr stringPtr = IntPtr.Zero;
                    RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(
                        delegate
                        {
                            // Create a CER (Contrained Execution Region)
                            RuntimeHelpers.PrepareConstrainedRegions();
                            try { }
                            finally
                            {
                                stringPtr = Marshal.SecureStringToBSTR(SecureString);
                            }

                            // Copy the SecureString content to our pinned string
                            char* pString = (char*)stringPtr;
                            char* pInsecureString = (char*)_GCH.AddrOfPinnedObject();
                            for (int index = 0; index < length; index++)
                            {
                                pInsecureString[index] = pString[index];
                            }
                        },
                        delegate
                        {
                            if (stringPtr != IntPtr.Zero)
                            {
                                // Free the SecureString BSTR that was generated
                                Marshal.ZeroFreeBSTR(stringPtr);
                            }
                        },
                        null);
                }
            }
        }

        void Deallocate()
        {            
            if (_GCH.IsAllocated)
            {
                unsafe
                {
                    // Determine the length of the string
                    int length = String.Length;

                    // Zero each character of the string.
                    char* pInsecureString = (char*)_GCH.AddrOfPinnedObject();
                    for (int index = 0; index < length; index++)
                    {
                        pInsecureString[index] = '\0';
                    }

                    // Free the handle so the garbage collector
                    // can dispose of it properly.
                    _GCH.Free();
                }
            }
        } 

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Deallocate();
        }

        #endregion
    }
}

Этот код требует, чтобы вы могли скомпилировать unsafe код, но он работает как шарм.

С уважением,

-Doug

12 голосов
/ 26 ноября 2009

Это должно помочь вам: Маршалинг паролей SecureString в строку

Из статьи, ключевые моменты:

  • Закрепить строку в памяти.
  • Использование управляемых указателей для изменения System.String.
  • Используйте строгие гарантии метода ExecuteCodeWithGuaranteedCleanup.
9 голосов
/ 26 ноября 2009

SecureStrings безопасны только до тех пор, пока вы их не используете. ) -;

1, что вы не должны делать, это копировать в строку (независимо от метода). Строка является неизменной и потенциально может оставаться в памяти надолго.

Копировать его на символ [] немного безопаснее, если принять меры предосторожности при обнулении этого массива как можно скорее. Но массив присутствует в памяти в течение некоторого времени, и это представляет угрозу безопасности (нарушение).

К сожалению, SecureStrings в библиотеке очень мало поддерживается. Наиболее распространенный способ работы с ними - по одному символу за раз.

Редактировать:

массив char[] должен быть закреплен, и Марк Байерс предоставляет ссылку на статью, делающую то же самое с закрепленной строкой. Это вопрос выбора, но риск строки заключается в том, что скопировать ее очень легко (достаточно передать какой-либо метод, выполняющий Trim()).

1 голос
/ 26 ноября 2009

Ссылка, предоставленная Марком, - это лучшее из того, что вы можете сделать, и это подход, который моя команда использовала для решения этой проблемы (хотя мы не вдавались в сложность использования CER). Я немного сомневался в том, чтобы использовать пиннинг, чтобы по сути сломать неизменность C # String, но он работает.

0 голосов
/ 26 ноября 2009

Использование Marshal.ZeroFreeBSTR :

РЕДАКТИРОВАТЬ: Да, создание новой строки создаст копию, так что вы потеряете контроль над очисткой содержимого. Вы можете получить доступ к char [], приведя указатель, возвращенный IntPtr.ToPointer (), в небезопасный контекст:

IntPtr ptr = Marshal.SecureStringToBSTR(str);
unsafe
{
    char *cp = (char*)ptr.ToPointer();
    //access char[] through cp
}

Marshal.ZeroFreeBSTR(ptr);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...