Соблюдать требования к безопасности приложений обычно можно с помощью простых в применении технологий защиты достаточно высокого уровня, таких как виртуальные частные сети (Virtual Private Network, VPN) или протокол Transport Layer Security (TLS), и, как правило, разработчикам советуют использовать именно такие механизмы, поскольку их защитные и другие свойства приняты в качестве стандартов довольно обширным сообществом. Можно рассчитывать на то, что такие хорошо изученные протоколы и широко применяемые реализации будут иметь меньше проблем, связанных с безопасностью, чем решения, созданные самостоятельно.
Несмотря на то что VPN и TLS прекрасно защищают данные при передаче, они не способны обеспечить их безопасность в остальное время – в периоды, когда данные зачастую с большей вероятностью подвергаются риску. Однако и здесь имеются стандартные инструментальные средства, в частности шифрующие файловые системы, гарантирующие, что файлы на диске защищены от хакеров, которые не смогут использовать соответствующую регистрационную информацию операционных систем. И по тем же самым причинам для обеспечения безопасности постоянно хранящихся объектов, например файлов, рекомендуется использовать стандартные инструменты. Однако в некоторых случаях разработчики не могут применять стандартные инструментальные средства, и приходится реализовывать собственные механизмы шифрования.
Возможные ситуации
Одна из самых распространенных ситуаций, в которых разработчики приложений напрямую используют шифрование, – это выпуск защищенных шифрованием токенов, сертификатов или псевдонимов, применяемых для управления сессиями и контроля доступа в Web-приложениях. Например, по условиям спецификации OAuth (www.oauth.net) провайдеры сервисов должны генерировать такие токены, а так называемые потребительские приложения – применять шифрование, когда необходимо подтвердить, что потребитель имеет право использовать сертификат. В таких ситуациях сами серверные объекты, требующие максимальной защиты, являются ключами, которые используются для обеспечения безопасности токенов. Так сделано потому, что возможность злоупотребления или фальсификации одного токена потенциально менее опасна, чем возможность создавать столько токенов, сколько вы хотите, а именно это, по существу, и дает доступ к ключам. Таким образом, использование шифрования для защиты протоколов приложений определяет новое требование к безопасности, согласно которому мы должны защищать ключи как объекты приложений.
Помимо этого, при использовании базы данных разработчику приложений иногда может потребоваться защитить информацию от потенциально некорректного использования администраторами баз. Для этого достаточно легко зашифровать информацию, чтобы обеспечить конфиденциальность. В большинстве случаев это необходимо для защиты таких важных сведений, как имена пользователей и пароли, которые хранятся в базе данных. В этих случаях лучше использовать механизмы, предоставляемые производителем базы данных, чем изобретать свой собственный подход. Однако, если при этом требуется гарантировать переносимость приложения между базами данных, то использовать встроенные механизмы защиты иногда попросту невозможно.
Бывает, что приложению необходимо хранить свои собственные мандаты (credential), используемые для аутентификации в других системах. И здесь самый распространенный случай связан с базами данных, когда приложение должно хранить свое собственное регистрационное имя и пароль для базы данных, необходимые ему для того, чтобы установить соединение с сервером.
Другая ситуация связана с приложениями, требующими доступа на запись к серверу каталогов с помощью протокола LDAP, в рамках которого приложение должно аутентифицировать себя для того, чтобы получить доступ. Во всех подобных случаях разработчикам приходится хранить мандаты в некотором защищенном виде, поскольку, как правило, требуется, чтобы одного лишь доступа к соответствующей регистрационной записи операционной системы было недостаточно для обращения к незашифрованным мандатам, принадлежащим приложению.
При использовании механизмов базы данных, предоставляемых провайдером, разработчики должны знать, как работают эти механизмы. Например, при корректном управлении ключами функция AES_ENCRYPT() в MySQL обеспечивает строгую конфиденциальность для данных в строках, но не целостность данных в столбцах. Предположим, что разработчики используют один и тот же ключ для двух строк, тогда администратор базы данных может легко поменять местами зашифрованные данные в строках, что недопустимо. Если администратор не может допустить такого риска, тогда разработчику необходимо сделать нечто большее, чем просто зашифровать информацию с помощью механизмов, которые предоставляет сама база данных.
Стандартные API и алгоритмы шифрования
Если по какой-то причине приложение должно напрямую использовать шифрование, то первое, чего следует избегать, – это разработка своего собственного алгоритма шифрования. Не стоит также задумываться о своей собственной реализации стандартного алгоритма шифрования и следует с большой осмотрительностью соглашаться на реализацию любого старого стандартного алгоритма.
Если вы возьметесь за проектирование собственного алгоритма, он у вас, скорее всего, получится хуже, чем у международного сообщества профессиональных криптографов или ученых. Если вы пытаетесь сами реализовать стандартный алгоритм, то можете сделать это с ошибками, связанными с безопасностью, которые могут потенциально нарушить синхронизацию (timing), или допустить возможность проведения других атак (M. Bond, R. Anderson. API-Level Attacks on Embedded Systems. Computer, Vol. 34, No. 10, 2001). Некоторыми такими ошибками злоумышленники смогут даже воспользоваться по сети, и во многих случаях потребуется время, чтобы их обнаружить и исправить. Правильный подход – использовать некоторые стандартные алгоритмы и хорошо известную и стабильную реализацию.
Самое предпочтительное – это выбрать набор инструментальных средств или библиотеку шифрования, которые лучше всего подходят для вашего языка и среды разработки. К счастью, существуют хорошие наборы инструментальных средств и библиотеки, способные удовлетворить практически любые требования разработчиков. Для большинства таких библиотек есть стандартные API, и их действительно стоит использовать, поскольку они поддерживают применение хорошей практики шифрования и позволяют приложению работать с различными экземплярами этих API. Три самых популярных API: Java Cryptographic Architecture, или Extension, известный также как JCE/JCA (http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html) для Java-разработчиков; Cryptoki (он же PKCS#11; ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-30/pkcs-11v2-30b-d6.pdf) для разработчиков на Си и C++ и ориентированных на свободно распространяемые решения; API для шифрования, предложенный компанией Microsoft (CAPI; http://msdn.microsoft.com/en-us/library/aa380256(VS.85).aspx) для тех, кто работает на платформах Microsoft.
Как только вы установили хорошую криптографическую среду разработки, возникает вопрос, какой алгоритм можно или нельзя отнести к стандартным: в каждый период времени специалисты по шифрованию и вопросам безопасности отдают предпочтение определенному, небольшому набору алгоритмов. Состав этого набора меняется, как правило, относительно медленно, по мере того как растет популярность каких-то новых алгоритмов и с развитием криптоанализа выявляются теоретические возможности организации атак, которые свидетельствуют о том, что предпочитаемые в данное время алгоритмы уже не выполняют свою задачу. Сейчас в состав такого набора могли бы входить AES-128, HMAC-SHA-1, SHA-256 и RSA с 2048-разрядными ключами. Предпочтительный набор меняется со временем и даже, при некоторых обстоятельствах, может зависеть от региона. Для получения представления о наилучшем сейчас наборе можно рекомендовать прочитать свежие RFC. IETF, организация, создающая большинство RFC, обычно обновляет набор алгоритмов, используемых в соответствии с наилучшей практикой шифрования, и список самых популярных реализаций этих алгоритмов. В большинстве случаев рекомендуется следовать по этому пути.
Основное исключение из этого правила возникает в ситуации, когда нужно использовать некий одобренный в конкретной стране алгоритм либо когда на этом настаивает пользователь или требует регулятор. В таких случаях стандартные инструментальные средства и библиотеки шифрования могут включать или не включать в себя соответствующие алгоритмы, но обычно существует их некая локальная поддержка. Однако лучше всего не вынуждать пользователей из других регионов применять такие «национальные» алгоритмы – более широкое сообщество доверяет им меньше, чем тем, что обычно используются в RFC.
Итак, вы решили, какой стандартный API, набор инструментальных средств и алгоритмы использовать, и теперь возникает вопрос, как защитить важные элементы данных. В большинстве случаев разработчикам хотелось бы обеспечить конфиденциальность и защитить целостность, чтобы предотвратить атаки по принципу «вырезать и скопировать». Однако, несмотря на то что обычно ставится именно такая цель, могут возникать вопросы системного уровня. Например, в случае базы данных весьма маловероятно, что кто-нибудь захочет обеспечить защиту целостности всего набора данных, поскольку при этом вставка строк обходилась бы слишком дорого. Более разумный подход – добавлять дополнительную информацию в защищаемые данные, например некоторые другие столбцы базы данных, и таким образом либо снижать вероятность использования атак по принципу «вырезать и скопировать», либо смягчать их последствия.
Анализ рисков
При защите данных с помощью шифрования разработчики должны ясно представить, от чего именно нужно защищаться. Это означает, что им следует создать некую модель угроз, оценить вероятность их появления и предполагаемые последствия. И это, конечно, означает, что им следует провести анализ рисков. Однако такого рода анализ отличается от общего анализа рисков системного уровня, поскольку разработчики в данном случае больше обеспокоены внутренними угрозами и в меньшей степени угрозами из Интернета. Внутренние угрозы иногда бывают менее очевидны или вероятность их возникновения занижается, хотя, говоря по справедливости, трудно с уверенностью это утверждать, поскольку многие организации весьма неохотно сообщают о фактах внутренних вторжений.
Конечно, не стоит забывать о том, что вредоносный код может быть запущен извне на серверном хосте и может случиться так, что какой-то фрагмент кода будет специально интегрирован в серверную инфраструктуру, для того чтобы получить доступ к защищенным данным. Если такой вредоносный код работает с теми же привилегиями, что и код, который на законных основаниях имеет доступ к соответствующим ключам дешифрования, тогда вредоносный код теоретически может получить доступ к важным данным. Это говорит о том, что использование шифрования для защиты данных приложения нельзя отделять от других методов защиты информации. В этом случае более надежная изоляция компонентов (например, с помощью разных регистрационных записей ОС) помогает гарантировать эффективность защиты, которую обеспечивает шифрование.
Вряд ли для большинства систем хакерами будет разработан код, целенаправленно использующий дефекты защиты, но в некоторых случаях, например для распространенных инфраструктур поддержки Web-приложений, такая задача злоумышленникам могла бы показаться интересной. В большинстве случаев атаку со стороны вредоносного кода можно считать маловероятной, в силу чего задаче предотвращения такой угрозы можно присвоить низкий приоритет. С другой стороны, если к важным данным, безопасность которых нужно обеспечить, относятся ключи, используемые для защиты доступа к токенам (так, как это делается в OAuth), тогда, поскольку вы не знаете заранее, как приложения будут использовать эти токены, вы должны рассматривать такие случаи более внимательно.
В любом случае, если анализ рисков показывает, что имеет смысл защитить данные приложения с помощью шифрования, лучше делать это, опираясь на хорошие методики управления ключами.
Управление ключами
Вернемся к примеру с MySQL и функцией AES_ENCRYTPT(). Одно из входных значений – это ключ, который приложение должно использовать в операции шифрования. К сожалению, этот ключ необходимо передавать незашифрованным, в силу чего возникает серьезная проблема – чтобы защитить данные в базе данных, сначала требуется защитить ключ.
То же самое касается некоторых других интерфейсов, предлагаемых популярными библиотеками шифрования. Например, многие библиотеки поддерживают интерфейсы, предназначенные для тех случаев, когда ключ шифрования представляет собой пароль, который человек в состоянии запомнить. Разработчику, не имеющему опыта работы с криптографическими интерфейсами, может показаться, что более естественно использовать такие интерфейсы шифрования на основе пароля (Password-Based Encryption, PBE) для обеспечения безопасности объектов данных в приложении, однако это не так.
Следует заметить, что нет никаких разумных причин использовать ключи, которые человек смог бы запомнить, и намного лучше, если это будут многоразрядные случайные величины. Кроме того, стоит чуть лучше представлять себе, каким образом можно корректно и некорректно использовать интерфейсы PBE, которые на практике обладают двумя серьезными недостатками.
Первый недостаток связан с производительностью. Поскольку предполагается, что эти интерфейсы используются с паролями, которые нельзя назвать абсолютно случайными и, как следствие, их можно подобрать, то хакер, имеющий доступ к защищенному объекту, может написать программу подбора пароля, например выполняя дешифрование и анализируя выходной формат. Для большинства защищаемых объектов будет очевидно, когда этот процесс увенчается успехом. И для того, чтобы затруднить подбор, большинство интерфейсов PBE сначала выполняют некоторые обязательные и затратные по времени операции. Например, пароль, передаваемый на вход, может быть хеширован несколько тысяч раз перед тем, как итоговый результат будет применен в качестве ключа для реальной операции шифрования. Это приводит к тому, что операции PBE занимают гораздо больше времени, чем основная операция шифрования.
Второй недостаток серьезнее. Применение интерфейсов PBE требует, чтобы код приложения имел и применял незашифрованный ключ каждый раз при выполнении операции шифрования/дешифрования. Самый простой подход (и его, наиболее вероятно, выберет занятый разработчик) состоит в том, чтобы в явном виде записать значение ключа в исходный код приложения. Правда, этот ключ сможет увидеть любой, у кого есть доступ к исходному коду, хотя таких людей, скорее всего, будет мало.
Если разработчик проявит чуть больше упорства, он примет решение хранить ключ в конфигурационном файле, однако каждый, кто имеет доступ к этим файлам, теперь имеет доступ и к ключам, а это нежелательно. Во многих случаях конфигурационные файлы также управляются с помощью системы контроля версий, чего не стоило бы допускать для ключей шифрования.
Как поступить в данной ситуации? Хранить ключи в коде – плохо, в конфигурационных файлах – плохо, и никакой другой возможности не существует. Сделать лучше можно единственным способом – использовать аппаратную поддержку. Если бы можно было хранить ключи на аппаратном уровне, а приложение работало бы только со ссылками на ключ, это позволило бы решить наши проблемы. На самом деле это можно сделать достаточно дешево либо вообще бесплатно благодаря спецификации Trusted Platform Module (TPM) группы Trusted Computing Group, которая для стандартного серверного аппаратного обеспечения предлагает возможность хранить ключи шифрования в защищенной от несанкционированного доступа памяти. Такая память фактически имеется на нескольких серийных системных платах, и некоторые (но пока не все) популярные наборы инструментальных средств шифрования поддерживают работу с ней.
Конечно, сейчас аппаратная поддержка для хранения ключей нужна не всем разработчикам, тем не менее они должны писать код, который позволял бы достаточно просто переходить с программных на аппаратные ключи и при этом минимизировать число строк кода. Добиться этого можно, например определив специфические для приложения интерфейсы таким образом, чтобы число компонентов, работающих с незашифрованными ключами, было минимальным – в идеале так, чтобы только конфигурационным утилитам приходилось обрабатывать незашифрованные ключи (или общие ключи, при более трепетном отношении к вопросам безопасности). В итоге обычный код приложения будет работать только со ссылками на ключи, а конфигурационные файлы должны содержать лишь такие ссылки либо ключи, зашифрованные с помощью некоторого скрытого ключа или (там, где это возможно) ключа, хранящегося в TPM. Основная идея всех этих манипуляций заключается в том, чтобы сократить до минимально возможного число строк кода, работающего с незашифрованными ключами.
***
Здесь были описаны ситуации, требующие специфических для приложений механизмов шифрования, и даны рекомендации, как при этом следует действовать. Общая идея достаточно очевидна: нужно разумно оценивать реальный риск. Несмотря на то что предпочтительнее использовать распространенную инфраструктуру защиты, такую как VPN и TLS, в некоторых случаях приложения должны напрямую обращаться к сервисам шифрования, и тогда безопасность, мобильность и даже производительность будут зависеть от того, насколько корректно сделано шифрование.
Стефен Фаррелл (stephen.farrell@cs.tcd.ie) – научный сотрудник Trinity College (Дублин) и директор по технологии NewBay Software.
В России шифрование приравнивается к оружию и поэтому строго контролируется регулятором в лице ФСБ, унаследовавшей области права и обязанности расформированного ФАПСИ. Разработчики, желающие встраивать механизмы криптографии в свои изделия, должны предварительно получать лицензию ФСБ на эту деятельность, причем круг используемых алгоритмов ограничен наработками самой спецслужбы – серию этих алгоритмов принято называть ГОСТ. Данные алгоритмы приняты на уровне рекомендательных стандартов шифрования в стеке IETF в виде так называемых информационных RFC.
Круг российских разработчиков, занимающихся кодированием алгоритмов ГОСТ, невелик – крупных производителей не более десятка. В то же время потребность в защите данных имеется практически в любом приложении, поэтому разработчики пошли путем разделения обязанностей: созданием библиотек шифрования занимаются лицензиаты ФСБ, а встроить эти проверенные спецслужбой библиотеки в свои продукты могут практически любые производители ПО. Однако даже в этом случае не избежать процедуры проверки на корректность встраивания защиты. Иначе говоря, рекомендация Стефена Фаррелла о использовании только проверенных библиотек в России закреплена на законодательном уровне.
Кроме того, в России имеется ряд законов, предписывающих использование шифрования при построении определенных систем защиты, что и обеспечивает развитие рынка продуктов со встроенной криптографией. Например, ФЗ-152 «О персональных данных» требует обязательной криптозащиты распределенной базы данных, и стандартным решением в этом случае является шифрование канала связи между базами и шифрование всей базы. Однако это решение является неэффективным – много времени тратится на шифрование того, что защищать вообще не нужно, – шифрования требуют только столбцы таблицы, содержащие персональные данные. При этом не стоит забывать о приведенной Фарреллом примере атаки – при шифровании столбца одним ключом возникает опасность перестановки данных и нарушения целостности таблицы.
Следует отметить, что не все производители баз данных позволяют встраивать российские алгоритмы шифрования в свои продукты, тем не менее некоторые российские разработчики предлагают подобные системы защиты. Например, компания Aladdin выпустила продукт eToken Secret Field, выполняющий защиту отдельных столбцов СУБД Oracle.
В 2004 году был принят ФЗ-98 «О коммерческой тайне», допускающий с помощью шифрования защищать объекты интеллектуальной собственности, однако эффективно реализовать защиту, например, чертежей и цифровых CAD/CAM-моделей не просто из-за огромного объема файлов, шифрование и дешифрование которых требует большого времени. В то же время для защиты модели совсем необязательно шифровать весь файл – достаточно закодировать наиболее существенную его часть, например каталог объектов модели. Без знания сведений об объектах будет практически невозможно понять структуру модели и расшифровать файл. При этом шифрование одного каталога можно реализовать достаточно эффективно, хотя это и потребует интеграции системы шифрования в системах CAD/CAM. Некоторые российские производители подобных продуктов предлагают такой функционал.
В целом же стоит отметить, что законодательные требования по использованию российских алгоритмов шифрования дают местным разработчиком определенные преимущества перед западными коллегами, которые, как правило, неохотно интегрируют в свои продукты шифрование по российским алгоритмам. С другой стороны, появление российских алгоритмов в виде RFC позволило западным компаниям реализовать российские алгоритмы шифрования, что и сделала, например, компания Check Point.
– Валерий Коржов (oskar@osp.ru) – обозреватель Computerworld Россия (Москва).