Очистить строку С# из памяти

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

Большинство результатов поиска В основном я нашел, что очистка содержимого String невозможна (поскольку строка неизменяема) и SecureString следует использовать.

Поэтому я придумал свое решение (используя небезопасный код) ниже. Тестирование показывает, что решения работают, но я все еще не уверен, что что-то не так с решением? Есть ли лучшие?

static unsafe bool clearString(string s, bool clearInternedString=false) 
{
    if (clearInternedString || string.IsInterned(s) == null)
    {
        fixed (char* c = s)
        {
            for (int i = 0; i < s.Length; i++)
                c[i] = '\0';
        }
        return true;
    }
    return false;
}

РЕДАКТИРОВАТЬ: Из-за комментариев о GC, перемещающих строку вокруг до того, как clearString вызывается: как насчет следующего фрагмента?

string s = new string('\0', len);
fixed (char* c = s)
{
    // copy data from secure location to s
    c[0] = ...;
    c[1] = ...;
    ...

    // do stuff with the string

    // clear the string
    for (int i = 0; i < s.Length; i++)
        c[i] = '\0';
}

Ответы

Ответ 1

Ваша проблема заключается в том, что строки могут перемещаться. Если GC работает, он может перемещать содержимое в новое место, но он не будет обнулять старый. Если вы обнулили нулевую строку, у вас нет гарантии, что ее копия не существует в другом месте в памяти.

Вот ссылка в сборщик мусора .NET, и в нем говорится о уплотнении.

EDIT: здесь ваша проблема с обновлением:

// do stuff with the string

Проблема заключается в том, что после того, как вы оставите свой контроль, вы потеряете возможность убедиться в его безопасности. Если бы это было полностью под вашим контролем, тогда у вас не было бы ограничения только на использование типа строки. Проще говоря, эта проблема существует уже давно, и никто не придумал безопасный способ справиться с этим. Если вы хотите сохранить его в безопасности, лучше всего использовать его другими способами. Очистка строки предназначена для того, чтобы кто-то не смог найти ее через дамп памяти. Лучший способ остановить это, если вы не можете использовать защищенную строку, - это ограничение доступа к машине, на которой работает код.

Ответ 2

Помимо стандартного ответа "Вы попадаете в небезопасную территорию", который, я надеюсь, объясняет сам, рассмотрим следующее:

CLR не гарантирует, что в любой заданной точке есть только один экземпляр строки, и это не гарантирует, что строки будут собирать мусор. Если бы я сделал следующее:

var input = "somestring";
input += "sensitive info";
//do something with input
clearString(input, false);

Каков результат? (Предположим, что я не использую строковые литералы, а это входы из какой-то среды)

Строка создается с содержимым "somestring". Другая строка создается с содержимым "конфиденциальной информации", а другая строка создается с содержимым "somestringsensitive info". Только последняя строка очищается: "конфиденциальная информация" - нет. Это может быть или не быть немедленно собранным мусором.

Даже если вы тщательно следите за тем, чтобы вы всегда очищали какую-либо строку с конфиденциальной информацией, CLR по-прежнему не гарантирует, что существует только один экземпляр строки.

изменить Что касается вашего редактирования, просто привязка строки сразу может иметь желаемый эффект - нет необходимости копировать строку в другое место или что-то еще. Вам нужно сделать это сразу после получения указанной строки, и есть еще другие проблемы с безопасностью, о которых нужно беспокоиться. Вы не можете гарантировать, что, например, источник строки не имеет ее копии в ITS-памяти, без четкого понимания источника и точно, как он это делает.

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

Кроме того, если вы передадите его другим функциям, которые вы сами не пишете, он может или не может быть скопирован этой функцией.

Ответ 3

Невозможно рассказать, сколько функций CLR и non-CLR проходит ваша строка, прежде чем она достигнет вашей функции, где вы пытаетесь ее очистить. Эти функции (управляемые и неуправляемые) могут создавать копии строки по разным причинам (возможно, несколько копий).

Вы не можете знать все эти места и четко их очищать, вы не можете гарантировать, что ваш пароль будет очищен из памяти. Вы должны использовать SecureString, но вам нужно понять, что выше все еще применяется: в какой-то момент вашей программы вы получите пароль, и вы придется иметь его в памяти (даже если на короткое время, пока вы переводите его в безопасную строку). Это означает, что ваша строка будет по-прежнему проходить через цепочки вызовов функций, которые вы не контролируете.

Ответ 4

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

Тем не менее, вы никогда не сможете гарантировать, что ваши данные будут безопасными, поскольку у вас никогда не будет полного контроля над ним. Например, достаточно глубоко внедренный вирус может считывать эту память во время работы программы, и это также возможно, что процесс завершен, и в этом случае код деструктора не будет работать, оставив данные в нераспределенной памяти, что могло бы быть выделены другому процессу, и он по-прежнему будет содержать ваши конфиденциальные данные; кто-то может легко использовать такой инструмент, как визуальная студия для мониторинга памяти отлаженного процесса, или написать программу, которая выделяет память и выполняет поиск ее для конфиденциальных данных.