При всей своей сумасшедшей сложности и высокотехнологичности, тема «защита информации» в своей практической реализации сводится к манипулированию весьма небольшим количеством приёмов, инструментов и ингредиентов. Этим она мне напоминает кулинарию. Там тоже есть весьма небольшое количество приборов и инструментов (плита, духовка, ножи, кастрюли, сковородки, миксер, блендер, мясорубка и т.п.) и некоторый ограниченный набор исходных продуктов. А что получится «на выходе» - пережаренная яичница комками или шедевр высокой кулинарии - это зависит исключительно от искусства повара.
Давайте вместе посмотрим, что же у нас на этой весьма специфической «кухне», и что с этим вообще можно сделать.
1. Решаемые задачи
Первая традиционная задача - это спрятать информацию от глаз тех товарищей, которым видеть её, как говорится, не обязательно.
Вторая традиционная задача - защитить данные от искажений и фальсификаций.
Вот такие задачи решаются на нашей кухне. Готовим первое и второе. Главный инструмент для приготовления первого -
шифрование, а для второго -
электронная подпись.
2. Важные принципы
- «Лучше перебдеть, чем недобдеть». Не для того инженеры фирмы Интел разгоняли свои гигагерцы, чтобы жались из-за нескольких сотен тактов процессора, рискуя при этом своей жизнью. Не для того инженеры Сигейта и Вестерн-диджитала раздували свои терабайтные диски, чтобы мы пожалели дополнительные 16 байт на длину ключа.
- Враг умнее, чем кажется. Мы тут все любители, вынужденные заниматься этой проблематикой лишь потому, что жизнь заставляет, а противостоят нам профессионалы. Возможно, лучшие в своей индустрии.
- Плохая защита хуже, чем полное отсутствие защиты. Защита даёт ощущение защищённости, но если она плохая, то это ощущение ложное.
- Секретные технологии и алгоритмы - в мусор. Не рассчитывайте, что вы сможете придумать и сохранить в тайне ото всех новую конструкцию замка. Хранить в тайне нужно только ключ, а конструкция замка должна быть опубликована, обсуждена и всесторонне опробована на прочность сообществом тех, кто разбирается в этих вопросах.
- Вся самодеятельность, связанная с криптографией, на территории РФ незаконна. Даже если ты всего лишь хочешь зашифровать богомерзким шифром Цезаря таблицу рекордов в собственноручно написанной игрушке, будь добр получить на это лицензию ФСБ (от оно как, Михалыч…). Любое встраивание криптографических средств в системы обработки и передачи данных - лицензируемый вид деятельности.
- «Готовые продукты», только если они не в открытых исходниках - в мусор. Все эти «интегрированные решения для обеспечения вашей безопасности» - такое скопище «закладок», что оторопь берёт.
- Есть множество простых правил гигиены, несоблюдение которых способно свести на нет любую защиту, сколь бы надёжной она ни была. Если самый секретный пароль написан на бумажке, приклеенной к монитору, надёжность защиты данных, сколь бы хитроумной она ни была, тождественно равна нулю.
- Если враг сможет выиграть войну, взломав один самый главный сервер, он этот сервер обязательно найдёт, взломает и выиграет войну. Не нужно даже пытаться все яйца хранить в одной корзине.
- Ломать шифры «в лоб» - почти безнадёжное занятие. Этим никто не занимается. Обычно используют уязвимости в сопутствующем софте, трояны, руткиты, всякую прочую прослушку, а также традиционные «гуманитарные» технологии - шантаж, подкуп, агентов под прикрытием, промывание мозгов и терморектальный криптоанализ.
- До недавнего времени сам факт использования стойкого крипто уже служил чётким указанием для «правоохранительных» органов на то, что субъект занимается чем-то незаконным. Сейчас, когда шифропанковскими инструментами начали пользоваться любители книг, фильмов и музыки, ситуация постепенно исправляется.
- В деле защиты информации нужно быть параноиком, но при этом ни в коем случае нельзя терять голову. Безопасность - не цель, а всего лишь средство, помогающее дожить до достижения цели.
- Я сторонник максимальной открытости, честности и откровенности, но самый надёжный фундамент для этого всего - правильно применяемая стойкая криптография и строжайшее соблюдение информационной гигиены. В противном случае открытость превращается в самонадеянность, честность в наказание за честность, а откровенность в глупость.
3. Основные инструменты и приспособления
По ходу дела буду вводить обозначения, из которых потом можно будет собирать формулы, чётко и ясно поясняющие суть происходящих процессов.
3.1. Генератор случайных чисел (ГСЧ)
Механизм, позволяющий получить случайную последовательность нулей и единиц. Очень важный инструмент, очень часто используется, например, при создании сеансовых ключей. Случайная последовательность - это такая, про которую нельзя сделать никаких предположений. Все возможные комбинации равновероятны. Внутренние взаимосвязи между выдаваемыми порциями данных отсутствуют.
Некоторые думают, что выдача ГСЧ - это обязательно хорошо перемешанный набор нулей и единиц. Конечно же, это не так. Если мы попросим хороший ГСЧ выдать десять бит, то он с одинаковой вероятностью (1/1024) может выдать такое: «0110011001», «0000000000», «1111111111», «0101010101» или «0000011111». Если ГСЧ выдал «0000000000000000», то вероятность того, что следующей цифрой будет «0», гарантированно равна 50%. Как всегда.
Задачи защиты данных предъявляют очень жёсткие требования к качеству ГСЧ. Генерируемые алгоритмически последовательности псевдослучайных чисел не годятся совсем.
Обозначать операцию получения случайного числа буду так:
<результат> = NewRN()
New random number. Если нужно будет указать, сколько конкретно нужно бит, количество бит пойдёт в аргумент. Например, если нужно получить 256 бит (32 байта), то выглядеть это будет так:
Key = NewRN(256)
Существует множество методов получения случайных чисел. Некоторые из них встроены в операционные системы и средства разработки. Выше я рекомендовал не пользоваться сторонними инструментами, но генерация случайных чисел - как раз тот случай, когда пользоваться сторонними средствами можно и нужно. Если в публичных источниках пишут, что некий ГСЧ можно использовать для криптографии, значит им можно пользоваться и голову себе больше этой проблемой не забивать. Поставить «закладку» в ГСЧ - вообще не реально.
Подробнее о ГСЧ -
здесь.
3.2. Вычисление хеш-функции
Оно же цифровая свёртка, оно же получение отпечатка. Алгоритм, который на вход получает некоторый объём данных, и по детерминированному алгоритму рассчитывает число. При этом порция данных на входе может быть любого размера (хоть один байт, хоть килобайт, хоть терабайт - не важно), а результат получается всегда одной и той же длины. Например, для алгоритма CRC32 это 4 байта, для алгоритма MD5 это 16 байт (128 бит), для SHA-2 - от 28 до 64 байт в зависимости от выбранного варианта.
Про цифровую свёртку можно сказать следующее:
- Получить из отпечатка само сообщение невозможно. Преобразование одностороннее. Впрочем, если известно всё сообщение кроме нескольких символов, и известен его отпечаток этого сообщения, то восстановить сообщение почти наверняка можно простым перебором вариантов.
- Подделать сообщение, защищённое отпечатком, очень сложно. Подделать - значит подменить сообщение таким другим сообщением, чтобы отпечаток этого другого сообщения совпал с отпечатком исходного. Упомянутый выше алгоритм CRC32 в этом плане ненадёжен (т.е. за какие-то жалкие несколько миллиардов попыток подделать сообщение можно, для современных компьютеров это дело нескольких минут), и поэтому применяется только для предотвращения непреднамеренной порчи данных.
Операцию получения отпечатка буду обозначать так:
<результат> = Hash(<данные>)
Если нужно будет указать конкретный алгоритм, добавится второй параметр:
<результат> = Hash(<данные>, <алгоритм>)
Пример:
MD = Hash(D, MD5)
Подробнее о хештровании -
здесь.
Описания наиболее популярных алгоритмов:
MD5,
SHA-1,
SHA-2.
3.3. Симметричное шифрование
Берём данные, которые нам нужно спрятать, берём секретную последовательность (ключ), скармливаем это всё шифрующему алгоритму и получаем абракадабру. Для того, чтобы получить исходное сообщение из этой абракадабры, нужен дешифрующий алгоритм и этот же самый ключ. Один и тот же ключ используется и для шифрования, и для дешифрования, поэтому такое шифрование называется симметричным.
Обозначение для операции шифрования:
<зашифрованные данные> = Crypt(<данные>, <секретный ключ>)
Обозначение для операции дешифрования:
<данные> = Decrypt(<зашифрованные данные>, <секретный ключ>)
Если нужно будет указать конкретный алгоритм, то добавится третий параметр.
Существует много разных методов симметричного шифрования начиная от простейшей подстановки по таблице, взлом которой описывается в одном из рассказов про Шерлока Холмса, до гораздо более изощрённых, считающихся надёжными. Особо хочу упомянуть один метод, абсолютная надёжность которого доказана математически. Это так называемый
кодовый блокнот. Предельно простой в реализации метод. Единственная его неприятность заключается в том, что длина ключа должна быть строго равна длине шифруемого сообщения. И повторное использование ключа не допускается.
Подробнее о симметричном шифровании -
здесь.
Наиболее популярные алгоритмы:
AES,
3DES,
Twofish.
3.4. Асимметричное шифрование
При асимметричном шифровании для собственно шифрования используется один ключ, а для дешифрования - другой. При этом из ключа шифрования невозможно (вернее, очень-преочень трудно) получить ключ, которым можно расшифровать сообщение. Ключ шифрования публикуется максимально широко (хотя бы даже на визитке его можно напечатать и раздавать всем желающим), поэтому называется публичным ключом (public key). Ключ дешифрования (секретный ключ, secret key) держится в максимально надёжном месте и охраняется пущи зеницы ока. Пара ключей (key pair) - публичный и секретный - генерируются вместе. Обозначать этот процесс буду так:
(<публичный ключ>, <секретный ключ>) = NewKeyPair()
Шифрование:
<зашифрованные данные> = Crypt(<данные>, <публичный ключ>)
Дешифрование:
<данные> = Decrypt(<зашифрованные данные>, <секретный ключ>)
Допустим, есть персонажи Алиса и Боб (очень популярные в литературе про криптографию), которые решили обмениваться секретными сообщениями. Можно им предложить такой сценарий:
- Алиса создаёт свою пару ключей: (PKA, SKA) = NewKeyPair()
- Боб создаёт свою пару ключей: (PKB, SKB) = NewKeyPair()
- Алиса передаёт Бобу свой публичный ключ: A → (PKA) → B
- Боб передаёт Алисе свой публичный ключ: B → (PKB) → A
- Алиса пишет письма (D), шифрует их и шлёт Бобу, который их читает:
A: ED = Crypt(D, PKB)
A → (ED) → B
B: D = Decrypt(ED, SKB) - Боб шлёт письма Алисе:
B: ED = Crypt(D, PKA)
B → (ED) → A
A: D = Decrypt(ED, SKA)
Сейчас асимметричное шифрование является основой всей гражданской криптографии, но вот так вот «в лоб», как описано мной, используется редко, потому что все известные алгоритмы асимметричного шифрования/дешифрования очень медленные. На несколько порядков медленнее, чем симметричные алгоритмы. Поэтому, когда нужно прокачать существенный объём (то есть считаем, что всегда), проделывают такой трюк с использованием промежуточного симметричного так называемого сеансового ключа (K). Считаем, что Алиса и Боб уже обменялись публичными ключами и теперь им нужно прокачать видяху на пару гигабайт:
A: K = NewRN() - одноразовый промежуточный ключ - случайное число
A: EK = Crypt(K, PKB) - зашифровали одноразовый ключ медленным асимметричным алгоритмом
A: ED = Crypt(D, K) - зашифровали пару гигабайт быстрым симметричным алгоритмом
A → (EK, ED) → B
B: K = Decrypt(EK, SKB) - Боб расшифровал промежуточный ключ
B: D = Decrypt(ED, K) - … и уже с его помощью само сообщение
Всё выглядело бы совсем красиво и радужно, если бы не проблема, которая называется
«человек-посередине» («man-in-the-middle», MITM). Если злоумышленник может перехватывать все сообщения A → B и наоборот, то он может вовремя подсуетиться и подменить ключи: Алисе вместо ключа Боба подсунуть свой собственный открытый ключ, и Бобу тоже вместо ключа Алисы дать собственный. В результате Алиса с Бобом будут ни сном ни духом не догадываться, что шифруют свои сообщения они не ключами друга, а ключами злоумышленника, который всю переписку на лету расшифровывает, читает, зашифровывает и передаёт дальше. Эта проблема решается комплексом мероприятий, который называется
«инфраструктура открытых ключей» («public key infrastructure», PKI), которую рассмотрю где-нибудь в следующей серии.
Что интересно, при использовании шифрования с открытым ключом сам отправитель не может прочитать отправленное им же сообщение, если он не сохранил у себя оригинал. Но при использовании промежуточного ключа можно эту проблему обойти. Допустим, Алиса решила выложить нечто секретное на специальный шифропанковский ресурс и хочет, чтобы этот файл смогла расшифровать она сама, а также два адресата - Боб и Катя. Последовательность действий (обмен ключами опускаю):
A: K = NewRN()
A: EKA = Crypt(K, PKA) - для себя
A: EKB = Crypt(K, PKB) - для Боба
A: EKC = Crypt(K, PKC) - для Кати
A: ED = Crypt(D, K)
A → (EKA, EKB, EKC, ED) → Общий ресурс
B: K = Decrypt(EKB, SKB) - Боб расшифровал промежуточный ключ
B: D = Decrypt(ED, K)
С: K = Decrypt(EKC, SKС) - Катя расшифровала промежуточный ключ
С: D = Decrypt(ED, K)
A: K = Decrypt(EKA, SKA) - Алиса расшифровала промежуточный ключ
A: D = Decrypt(ED, K)
Подробнее об асимметричном шифровании -
здесь.
Наиболее популярные алгоритмы:
RSA,
Elgamal,
ECDSA.
3.5. Электронная подпись
Электронная подпись - это способ, с помощью которого Боб может удостовериться, что сообщение пришло к нему от Алисы в неизменном виде. Очень похоже на асимметричное шифрование (более того, одна и та же ключевая пара обычно используется и для шифрования/дешифрования, и для подписания/проверки) с той лишь разницей, что при подписании используется секретный ключ, а при проверке - открытый.
Подписание:
<подпись> = Sign(<данные>, <секретный ключ>)
Проверка подписи (результат - «да» или «нет»):
<результат> = Check(<данные>, <подпись>, <открытый ключ>)
С некоторой долей погрешности можно сказать, что подпись - это хеш сообщения, зашифрованный секретным ключом отправителя. Проверка подписи заключается в том, чтобы собственноручно вычислить хеш сообщения, расшифровать открытым ключом отправителя полученный зашифрованный хеш, и эти два хеша сравнить. Если совпадают, сообщение осталось неизменным, если не совпадают - увы.
Шифрование и подписание часто используют совместно. Вот так:
A: DS = Sign(D, SKA)
A: K = NewRN()
A: EK = Crypt(K, PKB)
A: ED = Crypt((D, DS), K)
A → (EK, ED) → B
B: K = Decrypt(EK, SKB)
B: (D, DS) = Decrypt(ED, K)
B: ОК = Check(D, DS, PKA)
Если ОК - действительно ОК, то, значит, подпись верна и сообщение было действительно отправлено Алисой в точности таким, каким его получил Боб.
Понятное дело, что если между Алисой и Бобом встрял злоумышленник, подменивший ключи, всё подписание насмарку, и Боб вместо отправленного Алисой «я тебя люблю» вполне может получить как бы подписанное Алисой сообщение «иди к чёрту».
Электронная подпись применяется не только совместно с шифрованием. Например, все приложения, устанавливаемые на айфоны, айподы и айпэды, обязательно должны быть подписаны специальным особо секретным ключом, который есть у фирмы Apple. Таким образом Apple не допускает художественной самодеятельности, при которой к их ручонкам совсем ничего по ходу дела не прилипает.
3.6. Дополнительные инструменты
Уничтожение данных
Если вы удалили секретный файл с диска так, как привыкли это делать, то его парой кликов мышкой можно достать из корзины. Если удалили «насовсем», то его, конечно, в корзине нет, но специальными утилитками его можно легко восстановить. Когда приходят люди в масках и реквизируют оргтехнику, они, конечно, во всех этих помойках пороются. Для того, чтобы их нелёгкий труд ничего им не принёс, нужно все секретные данные, попавшие на диск, перезаписывать поверх чем-нибудь, что им не так интересно. Например, нулями, случайными числами или издевательскими фразами. Для этого есть специальные
утилитки.
Особо хардкорные параноики говорят, что нужно записать нули, потом единицы, потом случайные числа, потом снова нули и так несколько раз. Я не приветствую такую паранойю. Чем-то это похоже на стальной презерватив. Он, конечно, надёжнее резинового, но если уже дошло до таких мер безопасности, то это хороший признак того, что пора отойти от дел и больше не мучить ни себя, ни других. Современные жёсткие диски работают на пределе физических возможностей, и для извлечения крох информации из остаточной намагниченности нужна аппаратура такого уровня, что её применение для нас, балбесов, было бы слишком большой честью.
Вообще, есть очень хорошее правило: вообще не помещать в открытом виде секретные данные (особенно - секретные ключи) на энергонезависимые носители. При этом следует иметь в виду, что операционная система сама по своему хотению может сбрасывать содержимое оперативной памяти в своп-файл или файл гибернации. Поэтому особо секретные данные (ага, ключи) не нужно хранить даже в оперативной памяти дольше, чем это необходимо.
Сжатие данных
Перед шифрованием данные желательно сжимать. Хотя бы самым простым алгоритмом компрессии. После шифрования ничего сжать уже не получится. Ни на копейку.
Вот, собственно, и есть те базовые пять основных и два вспомогательных «кирпичиков», из которых строятся все современные системы защиты данных. Что удивительно, «кирпичики» эти чрезвычайно просты в реализации. Все алгоритмы известны, опубликованы и легко могут быть реализованы любым начинающим программистом почти на любом языке программирования. Самое сложное и интересное начинается тогда, когда из этих «кирпичиков» начинают строиться весьма причудливые «домики».
Продолжение -
вот здесь. Про PKI, который есть очень интересная и драматичная тема.