Как я могу повысить безопасность памяти в Delphi? - PullRequest
3 голосов
/ 11 февраля 2009

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

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

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

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

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

Ответы [ 11 ]

7 голосов
/ 11 февраля 2009

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

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

4 голосов
/ 11 февраля 2009

Два основных момента по этому поводу:

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

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

3 голосов
/ 31 марта 2010

Как насчет этого?

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;
2 голосов
/ 11 февраля 2009

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

1 голос
/ 12 февраля 2009

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

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

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

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

С уважением,

Jeroen Pluimers

1 голос
/ 11 февраля 2009

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

0 голосов
/ 21 ноября 2017

Будьте осторожны с функциями, которые пытаются трактовать строку как указатель, и попробуйте использовать 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;
0 голосов
/ 10 марта 2009

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

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

EDIT

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

0 голосов
/ 12 февраля 2009

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

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

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

0 голосов
/ 11 февраля 2009

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

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

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