Очистить строку С# из памяти
Я пытаюсь очистить содержимое памяти строки С# из соображений безопасности.
Я знаю класс 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
, и вы готовы писать небезопасный код, тогда вы можете написать свой собственный простой класс строк, который использует неуправляемую память и гарантирует, что вся память обнулена перед освобождением.
Тем не менее, вы никогда не сможете гарантировать, что ваши данные будут безопасными, поскольку у вас никогда не будет полного контроля над ним. Например, достаточно глубоко внедренный вирус может считывать эту память во время работы программы, и это также возможно, что процесс завершен, и в этом случае код деструктора не будет работать, оставив данные в нераспределенной памяти, что могло бы быть выделены другому процессу, и он по-прежнему будет содержать ваши конфиденциальные данные; кто-то может легко использовать такой инструмент, как визуальная студия для мониторинга памяти отлаженного процесса, или написать программу, которая выделяет память и выполняет поиск ее для конфиденциальных данных.