Certificate and Public Key Pinning — это техническое руководство для реализации привязки сертификата или публичного ключа сервера к клиенту, как это описано в презентации Securing Wireless Channels в Mobile Space, глава «Virginia» . Эта документация фокусируется на обеспечении ясного, простого и применимого руководства для обеспечении безопасности канала связи во враждебной среде, где действующие лица могут быть враждебно настроены и цена ошибки в доверии к ним весьма высока. Дополнительные материалы статьи включают в себя: выдержки с кодом, пример программы на Android, пример программы на iOS, пример программы на .Net и пример программы OpenSSL.
Пару слов про OWASP
Это перевод статьи международной организации The OWASP (Open Web Application Security Project) — Certificate and Public Key Pinning. Данная организация занимается проектом обеспечения безопасности веб приложений. Сообщество OWASP включает в себя корпорации, образовательные организации и частных лиц со всего мира. Сообщество работает над созданием статей, учебных пособий, документации, инструментов и технологий, находящихся в свободном доступе.
Как они сами заявляют — OWASP не аффилирован ни с одной компанией, занимающейся разработкой технологий, но он поддерживает грамотное использование технологий безопасности. OWASP избегает аффилирования, так как полагает, что свобода от влияния со стороны других организаций может облегчить распространение беспристрастной, полезной и дешевой информации о безопасности приложений.
Если вы не слышали про OWASP, то скоре всего вы просто не сталкивались с разработкой защищенных мобильных приложений, где фигурируют пользовательские данные и их сбережения.
OWASP сформировал набор рекомендаций для построения безопасных REST/SOAP сервисов. Разработали стандарт для проведения проверок уровня безопасности приложений. Практически все разработчики мобильных банков стараются следовать их рекомендациям.
Disclaimer
Данная статья не претендует на полный и дословный перевод. Многие термины в статье не содержат такие же емкие, но в то же время короткие аналоги в русском языке, поэтому некоторые из них остаются в заимствованном виде(без перевода).
Вы можете оставлять свои замечания в комментариях для улучшения перевода.
Используемые термины
- Pinning — привязка сертификата или публичного ключа сервера к клиенту
- end-to-end (security, encription…) — от точки до точки(безопасность, шифрование), на всем протяжении пути, между отправителем и получателем и т.д.
- input (trusted, unstrusted) — источник (доверенный, недоверенный), входные данные, входящие соединения и т.д.
- conference of trust — в данном контексте — «доверенная среда» между клиентом и сервером
- peer —
Пэр Лордов в нижней палате парламентаодноранговый участник соединения - certificate of interest — депозитынй сертификат?
Введение
Безопасные каналы связи — это краеугольный камень для пользователей и сотрудников, который работают удаленно или находятся в пути. Пользователи и разработчики ожидают end-to-end безопасности, когда отправляют и получают данные — особенно это касается важных данных при отправке через каналах связи, защищенные VPNом, SSL или TLS. В то время как организации, которые контролируют DNS и CA(прим. ред.: Certification Authority — удостоверяющие центры, задача которых подтверждать подлинность ключей шифрования с помощью сертификатов) снизили риски до нуля для большинства моделей угроз, пользователи и разработчики, которые находятся под другими DNS и публичным CA в иерархии, подвергаются угрозам, выходящим за рамки обычных моделей. На самом деле, история показала, что если полагаться на внешние сервисы, то можно нарваться на «дыры» и хронические нарушения безопасности в их каналах связи.
Пандемия злоупотребления доверием оказала влияние на пользователей, разработчиков и приложения, которые принимают решения по безопасности на непроверенных источниках(англ. — input). Эта ситуация сродни парадоксу: сущности, такие как DNS и CAs являются доверенными и подразумевается, что они обеспечивают доверенные источники; в то время как их источник не может быть доверенным. Опираясь на недоверенный источник для решений по безопасности это не только плохая карма, но и нарушение большого числа принципов для безопасного написания кода(например, OWASP’s Injection Theory and Data Validation).
Pinning эффективно устраняет «доверенную среду» между клиентом и сервером. Приложению, которое связано с сертификатом или публичным ключом сервера, уже не требуется зависеть от других — например DNS или CAs — когда принимает решения по установке безопасного соединения, опираясь на идентификацию однорангового участника подключения. Для тех, кто знаком с SSH, должны понимать, что связывание публичного ключа — почти идентично опции SSH’s StrictHostKeyChecking. Все это время SSH делала всё правильно и остальной мир только начинает понимать достоинства прямой идентификации хоста или сервиса по его публичному ключу.
Есть и другие, кто активно занимается pinning’ом, например Google и его браузер Chrome. Chrome преуспел в обнаружении компрометации DigiNotar, в результате чего раскрылся подозрительный перехват трафика Иранским правительством и его жителями. Отчет по компрометации может быть найден тут — Is This MITM Attack to Gmail’s SSL?; и мгновенных ответ от Google Security тут An update on attempted man-in-the-middle attacks.
Так в чем проблема то?
Пользователи, разработчики и приложения ожидают end-to-end безопасности на их безопасных каналах связи, но некоторые безопасные каналы, таковыми не являются. В частности, каналы связи построенные на всем известных протоколах, таких как VPN, SSL и TLS могут быть уязвимы к определенному числу атак.
Примеры прошлых неудач отображены на дикуссионной закладке для этой статьи. Эта шпаргалка не пытается каталогизировать неудачи в индустрии, расследовать конструктивные недостатки архитектур, оправдывать отсутствие подотчетности или ответственности у провайдеров, объяснять гонку на дно(англ. — race to the bottom) в сфере услуг и демистифицировать сговор между, например, браузерами и CA. Для дополнительного чтения пожалуйста посетите следующие ссылки PKI is Broken и The Internet is Broken
Нулевой пациент
Исходная проблема состояла в распространении ключей. Небезопасные соединения могут быть преобразованы в безопасные с помощью шифрования. Зашифрованные соединения могут быть преобразованы в проблему идентификации с подписями. Проблема идентификации замыкается на проблеме распространения ключей. Это одинаковые проблемы.
Лекарство
Есть три лекарства для проблемы распространения ключей. Первая — иметь первичное знание о вашем партнере или собеседнике(т.е. сервере или сервисе). Это может быть решено с помощью SneakerNet. К сожалению, SneakerNet не масштабируется и не может быть использовано для решения проблемы распространения ключей.
Второе — довериться другим и это имеет два варианта: (1) web of trust и (2) hierarchy of trust. Web of Trust и Hierarchy of Trust решают проблему распространения ключей в стерильной среде. Однако, Web of Trust и Hierarchy of Trust каждый требует от нас, чтобы мы полагались на других — или находились в «доверенной среде»(англ. confer trust). На практике, доверится другим будет проблематично.
Что такое Pinning?
Pinning — это процесс ассоциации хоста с его ожидаемым X509 сертификатом или публичным ключом. Если сертификат или публичный ключ известен или виден для хоста, то сертификат или публичных ключ ассоциирован или «связан»(англ. — pinned) с хостом. Если более чем один сертификат или публичный ключ принимается. то программа содержит pinset(набор сертификатов, взято от Jon Larimer and Kenny Root Google I/O talk) В этом случае, рекламируемая идентичность должна совпадать с одним из элементов набора pinset.
Хост или сервисный сертификат или публичный ключ могут быть добавлены в приложение на этапе разработке или он может быть добавлен во время первой встречи с сертификатом или публичным ключом. Во первых — добавление на этапе разработки — предпочтительнее предзагрузки сертификата или публичного ключа out of band обычно означает, что атакующий не может заразить связь. Если сертификат или публичный ключ добавлен при первой загрузке, то вы будете использовать key continuity. Key continuity может не сработать, если атакующий имеет привелигированную позицию во время первого подключения.
Pinning является средством достижения знания о раннем существовании взаимоотношения между пользователем и организацией или сервисом, чтобы помочь улучшить решения, связанные с безопасностью. Потому что вы уже имеете информацию на сервере или сервисе, вам не надо полагаться на обобщенный механизм, призванный решить проблему распространения ключа. Вот так, вам не надо включать в ДНС имена/адреса карт или CAs для связывания и статусов. За одним исключением — это отзыв сертификата и это обсуждается ниже в разделе Pinning Gaps.
Так же следует упомянуть, что Pinning это не Stapling. Stapling отправляет и сертификат и информацию о OCSP ответчике в одном запросе, для избежания дополнительных выборок для клиента во время валидации пути.
Когда надо Pin’ить?
Вы всегда должны pin’ить, когда вы хотите быть уверены в удаленном хосте или когда производиться связь во враждебной среде. Т.к. в основном это всегда утвердительные ответы, то возможно вы должны всегда pin’ить.
Идеальный случай для примера: во время двух недель или около того во время приготовления презентации и шпаргалки мы наблюдали три соответствующие и связанные неудачи. Первая была — Nokia/Opera умышленно сломали безопасный канал связи. Вторая была DigiCert выдала сертификат для подписи зловреда; Третья была Bit9’s потеряла ключ от корневого сертификата. Оборудование — это не только враждебная, но еще и токсичная среда.
Когда вводить белые списки?
Если вы работаете на организацию, которая практикует «фильтрацию исходящего трафика», как часть стратегии по предотвращению потери данных, то вы наверняка сталкивались с «перехватывающим прокси«. Я предпочитаю называть это как «хорошие» плохие парни (в противоположность «плохим» плохим парням) т.к. оба ломают end-to-end безопасность соединения и мы не можем сказать им «разойдись». В этом случае не рекомендуется добавлять в исключения перехватывающий прокси т.к. это сводит на нет все цели безопасности. Добавьте публичный ключ перехватывающего прокси к вашему pinset после того, как вас проинструктировали из отдела безопасности.
Заметка: Если вы добавили в белый список сертификат или публичный ключ для другого хоста (например, перехватывающего прокси) вы более не связываете ожидаемые сертификаты и ключи для хоста. Безопасность и целостность канала связи может пострадать и это естественно ломает end-to-end безопасность пользователя и организации.
Для большей информации о перехатывающих прокси, дополнительный риск, который они дают и как они «лажают» читайте Dr. Matthew Green’s How do Interception Proxies fail? и Jeff Jarmoc’s BlackHat talk SSL/TLS Interception Proxies and Transitive Trust.
Как вы pin’ите?
Идея состоит в том, чтобы использовать существующие протоколы и инфраструктуру в более стойкой к угрозам форме. Для переиспользования, программа будет делать эти вещи когда устанавливается безопасное соединение.
Чтобы усилить безопасность канала связи, программа будет обрабатывает OnConnect callback предложенный библиотекой, фреймворком или платформой. В колбеке, программа будет проверять хост путем валидации его сертификата или публичного ключа. Пока связывание сертификатов не произошло в колбеке OnConnect, то чаще всего это наиболее удобно, потому что информация о базовом соединении легко доступна.
Что с чем должно быть связано
Первая вещь, которую надо решить это что с чем должно быть связано. У вас на выбор две вещи: вы можете (1) связать сертификаты; или (2) связать публичные ключи. Если вы выберете публичные ключи, то у вас появиться еще два дополнительных выбора: (а) связать subjectPublicKeyInfo; или (b) связать один из конкретных типов, таких как RSAPublicKey или DSAPublicKey.
Эти три выбора объясняються ниже в деталях. Я бы «дал вам конфетку» пооищрил, за то, что вы связали бы subjectPublicKeyInfo , потому что у него есть публичные параметры(такие как {e,n} для RSA публичного ключа) и контекстная информация, такая как алгоритм и OID. Контекст поможет вам сохранить направление и картинка 1 ниже показывает дополнительно доступную информацию.
Кодировки/форматы
Для целей этой статьи, объекты в x509 — совместимым формате представления (PKCS#1 откладывает X509, оба используют ASN.1) Если у вас есть PEM закодированный объект (например, -----BEGIN CERTIFICATE-----, -----END CERTIFICATE-----), то конверитруйте объект в DER. Преобработвания используя OpenSLL предложены ниже в разеделе (Соглашения о форматах)
Сертификат — это объект, который связывает сущность(такую как человек или организация) к публичному ключу по средствам подписи. Сертификат закодирован в DER и имеет связь с датой или атрибутами такими как Subject (кто идентифицирован или связан), Issuer (кто подписал сертификат), Validity (NotBefore and NotAfter) и Public Key.
Сертификат имеет subjectPublicKeyInfo. SubjectPublicKeyInfo это ключ с дополнительной информацией. Тип ASN.1 включает Algorithm ID, a Version и расширяемый формат для содержания конкретного публичного ключа. Картинка 1 и 2 ниже показывает разные изображения одного RSA ключа, который subjectPublicKeyInfo. Ключ для сайта random.org и используется в примерах программ и в коде ниже.


Конкретный публичный ключ является закодированным публичным ключом. Формат ключа обычно будет указан где-то еще, например PKCS#1 в случае RSA публичных ключей. В этом случае тип RSAPublicKey и параметры {e,n} будут ASN&1 закодированы. Картинка 1 и 2 выше ясно показывает модули (n на строке 28) и экспонент (e на строке 289). Для DSA конкретный тип это DSAPublicKey и ASN.1 зашифрованные параметры будут {p,q,g,y}.
В итоге: (1) сертификат связывает сущность и публичный ключ; (2) сертификат имеет subjectPublicKeyInfo; и (3) subjectPublicKeyInfo уже содержит конкретный публичный ключ. Для тех кто хочет узнать больше и для большего погружения, с точки зрения программиста, могут посмотреть на статью Code Project’a Cryptographic Interoperability: Keys.
Сертификат
Легче всего связать сертификат. Вы можете выбрать сертификат из хранлища для вебсайта, можете написать email ИТшникам вашей компании, чтобы они выдали вам сертфикат, использовать openssl s_client чтобы заполучить сертификат и т.д. Когда ваш сертификат подойдет к концу срока действия, то вы просто обновляете приложение. Подразумевается, что если приложение не имеет багов или изьянов в безопасности, то приложение будет обновляться каждый год или два .
Во время исполнения, вы извлекаете сертификат вебсайта или сервера в callback’e. Вместе с callback’ом вы сравниваете извлеченный сертификат с сертификатом вшитым в программу. Если сравнение неудачное, то отклоняете соединение в методе или функции.
Есть другая сторона связывания сертификата. Если сайт проводит ротацию сертификата на регулярной основе, то ваше приложение придется обновлять с такой же периодичностью. Например, Google меняет сертификаты и вам придеться обновлять приложение примерно раз в месяц.(все зависит от сервисов Google). Даже когда Google проводит ротацию сертификатов, все, лежащие в основе публичные ключи(включая сертификаты), остаются статичными.
Публичный ключ
Связывание публичных ключей более гибкое, но более хитрое дело из-за необходимости дополнительных шагов по извлечению публичного ключа из сертификата. Так же как и с сертификатом, программа проверяет извлеченный публичный ключ, с копией вшитой в приложение.
Есть две негативные стороны при работе со связыванием публичных ключей. Первое, это сложнее работать с ключами(по сравнению с сертификатами) т.к. вам обычно приходиться его извлекать из сертификата. Извлечение меньше всего доставляет неудобство в Java и .NET, но это достаточно неудобно в Cocoa/CocoaTouch и OpenSSL. Второе, ключ статичен и может нарушать основные требования политики ротации.
Хеширование
Пока три выбора выше использовали DER кодировки, но еще допустимо использовать hash информацию (или другие производные). На самом деле исходные сэмплы программ были написаны используя переваренные сертификаты и публичные ключи. Сэмплы были изменены, чтобы разрешить программисту исследовать объекты в с помощью таких инструментов , как dumpasn1 и ASN.1 декодером.
Хэширование так же обеспечивает три дополнительных преимущества. Первое — хеширование позволяет анонимизировать сертификат или публичный ключ. Это может быть важно, если ваше приложение беспокоится об утечках информации во время декомпиляции и реверс инжиниринга.
Второе, усвоенный слепок сертификата часто доступен в качестве нативного API для многих библиотек, так что его удобно использовать.
Наконец, третье, организация может захотеть поддерживать резервный(или на случай бекапа) идентичность в случае, если первая идентичность была скомпрометирована. Хеширование удостоверяется ваши противники не увидят резервный сертификат или публичный ключ, чтобы его использовать. На самом деле Google IETF черновик websec-key-pinning использует данную технику.
Что насчет X509?
PKI{X} и интернет форма пересечение. Что интернет пользователи ожидают и что они получает от CA может различаться очень сильно. Например, интернет пользовать хочет достичь безопасности, пока CA хочет преуспеть в доходах и правовых аспектах. Многие удивлены, когда узнают, что пользовать часто требует применить верификацию идентификацию хоста, даже если CA выпустил сертификат (детали похоронены в CA гарантиях на их сертификаты и их Certification Practice Statement (CPS)).
Есть множество доступных PKI профилей. Для интернета представлет интерес, «Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL)», так же известеный как RFC 5280. С тех пор как сертификат специфицирован в ITU’s X509 стандарте, есть множество обязательных и опциональных полей доступных для проверки для обоих тел. Из-за непересекающихся целей между группами, в следующем разделе содержатся рекомендации.
Обязательные проверки
Все проверки X509 должны включать:
- Проверку пути. Проверка верифицирует все подписи всех сертификатов в цепочке тому, что соответствуют выданному PKI. Проверка начинается на сервере или сервисном сертификате(с листка) и протекает назад к доверенному корневому сертификату(к корню).
- Валидация полей notBefore и notAfter. Поле notAfter в особенности важно с тех пор, как CA не будет поддерживать сертификат после этой даты и не должна обеспечивать CRL/OCSP обновления после этой даты.
- Статус отзыва. Так же как и с полем notAfter, аннулирование важно потому, что CA не будет поддерживать сертификат, если он в списке аннулированных. IETF одобрила метод проверки аннулирования сертификат в OCSP и специфицировала его в RFC 2560.
Дополнительные проверки
[Размышления на тему, что еще представить и как лучше это представить. Имя темы? DNS обзор? Использование ключей? Алгоритмы? Геолокация на основе IP? Возвращайтесь в ближайшее время.] В модели где предшествовали PKIX RFC-5280, X.509v1 было сильная связанность Имени Темы сертификата к X.500 Каталогу(англ. — Directory). С обновлением к X.509v3, Каталог все еще стандарт для аутентификации атрибутов caСертификата, вместо принятия самоподписанного корня. Геолокация так же важна, для примера — фейковый сертификат Гугла был выдан во Флориде, вместо Mountain View CA.
Привязывание сертификата к Каталогу может связать корневой caСертификат и провалидировать сущность у которой могут иметься атрибуты, такие как локация. Это описано в RFC-1255. Дополнительные поля специфицированы, также как альтернативное поле «тема», например RFC-0822 email address или DNS имя может быть расположены в поле DNS, но действительно тяжелое поднятие сделано X.500 Каталогом, который сейчас используется в качестве межсертификатного доверенного трубопровода в Федеральном Мосту между важных смежных объединений по интересам, который не сфокусированы на интернете. Пока эти кросс-сертификаты ценны в валидации между доверенными объединениями, само подписанный корень все еще нуждается быть связанными, курируемый в доверенном хранилище, таком как хранилище веб браузера или представленными федеративному сообществу. Каталог может играть роль в заполнении пробелов в валидации caСертификатов, локально или национально под административным доменом, таким как c=US. Путем развода темы от Каталога, начинают возникать проблемы, в которых привязка сертификатов играет ключевую роль, чтобы удостовериться, что клиент и сервер имеет одинаковые точки соприкосновения.
Проверки публичного ключа
Смотри также (q.v.). Верификация идентичности хоста со знанием об его ожидаемом/связанном публичном ключом.
Примеры связывания
Эта секция демонстрирует связывание сертификатов и публичных ключей в Android Java, iOS, .Net, и OpenSSL. Все программы пытаютсья соединить random.org и запрашиваемые данные(Dr. Mads Haahr участвует в AOSP’s pinning program, то сайт должен иметь статический ключ) Программы наслаждаются ранним взаимоотношениям с сайтом(более точнее априорным знанием), поэтому они включат копию публичного ключа сайта и связывают идентичность и ключ.
Параметр проверки возвращает значение проверки, проверка ошибок была опущена в коде ниже, но присутствует в примерах программ. Поэтому пример кода может быть использован для копи/пасты. Забегая вперед, наиболее дискомфортные языки — это С-шные: iOS и OpenSSL.
HTTP pinning
RFC 7469 представил новый HTTP заголовок, который разрешает SSL серверам объявлять хеши их сертификатов с периодом времени, в котором сертификаты не должны быть изменены. Например:
Public-Key-Pins: max-age=2592000; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="; pin-sha256="LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="; report-uri="http://example.com/pkp-report"
Пожалуйста заметьте, что RFC 7469 является достаточно спорным с тех пор, как он разрешает переопределять для локально установленных властей. Это позволяет противнику или другим личностям, кто успешно произвел атаку фишингом переопределить известный хороший набор (pinset) с неаутентичным или мошеннической информацией. Второе, механизм отчетов — подавлен от сломанных pinset’ов, поэтому соблюдение юзер-агентов будет сложным под прикрытием данного факта. Вот так, отчетность взломанного pinset выкрикивает, что не должна отчитывать.
Android
Этот пример использует концепцию из developer.android.com unknown CA implementation document.
В целом вы можете научит HttpsURLConnection доверять специфичному набору CAs.
public class KeyPinStore { private static KeyPinStore instance = null; private SSLContext sslContext = SSLContext.getInstance("TLS"); public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{ if (instance == null){ instance = new KeyPinStore(); } return instance; } private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{ // Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // randomCA.crt should be in the Assets directory InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("randomCA.crt")); Certificate ca; try { ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally { caInput.close(); } // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager // SSLContext context = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); } public SSLContext getContext(){ return sslContext; } }
Вы можете посмотреть полный пример на Гитхабе Android app pubkey-pin-android.
iOS
SSl библиотека с открытым исходным кодом для iOS и OS X была выпущена в Black Hat 2015, которая общеспечивает легкое API для установки связки сертификатов в прилоежние:https://github.com/datatheorem/TrustKit .
Иначе и когда используете NSURLConnection, iOS pinning выполняется через NSURLConnectionDelegate. Делегат должен реализовать метод connection:canAuthenticateAgainstProtectionSpace: и connection:didReceiveAuthenticationChallenge:. С помощью connection:didReceiveAuthenticationChallenge:, делегат доложен вызвать SecTrustEvaluate чтобы выполнить обычные X509 проверки.
Download: iOS sample program
-(IBAction)fetchButtonTapped:(id)sender { NSString* requestString = @"https://www.random.org/integers/? num=16&min=0&max=255&col=16&base=16&format=plain&rnd=new"; NSURL* requestUrl = [NSURL URLWithString:requestString]; NSURLRequest* request = [NSURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0f]; NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; } -(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace: (NSURLProtectionSpace*)space { return [[space authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge { if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) { do { SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; if(nil == serverTrust) break; /* failed */ OSStatus status = SecTrustEvaluate(serverTrust, NULL); if(!(errSecSuccess == status)) break; /* failed */ SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0); if(nil == serverCertificate) break; /* failed */ CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate); [(id)serverCertificateData autorelease]; if(nil == serverCertificateData) break; /* failed */ const UInt8* const data = CFDataGetBytePtr(serverCertificateData); const CFIndex size = CFDataGetLength(serverCertificateData); NSData* cert1 = [NSData dataWithBytes:data length:(NSUInteger)size]; NSString *file = [[NSBundle mainBundle] pathForResource:@"random-org" ofType:@"der"]; NSData* cert2 = [NSData dataWithContentsOfFile:file]; if(nil == cert1 || nil == cert2) break; /* failed */ const BOOL equal = [cert1 isEqualToData:cert2]; if(!equal) break; /* failed */ // The only good exit point return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust] forAuthenticationChallenge: challenge]; } while(0); // Bad dog return [[challenge sender] cancelAuthenticationChallenge: challenge]; }
Net
.Net pinning может быть достигнуто путем использования ServicePointManager как показано ниже.
Download: .Net sample program.
// Encoded RSAPublicKey private static String PUB_KEY = "30818902818100C4A06B7B52F8D17DC1CCB47362" + "C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E" + "D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2" + "9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE" + "2C3F4CBED9460129C72B0203010001"; public static void Main(string[] args) { ServicePointManager.ServerCertificateValidationCallback = PinPublicKey; WebRequest wr = WebRequest.Create("https://encrypted.google.com/"); wr.GetResponse(); } public static bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (null == certificate) return false; String pk = certificate.GetPublicKeyString(); if (pk.Equals(PUB_KEY)) return true; // Bad dog return false; }
OpenSSL
Pinning может быть достигнуто одним и двух способов в OpenSSL. Первое это пользователь поддерживает verify_callback. Второе это после того, как связь установилась via SSL_get_peer_certificate. Или метод вам разрешит доступ к сертификату peer’а.
Хоть OpenSSL выполняет проверки X509, вы должны разорвать соединение и выключить socket в случае ошибки. В связи с архитектурой, сервер, который не поддерживает сертификат может вернут результат X509_V_OK с NULL сертификатом. Чтобы проверить результат обычной проверки вам надо: (1) вызвать SSL_get_verify_result и сверить код ответа с is X509_V_OK; и (2) вызвать SSL_get_peer_certificate и проверить, что сертификат non-NULL.
Download: OpenSSL sample program.
int pkp_pin_peer_pubkey(SSL* ssl) { if(NULL == ssl) return FALSE; X509* cert = NULL; FILE* fp = NULL; /* Scratch */ int len1 = 0, len2 = 0; unsigned char *buff1 = NULL, *buff2 = NULL; /* Result is returned to caller */ int ret = 0, result = FALSE; do { /* http://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */ cert = SSL_get_peer_certificate(ssl); if(!(cert != NULL)) break; /* failed */ /* Begin Gyrations to get the subjectPublicKeyInfo */ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ /* http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 */ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); if(!(len1 > 0)) break; /* failed */ /* scratch */ unsigned char* temp = NULL; /* http://www.openssl.org/docs/crypto/buffer.html */ buff1 = temp = OPENSSL_malloc(len1); if(!(buff1 != NULL)) break; /* failed */ /* http://www.openssl.org/docs/crypto/d2i_X509.html */ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); /* These checks are verifying we got back the same values as when we sized the buffer. */ /* Its pretty weak since they should always be the same. But it gives us something to test. */ if(!((len1 == len2) && (temp != NULL) && ((temp - buff1) == len1))) break; /* failed */ /* End Gyrations */ /* See the warning above!!! */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fopen.html */ fp = fopen("random-org.der", "rx"); if(NULL ==fp) { fp = fopen("random-org.der", "r"); if(!(NULL != fp)) break; /* failed */ /* Seek to eof to determine the file's size */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_END); if(!(0 == ret)) break; /* failed */ /* Fetch the file's size */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/ftell.html */ long size = ftell(fp); /* Arbitrary size, but should be relatively small (less than 1K or 2K) */ if(!(size != -1 && size > 0 && size < 2048)) break; /* failed */ /* Rewind to beginning to perform the read */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_SET); if(!(0 == ret)) break; /* failed */ /* Re-use buff2 and len2 */ buff2 = NULL; len2 = (int)size; /* http://www.openssl.org/docs/crypto/buffer.html */ buff2 = OPENSSL_malloc(len2); if(!(buff2 != NULL)) break; /* failed */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fread.html */ /* Returns number of elements read, which should be 1 */ ret = (int)fread(buff2, (size_t)len2, 1, fp); if(!(ret == 1)) break; /* failed */ /* Re-use size. MIN and MAX macro below... */ size = len1 < len2 ? len1 : len2; /*************************/ /***** PAYDIRT *****/ /*************************/ if(len1 != (int)size || len2 != (int)size || 0 != memcmp(buff1, buff2, (size_t)size)) break; /* failed */ /* The one good exit point */ result = TRUE; } while(0); if(fp != NULL) fclose(fp); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff2) OPENSSL_free(buff2); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff1) OPENSSL_free(buff1); /* http://www.openssl.org/docs/crypto/X509_new.html */ if(NULL != cert) X509_free(cert); return result; }
Альтернативы pinning’у.
Не все приложения используют split-key криптографию. К счастью, есть протоколы, которые позволяют вам настроить безопасные каналы связи, основываясь на знании паролей и пре-хешированных секретов(лучше чем класть секрет в сеть в базовой схеме аутентификации). Два расположены ниже — SRP и PSK. SRP и PSK имеют 88 шифро наборов назначенных им, так что выбор есть.

SRP
Secure Remote Password (SRP) это Password Authenticated Key Exchange (PAKE) by Thomas Wu основанный на Diffie-Hellman. Протокол стандартизован в RFC 5054 и доступен в OpenSSL библиотеке (среди остальных). В схеме SRP, сервер использует верифаер, который состоит из следующей пары: {salt, hash(password)}. У пользователя есть пароль, а соль получает от сервера. В каждом инстансе, обе стороны выбирают рандомные значения(одноразовые номера) и выполняют протокол, используя g{(salt + password)|verifier} + nonces — это лучше чем традиционный Diffie-Hellman gab.
Базовые схемы Diffie-Hellman являются частью целого набора проблем в основе Discrete Logs (DL), которые являются логарифмами над конечным полем. DL схемы привлекательные, потому что тяжелые (до тех пор, пока P=NP).
PSK
PSK это Pre-Shared Key и специфицированны в RFC 4279 и RFC 4764. Общий секрет используется в качестве pre-master секрета в TLS-PSK для SSL/TLS; или используется для ключа в блочном шифре EAP-PSK. EAP-PSK спроектирован для аутентификации через небезопасные сети, такие как IEEE 802.11.
Разное
Этот раздел покрывает «бумажную работу» и другие различные вещи связанные с pinning’ом.
Эфемерные ключи
Эфемерные ключи, это временные ключи, которые использовались для одного экземпляра выполнения протокола, а затем их выкинули. Эфемерный ключ имеет преимущество в обеспечение профилактики безопасности это означает компромис сайта или сервисный ключ для подписания на длительный период(статичный) не облегчает дешифрование прошлых сообщений, потому что ключ был временный и отбросился(после прекращения сеанса).
Эфемерные ключи не влияют на pinning, потому что он доставляется в другом сообщении — ServerKeyExchange. В дополнение, эфемерные ключ это ключ, а не сертификат, поэтому он не меняет конструкции из цепочки сертификатов. Вот так, certificate of interest будет все еще располагаться в certificates[0].
Pinning пробелы
Есть два пробела для pinning из-за переиспользованию текущей инфраструктуры и протоколов. Первое — явный вызов не отсылается программой к серверу на другом конце, основывается на публичной информации о сервере. Поэтому программа никогда не знает может ли участник на другом конце расшифровать сообщения. Однако недостатком является то, что как правило противник будет получать сообщения, которые он не сможет расшифровать.
Второе — это отзыв сертификата. Клиенты обычно не вовлечены в проверку даты отзыва сертификата, поэтому возможно использовать плохой сертификат или ключ в pinset. Даже если отзыв случился, то доступ к Certificate Revocation Lists (CRLs) и Online Certificate Status Protocol (OCSP) может быть заблокирован во враждебной среде. Приложение может пытаться устранить недостаток, где основным критерием является «актуальность». Вот так, приложение должно обновляться и распространяться немедленно, когда критические параметры безопасности меняются.
No Relationship ^@$!
Если вы не имеете отношение к серверу, то еще не все потеряно. Во первых вы можете связать хост или сертификат сервера или публичный ключ в первом запросе. Если плохой парень не был активен, когда вы устанавливали связь и привязывали сертификат или публичный ключ, то он или она не преуспеет в своем деле в ближайшем будущем.
Второе, плохие сертификаты отслеживаються быстро благодоря таким проектам как Chromium и Certificate Patrol, и инициатив EFF’s SSL Observatory.
В третьих, помощь уже в пути. есть множество фьючерсов, которые помогут в вашем стремлении:
- Public Key Pinning (http://www.ietf.org/id/draft-ietf-websec-key-pinning-09.txt) – расширение HTTP протокола, разрешающая web host operators давать инструкции user agents (UAs) запоминать («pin») the hosts’ cryptographic identities на определенный период времени.
- DNS-based Authentication of Named Entities (DANE) (https://datatracker.ietf.org/doc/rfc6698/) — использует Secure DNS для связывания Certificates с Domain Names для S/MIME, SMTP с TLS, DNSSEC and TLSA записей.
- Sovereign Keys (http://www.eff.org/sovereign-keys) — оперирует путем обеспечения дополнительного и безопасного пути связывания доменных имен с публичными ключаси via DNSSEC. PKI (hierarchical) все еще используется. Semi-centralized with append only logging.
- Convergence (http://convergence.io) – различные [географические] виды на сайты и их данные (certificates и public keys). Web of Trust используется. Semi-centralized.
Пока Sovereign Keys и Convergence все еще требуеют от нас удостовериться во внешнем источнике, участвующие стороны не служат акционерам и не приносят доходы. Их интересы это прозрачность в индустрии и безопасность пользователей.
Есть что еще почитать?
Pinning является хорошо забытой старой вещью, которую подняли, отряхнули и положили в новую упаковку. Пока «pinning» и «pinsets» относительно новые термины, для старых вещей, Jon Larimer и Kenny Root провели некторое время по этой теме на Google I/O 2012 с их беседой на тему Security and Privacy in Android Apps.
Соглашения по формату
В качестве соглашения с читателями, возьмем за пример данную конвертацию между PEM и DER форматами, используя OpenSSL.
# Public key, X509 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl rsa -in rsa-openssl.pem -pubout -outform DER -out rsa-openssl.der
# Private key, PKCS#8 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl pkcs8 -nocrypt -in rsa-openssl.pem -inform PEM -topk8 -outform DER -out rsa-openssl.der
Используемые материалы
- OWASP Injection Theory
- OWASP Data Validation
- OWASP Transport Layer Protection Cheat Sheet
- IETF Public Key Pinning
- IETF RFC 5054 (SRP)
- IETF RFC 4764 (EAP-PSK)
- IETF RFC 1421 (PEM Encoding)
- IETF RFC 5280 (Internet X.509, PKIX)
- IETF RFC 4648 (Base16, Base32, and Base64 Encodings)
- IETF RFC 3279 (PKI, X509 Algorithms and CRL Profiles)
- IETF RFC 4055 (PKI, X509 Additional Algorithms and CRL Profiles)
- IETF RFC 2246 (TLS 1.0)
- IETF RFC 4346 (TLS 1.1)
- IETF RFC 5246 (TLS 1.2)
- IETF RFC 6698, Draft (DANE)
- EFF Sovereign Keys
- Thoughtcrime Labs Convergence
- RSA Laboratories PKCS#1, RSA Encryption Standard
- RSA Laboratories PKCS#6, Extended-Certificate Syntax Standard
- ITU Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)
- TOR Project Detecting Certificate Authority Compromises and Web Browser Collusion
- Code Project Cryptographic Interoperability: Keys
- Google I/O Security and Privacy in Android Apps
- Trevor Perrin Transparency, Trust Agility, Pinning (Recent Developments in Server Authentication)
- Dr. Peter Gutmann’s PKI is Broken
- Dr. Matthew Green’s The Internet is Broken
- Dr. Matthew Green’s How do Interception Proxies fail?
- Presentation: SSL Pinning implementation and bypasses for iOS and Android
Авторы и редакторы
- Jeffrey Walton — jeffrey, owasp.org
- JohnSteven — john, owasp.org
- Jim Manico — jim, owasp.org
- Kevin Wall — kevin, owasp.org
- Ricardo Iramar — ricardo.iramar, owasp.org