Используя C или C++, после расшифровки файла на диск - как я могу гарантировать, что он будет удален, если приложение выйдет из строя или система отключится и не сможет его правильно очистить? Используете C или C++ в Windows и Linux?
Как гарантировать очистку файлов, расшифрованных во время выполнения?
Ответы (13)
Не записывайте расшифрованный файл на диск вообще.
Если система выключена, файл все еще находится на диске, и доступ к диску и, следовательно, к файлу возможен.
Исключением будет использование зашифрованной файловой системы, но это вне контроля вашей программы.
К сожалению, нет 100% надежного способа гарантировать, что файл будет удален в случае полного сбоя системы. Подумайте о том, что произойдет, если пользователь просто вытащит вилку из розетки, пока файл находится на диске. Никакая обработка исключений не защитит вас от этого (наихудшего) случая.
Лучшее, что вы можете сделать, это вообще не записывать расшифрованный файл на диск. Если файл существует как в зашифрованном, так и в расшифрованном виде, это слабое место в вашей безопасности.
Следующее лучшее, что вы можете сделать, это использовать предложение Брайана по структурированной обработке исключений, чтобы убедиться, что временный файл очищен. Это не защитит вас от всех возможностей, но будет иметь большое значение.
Наконец, я предлагаю вам проверять наличие временных расшифрованных файлов при запуске вашего приложения. Это позволит вам навести порядок после вашего приложения в случае полного сбоя системы. Хранить эти файлы в течение любого времени не идеально, но, по крайней мере, это позволит вам избавиться от них как можно быстрее.
Я не знаю, работает ли это в Windows, но в Linux, предполагая, что вам нужен только один процесс для доступа к расшифрованному файлу, вы можете открыть файл, а затем вызвать unlink() для удаления файла. Файл будет продолжать существовать до тех пор, пока процесс держит его открытым, но когда он будет закрыт или процесс умрет, файл больше не будет доступен.
Конечно, содержимое файла все еще находится на диске, так что на самом деле вам нужно больше, чем просто удалить его, но и обнулить содержимое. Есть ли причина, по которой расшифрованный файл должен находиться на диске (размер?). Лучше просто хранить расшифрованную версию в памяти, желательно помеченную как не подлежащую обмену, чтобы она никогда не попала на диск.
Постарайтесь полностью избежать этого:
Если файл является конфиденциальным, лучше всего не записывать его на диск в расшифрованном формате.
Защита от сбоев: структурированная обработка исключений:
Однако вы можете добавить структурированную обработку исключений, чтобы перехватывать любые сбои.
Что, если они перекроют вилку?:
Есть способ защититься от этого...
Если вы работаете в Windows, вы можете использовать MoveFileEx и параметр MOVEFILE_DELAY_UNTIL_REBOOT со значением NULL для удаления файла при следующем запуске. Это защитит от случайного выключения компьютера с неудаленным файлом. Вы также можете убедиться, что у вас есть открытый дескриптор этого файла (не указывайте права общего доступа, такие как FILE_SHARE_READ, и используйте CreateFile, чтобы открыть его). Таким образом, никто не сможет прочитать его.
Другие способы избежать проблемы. Все это не оправдания наличия расшифрованного файла на диске, а:
Вы также можете рассмотреть возможность записи в файл, размер которого превышает MAX_PATH, используя синтаксис файла \\?\. Это гарантирует, что файл не будет доступен для просмотра в проводнике Windows.
Вы должны установить файл с временным атрибутом
Вы должны установить файл со скрытым атрибутом
В C (и, я полагаю, и в C++ тоже), пока ваша программа не падает, вы можете зарегистрировать обработчик atexit() для очистки. Просто избегайте использования _exit() или _Exit(), так как они обходят обработчики atexit().
Однако, как указывали другие, лучше избегать записи расшифрованных данных на диск. И просто использовать unlink() (или эквивалент) недостаточно; вам нужно перезаписать некоторые другие данные поверх исходных данных. И журналируемые файловые системы делают это очень сложным.
Процесс не может защищать или наблюдать за собой. Ваша единственная возможность — запустить второй процесс в качестве своего рода сторожевого таймера, который регулярно проверяет работоспособность дешифрующего другого процесса. Если другой процесс выйдет из строя, сторожевой таймер заметит и удалит сам файл.
Вы можете сделать это с помощью Hearth-beats (регулярный опрос другого процесса, чтобы узнать, жив ли он еще) или с помощью прерываний, отправленных самим другим процессом, которые вызовут тайм-аут в случае сбоя.
Например, вы можете использовать сокеты, чтобы установить соединение между сторожевым таймером и вашим приложением.
Становится ясно, что вам нужен какой-то механизм блокировки, чтобы предотвратить свопинг на файл подкачки / раздел подкачки. В системах Posix это можно сделать с помощью файла m(un)lock* family of functions.
Возникла проблема с удалением файла. Это не совсем прошло. Когда вы удаляете файлы с жесткого диска (не считая корзины), файл на самом деле не исчезает. Просто указатель на файл удаляется.
Вы когда-нибудь видели эти шпионские фильмы, где они перезаписывают жесткий диск 6, 8, 24 раза, и вот как они узнают, что он чист. Ну, они делают это не просто так. Я бы приложил все усилия, чтобы не хранить расшифрованные данные файла. Или, если необходимо, сделайте это небольшими объемами данных. Четные, разрозненные данные.
Если вы должны, то они попытаются поймать, чтобы защитить вас немного.. Хотя ничто не может защитить от отключения электроэнергии.
Удачи.
Проверьте tmpfile().
Это часть BSD UNIX, не уверен, является ли он стандартным.
Но он создает временный файл и автоматически разъединяет его, так что он будет удален при закрытии.
Запись в файловую систему (даже временную) небезопасна.
Делайте это только в случае крайней необходимости.
При желании вы можете создать файловую систему в памяти.
Никогда не использовал ее сам, поэтому никаких рекомендаций, но быстрый поиск в Google нашел несколько.
В C++ вы должны использовать тактику RAII:
class Clean_Up_File {
std::string filename_;
public Clean_Up_File(std::string filename) { ... } //open/create file
public ~Clean_Up_File() { ... } //delete file
}
int main()
{
Clean_Up_File file_will_be_deleted_on_program_exit("my_file.txt");
}
RAII помогает автоматизировать большую часть очистки. Вы просто создаете объект в стеке и очищаете этот объект в конце своего жизненного цикла (в деструкторе, который будет вызываться, когда объект выходит из области видимости). ScopeGuard даже немного упрощает эту задачу.
Но, как уже упоминалось, это работает только в «нормальных» обстоятельствах. Если пользователь отключит компьютер, вы не можете гарантировать, что файл будет удален. И может быть возможно восстановить файл (даже в UNIX можно "загрузить жесткий диск а>").
Кроме того, как указано в комментариях, в некоторых случаях объекты не выходят за пределы области видимости (например, функция std::exit(int) выходит из программы, не выходя из текущей области), поэтому RAII в этих случаях не работает. Лично я никогда не вызываю std::exit(int), а вместо этого либо выбрасываю исключения (которые раскручивают стек и вызывают деструкторы; что я считаю «аномальным выходом»), либо возвращаю код ошибки из main() (который вызывает деструкторы и что я также считаю «Аномальный выход»). IIRC, отправка SIGKILL также не вызывает деструкторы, а SIGKILL невозможно поймать, так что вам тоже не повезло.
Это сложная тема. Как правило, вы не хотите записывать расшифрованные файлы на диск, если можете этого избежать. Но хранение их в памяти не всегда гарантирует, что они не будут записаны на диск как часть файла подкачки или иным образом.
Я читал статьи об этом давным-давно, и я помню, что между Windows и Linux была некоторая разница в том, что можно было гарантировать, что страница памяти не будет записана на диск, а нельзя; но я не помню четко.
Если вы хотите проявить должную осмотрительность, вы можете найти эту тему и прочитать об этом. Все зависит от вашей модели угроз и того, от чего вы хотите защититься. В конце концов, вы можете использовать сжатый воздух, чтобы охладить оперативную память и извлечь из нее ключ шифрования (что на самом деле было в новом шпионском шоу Кристиана Слейтера «Мой собственный злейший враг» — которое, как я думал, было лучшим применением передовых, точных, компьютерных технологий). техники безопасности в медиа еще)
в Linux/Unix используйте unlink сразу после создания файла. Файл будет удален, как только ваша программа закроет дескриптор файла или выйдет.
Более того, файл будет удален даже в случае сбоя всей системы, потому что он удаляется, как только вы отсоединяете его.
Физически данные с диска, разумеется, не удалятся, поэтому они все равно могут быть доступны для взлома.
Помните, что компьютер может быть выключен в любое время. Затем кто-то, кто вам не нравится, может загрузиться с Linux live CD и изучить ваш диск с любым желаемым уровнем детализации, ничего не меняя. Ни одна система, которая записывает открытый текст на диск, не может быть защищена от таких атак, и их несложно осуществить.
Вы можете настроить функцию, которая будет многократно перезаписывать файл единицами и нулями, предпочтительно вводя некоторую случайность, и настроить ее для запуска в конце программы или при выходе. Это будет работать при условии отсутствия аппаратных или программных сбоев, сбоев питания или других сбоев, а также при условии, что файловая система записывает только в те сектора, которые, как утверждается, использует (журналируемые файловые системы, например, могут оставлять части файла в другом месте). ).
Следовательно, если вам нужна безопасность, вам нужно убедиться, что открытый текст не записывается, а это также означает, что его нельзя записать в пространство подкачки или что-то подобное. Узнайте, как пометить память как неподкачиваемую на всех платформах, для которых вы пишете. Убедитесь, что ключи расшифровки и тому подобное обрабатываются так же, как и открытый текст: никогда не записываются на диск ни при каких обстоятельствах и хранятся в памяти без возможности замены.
Затем ваша система должна быть защищена от атак, за исключением проникновения злоумышленников, прерывания работы и замораживания ваших микросхем ОЗУ перед отключением питания, чтобы они не потеряли свое содержимое перед передачей для проверки. Или власти требуют ваш ключ, законно (ознакомьтесь с вашими местными законами здесь) или незаконно.
Мораль этой истории: настоящая безопасность — это сложно.
Метод, который я собираюсь реализовать, будет заключаться в потоковом расшифровывании, так что единственная часть, которая находится в памяти, — это часть, которая расшифровывается во время чтения по мере использования данных. Вот схема трубопровода:
Это будет потоковая реализация, поэтому единственные данные, которые находятся в памяти, — это данные, которые я потребляю в приложении в любой момент. Это делает некоторые вещи сложными, учитывая, что многие традиционные трюки с файлами больше не доступны, но, поскольку реализация будет основана на потоке, я все равно смогу искать разные точки файла, которые будут преобразованы в поток шифрования для расшифровки в разные разделы.
По сути, это будет шифрование блоков файла за раз, поэтому, если я попытаюсь перейти к определенной точке, он расшифрует этот блок для чтения. Когда я читаю блок, он расшифровывает следующий блок и освобождает предыдущий (в потоке шифрования).
Эта реализация не требует расшифровки в файл или в память и совместима с другими потребителями и поставщиками потоков (fstream).
Это мой «план». Я никогда раньше не занимался подобной работой с fstream и, скорее всего, задам вопрос, как только буду готов над этим работать.
Спасибо за все остальные ответы - это было очень информативно.