Вопрос

Можно ли «стереть» строки в Delphi?Позволь мне объяснить:

Я пишу приложение, которое будет включать DLL для авторизации пользователей.Он прочитает зашифрованный файл в XML DOM, использует содержащуюся в нем информацию, а затем освободит DOM.

Очевидно, что незашифрованный XML все еще находится в памяти DLL и, следовательно, уязвим для проверки.Я не собираюсь переусердствовать с защитой этого — пользователь может создать еще одну DLL — но я хотел бы сделать основной шаг, чтобы имена пользователей не оставались в памяти на века.Однако я не думаю, что смогу легко стереть память из-за ссылок.Если я пройдусь по своему DOM (который является классом TNativeXML) и найду каждый экземпляр строки, а затем превращу его во что-то вроде «ааааа», то не назначит ли он фактически указатель новой строки ссылке на DOM, а затем не оставит старую строку сидящей есть ли в памяти ожидающий перераспределения?Есть ли способ быть уверенным, что я убиваю единственную и оригинальную копию?

Или есть ли в D2007 возможность стереть из кучи всю неиспользуемую память?Таким образом, я мог бы освободить DOM, а затем приказать ему стереть данные.

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

Это было полезно?

Решение

Я не думаю, что об этом стоит беспокоиться, потому что, если пользователь может прочитать память процесса с помощью DLL, тот же пользователь также может остановить выполнение в любой заданный момент времени.Остановка выполнения до очистки памяти все равно предоставит пользователю полный доступ к незашифрованным данным.

ИМО, любой пользователь, достаточно заинтересованный и способный делать то, что вы описываете, не почувствует серьезных неудобств из-за очистки памяти вашей DLL.

Другие советы

Два общих момента по этому поводу:

Во-первых, это одна из тех областей, где «если вы должны спросить, вам, вероятно, не стоит этого делать». И пожалуйста, не пойми это неправильно;Я не имею в виду неуважение к вашим навыкам программирования.Просто вы либо являетесь экспертом в написании безопасного, криптостойкого программного обеспечения, либо нет.Примерно так же, как знание «немного каратэ» гораздо опаснее, чем отсутствие знания каратэ вообще.Существует ряд сторонних инструментов для написания безопасного программного обеспечения на Delphi, которые имеют экспертную поддержку;Я настоятельно рекомендую всем, у кого нет глубоких знаний о криптографических службах в Windows, математических основах криптографии и опыта борьбы с атаками по побочным каналам, использовать их вместо попыток «свернуть свои собственные».

Чтобы ответить на ваш конкретный вопрос:Windows API имеет ряд полезных функций, таких как СклепЗащититьПамять.Однако это создаст ложное чувство безопасности, если вы зашифруете свою память, но у вас есть дыра в другом месте системы или вы обнаружите побочный канал.Это все равно, что запереть дверь, но оставить окно открытым.

Как насчет чего-то вроде этого?

procedure WipeString(const str: String);
var
  i:Integer;
  iSize:Integer;
  pData:PChar;

begin
    iSize := Length(str);
    pData := PChar(str);

    for i := 0 to 7 do
    begin
      ZeroMemory(pData, iSize);
      FillMemory(pData, iSize, $FF); // 1111 1111
      FillMemory(pData, iSize, $AA); // 1010 1010
      FillMemory(pData, iSize, $55); // 0101 0101
      ZeroMemory(pData, iSize);
    end;
end;

DLL не владеют выделенной памятью, а процессы.Память, выделенная вашим конкретным процессом, будет удалена после завершения процесса, независимо от того, находится ли DLL (поскольку она используется другим процессом) или нет.

Как насчет расшифровки файла в поток, используя для проверки процессор SAX вместо XML DOM, а затем перезаписав расшифрованный поток перед его освобождением?

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

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

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

КСТАТИ:Холлвард Вассботн написал хороший блог о FastMM:http://hallvards.blogspot.com/2007/05/use-full-fastmm-consider-donating.html

С уважением,

Йерун Плаймерс

Беспорядочно, но вы можете записать размер кучи, который вы использовали, пока куча заполнена конфиденциальными данными, а затем, когда она будет выпущена, выполните GetMem, чтобы выделить вам большой кусок, охватывающий (скажем) 200% от этого.выполните Заполнение этого фрагмента и предположите, что любая фрагментация вряд ли принесет большую пользу эксперту.Бри

Как насчет сохранения пароля в виде хэш-значения в XML и его проверки путем сравнения хеша входного пароля с хешированным паролем в вашем XML.

Редактировать:Вы можете хранить все конфиденциальные данные в зашифрованном виде и расшифровывать их только в самый последний момент.

Можно ли загрузить расшифрованный XML в массив символов или байтов, а не в строку?Тогда не будет обработки копирования при записи, и вы сможете заполнить память нулями перед освобождением?

Будьте осторожны при назначении массива символов в строку, поскольку Delphi имеет здесь некоторую умную обработку для совместимости с традиционным упакованным массивом [1..x] символов.

Кроме того, не могли бы вы использовать ShortString?

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

Существует несколько методов, которые можно использовать для создания хеша строки.Хорошей отправной точкой было бы рассмотрение проекта с открытым исходным кодом Turbo Power».LockBox", я считаю, что там есть несколько примеров создания односторонних хеш-ключей.

РЕДАКТИРОВАТЬ

Но как знание хэш-значения, если оно одностороннее, поможет?Если вы действительно параноик, вы можете изменить значение хеш-функции чем-то предсказуемым, о чем знаете только вы...скажем, случайное число, использующее определенное начальное значение плюс дату.Затем вы можете сохранить в XML-коде только достаточную часть хеша, чтобы использовать его в качестве отправной точки для сравнения.Генераторы псевдослучайных чисел хороши тем, что они всегда генерируют одну и ту же серию «случайных» чисел с одним и тем же начальным числом.

Будьте осторожны с функциями, которые пытаются рассматривать строку как указатель, и пытайтесь использовать FillChar или ZeroMemory чтобы стереть содержимое строки.

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

 

procedure BurnString(var s: UnicodeString);
begin
    {
        If the string is actually constant (reference count of -1), then any attempt to burn it will be
        an access violation; as the memory is sitting in a read-only data page.

        But Delphi provides no supported way to get the reference count of a string.

        It's also an issue if someone else is currently using the string (i.e. Reference Count > 1).
        If the string were only referenced by the caller (with a reference count of 1), then
        our function here, which received the string through a var reference would also have the string with
        a reference count of one.

        Either way, we can only burn the string if there's no other reference.

        The use of UniqueString, while counter-intuitiave, is the best approach.
        If you pass an unencrypted password to BurnString as a var parameter, and there were another reference,
        the string would still contain the password on exit. You can argue that what's the point of making a *copy*
        of a string only to burn the copy. Two things:

            - if you're debugging it, the string you passed will now be burned (i.e. your local variable will be empty)
            - most of the time the RefCount will be 1. When RefCount is one, UniqueString does nothing, so we *are* burning
                the only string
    }
    if Length(s) > 0 then
    begin
        System.UniqueString(s); //ensure the passed in string has a reference count of one
        ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar));

        {
            By not calling UniqueString, we only save on a memory allocation and wipe if RefCnt <> 1
            It's an unsafe micro-optimization because we're using undocumented offsets to reference counts.

            And i'm really uncomfortable using it because it really is undocumented.
            It is absolutely a given that it won't change. And we'd have stopping using Delphi long before
            it changes. But i just can't do it.
        }
        //if PLongInt(PByte(S) - 8)^ = 1 then //RefCnt=1
        //  ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar));

        s := ''; //We want the callee to see their passed string come back as empty (even if it was shared with other variables)
    end;
end;

Как только у вас появится UnicodeString версию, вы можете создать AnsiString и WideString версии:

procedure BurnString(var s: AnsiString); overload;
begin
    if Length(s) > 0 then
    begin
        System.UniqueString(s);
        ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar));

        //if PLongInt(PByte(S) - 8)^ = 1 then //RefCount=1
        //  ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar));

        s := '';
    end;
end;

procedure BurnString(var s: WideString);
begin
    //WideStrings (i.e. COM BSTRs) are not reference counted, but they are modifiable
    if Length(s) > 0 then
    begin
        ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar));

        //if PLongInt(PByte(S) - 8)^ = 1 then //RefCount=1
        //  ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar));

        s := '';
    end;
end;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top