Технічна стаття

Виявлення підробленого XLSX: HotXLS Agile dataIntegrity HMAC

HotXLS записує відповідний специфікації блок dataIntegrity у Agile-зашифровані пакети XLSX і перевіряє його під час відкриття. HMAC-SHA-512 покриває весь потік EncryptedPackage, включно з його восьмибайтовим префіксом StreamSize, і перевіряється над шифротекстом ще до розшифрування будь-якого сегмента, тож неправильний пароль чи модифікований пакет виявляється, а не розшифровується в сміття

Шифрування без цілісності — це лише половина відповіді, а формати файлів Office роблять цю прогалину легкою для того, щоб її не помітити, бо шифрування зовні виглядає настільки ретельним. Розуміння того, що обіцяє кожен шар, — це те, що тримає перевірку безпеки короткою

Що насправді обіцяє зашифрована книга?

Agile-шифрування, визначене в [MS-OFFCRYPTO], дає вам конфіденційність через AES у режимі CBC з ключем, похідним від ітерованого хеша пароля SHA-512. Конфіденційність — це вся обіцянка цієї конструкції. CBC — не автентифікований режим: він нічого не каже про те, чи є шифротекст, який ви розшифровуєте, тим самим шифротекстом, що був записаний

Практичний наслідок конкретний. Переверніть біти в зашифрованому пакеті — і CBC із задоволенням розшифрує їх в інший відкритий текст. Зазвичай десь далі по конвеєру ви отримаєте помилку розбору ZIP, бо пошкоджений потік deflate рідко виживає, але слово «зазвичай» тут виконує величезну роботу, а помилка парсера далі по конвеєру — жахливе місце, щоб дізнатися, що файл було змінено. Елемент dataIntegrity існує саме для того, щоб відповісти на це питання прямо, ще до розшифрування, за допомогою MAC над точними байтами

Як виконується перевірка і в якому порядку

Найцікавіше тут — порядок. HotXLS виводить проміжний ключ із пароля, розшифровує зашифровані ключ HMAC і значення HMAC з атрибутів dataIntegrity за допомогою IV, похідних від ключів блоків, обчислює HMAC-SHA-512 над зашифрованим пакетом у тому вигляді, в якому він збережений, і порівнює. Лише після цього починається розшифрування сегментів

Перевірка MAC над шифротекстом, а не над відкритим текстом, — це стандартна дисципліна encrypt-then-MAC, і саме вона робить перевірку змістовною: підроблений пакет відхиляється без того, щоб хоч один контрольований атакуючою стороною байт пройшов через шлях розшифрування й розпакування. Обидва порівняння на шляху відкриття — хеш verifier пароля та значення HMAC — накопичують розбіжності через XOR і OR по всьому дайджесту замість того, щоб повертатися раніше на першому невідповідному байті, тож жодне з них не розкриває позицію байта через синхронізацію в часі

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Працює для звичайних, Standard-зашифрованих і Agile-зашифрованих файлів
    if Book.OpenEncrypted('incoming.xlsx', PasswordFromVault) = 1 then
      ProcessWorkbook(Book)
    else
      // Неправильний пароль, або пакет, чий HMAC dataIntegrity не збігся
      Quarantine('incoming.xlsx');
  except
    on E: Exception do
      Quarantine('incoming.xlsx: ' + E.Message);
  end;
  Book.Free;
end;

На боці запису у вашому коді нічого не змінюється. SaveAsEncrypted автоматично видає цей блок, а солі, вхідні дані verifier і ключ HMAC надходять з CryptGenRandom. Якщо цей виклик зазнає невдачі, HotXLS викликає виняток, а не відкочується до слабшого джерела. Fail-closed CSPRNG — це не параноя; тихе пониження до передбачуваного джерела випадковості створює файли, що виглядають зашифрованими, проходять кожен функціональний тест і при цьому нічого не варті

Чому файли без цього блоку все одно відкриваються?

Тому що величезна кількість Agile-зашифрованих книг в обігу написана виробниками, які взагалі опускають dataIntegrity, а відхилення таких файлів зламало б набагато більше легітимної роботи, ніж захистило б. HotXLS вважає цілісність наявною лише тоді, коли обидва атрибути — зашифрований ключ HMAC і зашифроване значення HMAC — присутні й коректно сформовані. В іншому разі перевірка пропускається, і файл відкривається, як і раніше

Це рішення про сумісність із наслідком для безпеки, який варто явно назвати у вашій власній моделі загроз: відсутність блоку неможливо відрізнити від його видалення атакуючою стороною, бо самі атрибути перебувають поза MAC, який вони б несли. Якщо ви керуєте обома кінцями конвеєра, розглядайте відсутній блок як збій політики на рівні застосунку. Якщо ви приймаєте файли з усього світу, розглядайте перевірку такою, якою вона є, — цінним сигналом за наявності й жодним сигналом за відсутності

Пароль на редагування — це домовленість, а не межа захисту

Класичні книги XLS підтримують окремий механізм, який регулярно плутають із шифруванням: резервування на запис, запит Excel «пароль для редагування». HotXLS надає доступ до нього через SetModifyPassword, який приймає пароль, прапорець рекомендованого режиму лише для читання й ім'я користувача, що резервує файл, і повідомляє стан через IsWriteReserved. Передача порожнього пароля знімає резервування

Записується пара записів WRITEPROT і FILESHARING, що несуть прапорець рекомендованого режиму лише для читання, застарілий 16-бітний хеш пароля та ім'я користувача як рядок BIFF8 Unicode. Цей 16-бітний хеш — контрольна сума, а не криптографічний дайджест, і вміст документа взагалі не зашифрований. Будь-хто, хто відкриє файл будь-яким іншим інструментом, прочитає все. Справжнє завдання цієї функції — координація: вона повідомляє наступній людині, що хтось вважає цей файл своїм для редагування, — та сама категорія, що й елементи керування на рівні аркуша, розглянуті в статті про захист аркуша XLSX і опції дозволів

var
  Book: IXLSWorkbook;   // з підрахунком посилань через інтерфейс: не викликати Free
begin
  Book := TXLSWorkbook.Create;
  if Book.Open('shared-model.xls') = 1 then
  begin
    // Рекомендований режим лише для читання, зарезервовано службою звітності
    Book.SetModifyPassword('edit-me', True, 'Reporting Service');

    if Book.IsWriteReserved then
      Book.SaveAs('shared-model.xls', xlExcel97);
  end;
end;

Використовуйте обидва шари для того, у чому кожен добрий. Справжня конфіденційність походить від SaveAsEncrypted із паролем, якого не має ніхто поза аудиторією, що дає вивід AES-256, описаний у статті про AES-захищений вивід XLSX. Резервування на запис додається зверху, коли книга — це артефакт спільного редагування і ви хочете, щоб Excel запитував, перш ніж хтось перезапише файл

Що перевіряти на шляху приймання недовірених файлів

Перевірка цілісності захищає зашифроване корисне навантаження, а не контейнер навколо нього. Файл XLSX — це архів ZIP, і структура архіву розбирається ще до того, як запрацює будь-яка логіка шифрування, тож перевірка на рівні контейнера має стояти першою в ланцюжку; конкретні режими збою розглянуті в статті про перевірку кінця центрального каталогу ZIP для недовірених XLSX. Після цього ставтеся до збою цілісності та неправильного пароля як до однієї операційної події, бо з вашого боку вони за задумом невідрізнимі, і обидва означають, що файлу не можна довіряти бути тим, чим його вважає відправник

Фіксуйте в журналі, які файли взагалі несли блок dataIntegrity. На кількох тисячах документів ця статистика розповідає щось корисне про інструментарій ваших відправників і перетворює перевірку окремого файлу на спостереження на рівні всього парку, на основі якого можна діяти

HotXLS читає та записує XLS, XLSX і ODS з Delphi та C++Builder без встановленого Excel, реалізуючи шляхи Standard і Agile шифрування [MS-OFFCRYPTO] на Pascal. API шифрування, захисту та роботи з книгою задокументовані на сторінці HotXLS Delphi spreadsheet component