Нужно ли очищать содержимое стека?

Мы находимся под сертификацией PCI PA-DSS, и одно из ее требований — избегать записи чистого PAN (номера карты) на диск. Приложение не записывает такую ​​информацию на диск, но если операционная система (в данном случае Windows) должна поменяться местами, содержимое памяти записывается в файл подкачки. Поэтому приложение должно очистить память, чтобы службы захвата оперативной памяти не могли считывать конфиденциальные данные.

Есть три ситуации:

  • выделение кучи (malloc): перед освобождением памяти область можно очистить с помощью memset
  • статические или глобальные данные: после использования область может быть очищена с помощью memset
  • локальные данные (член функции): данные помещаются в стек и недоступны после завершения функции

Например:

void test()
{
char card_number[17];

strcpy(card_number, "4000000000000000");
}

После выполнения теста память все еще содержит информацию номер_карты.

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

memset(card_number, 0, sizeof(card_number));

Есть ли способ очистить стек в какой-то момент, как прямо перед завершением программы?

8

Решение

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

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

4

Другие решения

Я предполагаю, что вы хотите избавиться от этой ситуации ниже:

#include <iostream>

using namespace std;

void test()
{
char card_number[17];
strcpy(card_number, "1234567890123456");
cout << "test() -> " << card_number << endl;
}

void test_trash()
{
// don't initialize, so get the trash from previous call to test()
char card_number[17];
cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
test();
test_trash();
return 0;
}

Выход:

test() -> 1234567890123456
trash from previous function -> 1234567890123456

Вы МОЖЕТЕ сделать что-то вроде этого:

#include <iostream>

using namespace std;

class CardNumber
{
char card_number[17];

public:
CardNumber(const char * value)
{
strncpy(card_number, value, sizeof(card_number));
}

virtual ~CardNumber()
{
// as suggested by @piedar, memset_s(), so the compiler
// doesn't optimize it away.
memset_s(card_number, sizeof(card_number), 0, sizeof(card_number));
}

const char * operator()()
{
return card_number;
}
};

void test()
{
CardNumber cardNumber("1234567890123456");
cout << "test() -> " << cardNumber() << endl;
}

void test_trash()
{
// don't initialize, so get the trash from previous call to test()
char card_number[17];
cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
test();
test_trash();
return 0;
}

Выход:

test() -> 1234567890123456
trash from previous function ->

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

И ДА: чтобы четко ответить на заголовок вашего вопроса: стек не будет очищаться автоматически … вы должны очистить его самостоятельно.

2

Я считаю, что это необходимо, но это только половина проблемы.

Здесь есть две проблемы:

  1. В принципе, ничто не мешает ОС обменять ваши данные, пока вы все еще их используете. Как указано в другом ответе, вы хотите VirtualLock на окнах и mlock на Linux.

  2. Вы должны запретить оптимизатору оптимизировать memset, Это также относится к глобальной и динамически выделяемой памяти. Я настоятельно рекомендую взглянуть на cryptopp SecureWipeBuffer.

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

2

Стек очищается перемещением указателя стека, а не извлечением из него значений. Единственная механика — вставлять возврат в соответствующие регистры. Вы должны сделать все это вручную. Кроме того — volatile может помочь вам избежать оптимизаций для каждой переменной. Вы можете очистить стек вручную, но — для этого вам нужен ассемблер — и не так-то просто начать манипулировать стеком — это на самом деле не ваш ресурс — вам это принадлежит компилятору ,

1
По вопросам рекламы [email protected]