Одна из санкций, которая досталась России, — запрет на выдачу и продление SSL-сертификатов. Это приводит к тому, что у некоторых компаний сертификат может протухнуть и сайты перестанут открываться.

Основных решений два:

  1. Использовать российский Яндекс.Браузер или Атом.

  2. Поставить на компьютер сертификат или профиль от Минцифры.

Для мобильных приложений это превращается в особую проблему — могут перестать проходить платежи разных эквайрингов.

Например, 15 февраля 2023 года у Сбера истечёт действие сертификата и надо переходить на самоподписанный. Если этого не сделать, то эквайринг через Сбер может перестать работать. SberPay будет работать как и раньше.

В статье покажу, что делать разработчикам приложений, чтобы экраны c 3-D Secure открывались и эквайринг продолжал работу.

Info.plist

На уровне конфигурации проекта ничего менять не нужно. Скорее всего, у вас уже стоит флаг NSAllowsArbitraryLoadsInWebContent в Info.plist: он нужен, чтобы вообще уметь хоть что-то грузить в WKWebView.

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoadsInWebContent</key>
  <true/>
</dict>

На ревью Apple спросит, зачем это вам — расскажите про 3-D Secure.

Подставляем правильный сертификат

Сертификаты берём отсюда https://www.gosuslugi.ru/crt. Нам понадобится сертификат для macOS в .pem формате.

Увы, это страница для пользователей, а не для разработчиков, поэтому сертификаты придётся привести в другой вид. Важно, что сертификатов два: Russian Trusted Root CA и Russian Trusted Sub CA. Нужны оба.

Чтобы получить их, можно взять .pem файл, разделить его на два и сконвертировать каждый в .der, потому что iOS только с .der умеет работать.

openssl x509 -outform der -in certificate1.pem -out certificate1.der

Если вы почему-то доверяете мне, а не Минцифре, то можете сразу взять готовые:

Russian Trusted Sub CA.der

Russian Trusted Root CA.der

Добавляйте их в проект, линкуйте так, чтобы они попал в бандл.

Дополнительная проверка сертификата

Если экран не проходит стандартную валидацию сертификатом, зашитым в операционную систему, то в делегате WKWebView вызывается дополнительная проверка на проверку подключения. По документации мы должны спросить у пользователя, стоит ли открывать эту страницу или как-то иначе проверить безопасность подключения. Будем разрешать подключение после проверки сертификата от Минцифры.

Поймаем провалившуюся проверку. В челлендже есть serverTrust-объект, по которому мы должны принять решение. Если валидация не прошла, то возвращаемся к дефолтному поведению через .performDefaultHandling

extension ViewController: WKNavigationDelegate {
    func webView(
        _ webView: WKWebView,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, 
                                      URLCredential?) -> Void
    ) {
        guard let serverTrust = challenge.protectionSpace.serverTrust 
        else { return completionHandler(.performDefaultHandling, nil) }
        
        Task.detached(priority: .userInitiated) {
            if await self.validator.checkValidity(of: serverTrust) {
                // Allow our sertificate
                let cred = URLCredential(trust: serverTrust)
                completionHandler(.useCredential, cred)
            } else {
                // Default check for another connections
                completionHandler(.performDefaultHandling, nil)
            }
        }
    }
}

Всю работу с сертификатами спрячем в валидатор. Вся работа должна быть в отдельном потоке, поэтому весь код обёрнут в актор для синхронизации значений внутри него. В примере много специфичного кода из фреймворка Security, но его документация все объясняет очень подробно.

actor CertificateValidator {
    var certificates = [SecCertificate]()
   
    func prepareCertificates(_ names: [String]) {
        certificates = names.compactMap(certificate(name:))
    }

    private func certificate(name: String) -> SecCertificate? {
        let path = Bundle.main.url(forResource: name, withExtension: "der")
        let certData = try! Data(contentsOf: path!)
        
        let certificate = SecCertificateCreateWithData(nil, certData as CFData)
        return certificate
    }
  
    func checkValidity(of serverTrust: SecTrust, anchorCertificatesOnly: Bool = false) -> Bool {
        SecTrustSetAnchorCertificates(serverTrust, certificates as CFArray)
        SecTrustSetAnchorCertificatesOnly(serverTrust, anchorCertificatesOnly)

        var error: CFError?
        let isTrusted = SecTrustEvaluateWithError(serverTrust, &error)
        
        return isTrusted
    }
}

Ну и вызовем загрузку сертификатов во viewDidLoad:

class ViewController: UIViewController {

    let validator = CertificateValidator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        Task {
            let names = ["Russian Trusted Root CA",
                         "Russian Trusted Sub CA"]
            await validator.prepareCertificates(names)
        }

        
        let url = URL(string: "https://3dsecmt.sberbank.ru/payment/se/keys.do")!
        webView.navigationDelegate = self  
        webView.load(URLRequest(url: url))
    }

    @IBOutlet weak var webView: WKWebView!
}

Полный пример вы можете посмотреть в GitHub. Запускайте проект, сайт должен открыться.

В статье я показал самый простой пример, чтобы была понятна суть, но в руководстве от Сбера вы можете найти полный текст с дополнительными проверками.g

Вопросы

Как это сделать на Andriod?

Посмотрите инструкцию от Сбера

Будут ли проблемы у других банков?

Рано или поздно. Решение прогоняет все запросы через сертификат Минцифры, поэтому должно быть массовым решением.

Что будет с эквайрингами в других странах?

Если мы не прошли проверку по сертификату Минцифры, то передаём контроль поведению по умолчанию, что позволит грузиться и иностранным эквайрингам.

Как это решение влияет на PCI DSS?

Никак не должно влиять, потому что NSAllowsArbitraryLoadsInWebContent и так стоял, а других решений для России нет пока.

Когда надо обновлять сертификат?

Через 5 лет истекает самый короткоживущий. Заведите себе напоминание обновить за год, тогда большинство пользователей бесшовно обновится.

Сертификат Минцифры безопасен?

По сути, вы передаёте возможность читать ваш трафик государству. Подходит вам это или нет — решайте сами, у всех разные ситуации. Можно отдать это на откуп пользователям, чтобы они сами поставили нужный профиль, но большинство из них просто не поймут проблему и способ решения, так как столкнутся с этим впервые.

Вижу в логах текст [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.

Вся работа с фреймворком Security должна быть в фоновом потоке, но видимо где-то в WKWebView это нарушено.

У меня эквайринг подключен через SDK и я не могу поменять код, что делать?

Скорее всего последняя версия SDK уже содержит это исправление. Уточните у своего экваера.

Надеюсь, статья поможет вовремя выпустить обновления для приложений и не потерять в деньгах. Если у вас есть вопросы или вы можете дополнить тему — приходите в комментарии.


Если понравилась статья и хочешь больше узнать о разработке приложений Dodo Brands, подписывайся на наш канал Dodo Mobile.

Комментарии (19)


  1. ky0
    23.12.2022 19:47
    +9

    Конкурс эвфемизмов продолжается. В этот раз участник — фраза «санкции, которые достались России». В лотерею выиграли, не иначе.


    1. akaDuality Автор
      23.12.2022 20:04
      +7

      Ачивмент анлокд: докопаться до пятого слова в статье.


      1. ky0
        23.12.2022 20:37

        Да бросьте — раз в пару недель публикуют статьи, где какая-то хрень, вплоть до орфографических ошибок, не в преамбуле, а прямо в заголовке — так что вы не рекордсмен. И да — я стараюсь тыкать в такое носом, потому что желания читать до конца с такого старта сильно убавляется, какие бы сверкающие горы технической крутоты не сулила тема.


        1. d_egaluev
          24.12.2022 00:01
          +3

          желаниЕ*


          1. ky0
            24.12.2022 01:18
            -3

            Да, вы правы, мой косяк. Хорошо, что это обычный комментарий, а не статья — тут всего лишь половина позора джунглей. С другой стороны, стыдливо прикрывать фиговым листиком текущую внешнеполитическую ситуацию — это похуже любых деепричастных оборотов. Потому что правила языка запомнить можно, а вот перестать быть лицемером — под вопросом.


            1. akaDuality Автор
              24.12.2022 07:06
              +3

              Хорошо что вы — белый рыцарь, который не переходит на личности.


              1. ky0
                24.12.2022 10:03

                На личности действительно переходить не стоит, более того — данный случай не вполне однозначный и фраза, к которой я прицепился, вполне может быть просто не совсем удачным оборотом. Если мне действительно всё это на ночь глядя показалось — и вы не из тех, кто в фоновом режиме одобряет происходящее, считая, что РФ в нём пострадавшая сторона — с удовольствием извинюсь.


      1. nick-for-habr
        24.12.2022 00:11
        +5

        Крепитесь: не преклонить колено не высказаться презрительно-одобрительно о "санкциях" (или любое слово из стоп-листа) - недопустимо на ресурсе, поощряющем свободный обмен мнениями интеллектуальных профессионалов. Тут же прибежит дежурный дневальный активист-интеллектуал с целью потыкать носом во что-то дурнопахнущее. Если повезёт - то чужим носом, но и альтенатива активистов не смущает. Не найдёт дурнопахнущего - не беда, у него с собой всегда есть термосок: нальёт от души.

        По тексту - всё кратко и по существу, без лишних соплей: вот задача, а вот - решение. Рациональный подход.


  1. SavenkoOleg
    24.12.2022 06:59
    +2

    Использование SSL от МинЦифры даёт государству доступ к данным... А те сертификаты, которые использовались до санкций они каким компаниям и вероятно государствам предоставляли доступ к данным? Я к тому, что все как-то опасаются, что их данные попадут в руки государства в России, но совершенно не беспокоятся, что они попадут в руки других государств. А ведь эти самые другие государства тоже пользуются такими данными. Смотрят, например, что какая-то компания в России делает то, что им не нравится и бум! санкции! А ведь вводить санкции не имея такой информации гораздо сложнее. В общем, не знаю, как-то наивно на мой взгляд так безоговорочно доверять «другим государствам» особенно когда эти государства ищут любые возможности сделать больно


    1. akaDuality Автор
      24.12.2022 07:01

      Все так. Но раньше это была третья сторона которой скорее всего не интересно что там происходит, а кому нужно было больше безопасности выпускали свои сертификаты и делали ssl-пининг


    1. Fynjy007
      24.12.2022 13:06

      При выдаче сертификата, на подпись отправляется публичный ключ в виде запроса сертификата. Приватный ключ не разглашается. Товарищ майор может прочитать вашу переписку, только в том случае, если вы сами передадите ему приватный ключ. Подробности можно найти в википедии в статье Certificate signing request.


      1. akaDuality Автор
        24.12.2022 14:01

        Но в данном случае мы берем сертификат выданный государством, а значит приватный ключ у него.


        1. Fynjy007
          24.12.2022 14:15

          У государства, а точнее у Минцифры, есть приватный ключ парный к сертификату с публичным ключом, который вы скачиваете с указанного в статье сайта. Этот приватный ключ используется для создания подписи на запросе с вашим публичным ключом для создания сертификата с вашим публичным ключом. На этом всё. В шифровании трафика между вами и вашим клиентом участвуют: ваш сертификат с вашим публичным ключом, подписанный минцифры, ваш приватный ключ и алгоритм установления сеансовых ключей.

          UPD. Чисто теоретически, можно использовать атаку человек по середине для подмены вашего сертификата с вашим публичным ключом. Но это долго, дорого, может вызвать скандалы и может конфликтовать с существующей инфраструктурой, на которую раздача ключей минцифры ориентирована. У нас, ЕМНИП, никак хранение трафика запустить не могут, а тут такие глобальные вещи.


          1. DerRotBaron
            24.12.2022 15:24
            +1

            Чисто теоретически, можно использовать атаку человек по середине для подмены вашего сертификата с вашим публичным ключом.

            Даже несколько хуже. В статье предполагается, что госсертификат вообще выписан на третью сторону, к которой надо обращаться (Сбербанк). Тут даже Certificate Pinning применять скорее всего некорректно.

            Но это долго, дорого, может вызвать скандалы и может конфликтовать с существующей инфраструктурой, на которую раздача ключей минцифры ориентирована.

            Непроверяемое утверждение, как впрочем и с любыми другими CA. Но дело в том, что для коммерческих CA такое хотя бы в теории должно привести к потере доверия и быстрому удалению отовсюду, а государство может одновременно:

            • быть звведомо недоверенным для одной части общества

            • быть заведомо доверенным для другой части общества, и этой части можно легко рассказать что это был "фейк"

            • быть политически безальтернативным поставщиком для госкорпораций и госсервисов и заставить их принять такую.угрозу.

            Не буду утверждать, что CA минцифры уже используется для атак или даже когда-либо будет, но это должно приниматься во внимание как фактор риска


            1. Fynjy007
              24.12.2022 16:46

              Даже несколько хуже. В статье предполагается, что госсертификат вообще выписан на третью сторону, к которой надо обращаться (Сбербанк). Тут даже Certificate Pinning применять скорее всего некорректно.

              Подозреваю что тут недопонимание и тут просто идёт достройка цепочки доверия на случай, если у клиента не установлены сертификаты минцифры. Это легко проверить: в статье предоставлен код, со ссылкой на https://3dsecmt.sberbank.ru/payment/se/keys.do переходим по ссылке и смотрим цепочку сертфикатов и смотрим кто, кому, что. В браузере должен быть установлен сертификат от минцифры.

              Непроверяемое утверждение ... приниматься во внимание как фактор риска

              Да утверждение непроверяемое, к счастью в нашем мире техническая возможность может потребовать кучу бабла, согласований, оборудования. А учитывая обстановку в России, любая попытка двигаться в эту сторону вызовет кучу воя. Тут и глобальная прокся, и прехваты запросов, и массовое шифрование/дешифрование/хранение трафика. Это просто не может пройти незамеченным. Плюс остаётся возможность скандала, если, например, гугл внезапно станет использовать сертификат от минцифры. Опять вой, скандал и прочее в том-же духе. Я, лично, оцениваю этот вариант потенциально возможным, но маловероятным.


          1. thevlad
            24.12.2022 15:31

            Какой "ваш приватный ключ"? Сертификат нужен чтобы получить аутентичный публичный ключ "другой стороны", а дальше классическое согласование ключей через DH и шифрование потока данных симметричной криптой посредством согласованного сессионного ключа.


            1. Fynjy007
              24.12.2022 16:32

              Смотрите алгоритм получения сертификата. Для этого нужно сгенерировать приватный и публичный ключ. Из публичного делается запрос сертификата, из которого в свою очередь сертификат с публичным ключом. Потом смотрим DH алгоритм и обнаруживаем, что там требуется, кроме публичного ключа ещё и приватный. Вот этот самый приватный ключ, который был сгенерён в самом начале.


              1. thevlad
                24.12.2022 16:40

                Это верно для стороны создавшей сертификат, но я не очень понял как это соотносится с темой статьи.


                1. Fynjy007
                  24.12.2022 16:48
                  +1

                  Мне просто надоели стоны на тему минцифры ой как страшно, если я отнял у вас слишком много времени на свои, возможно бесполезные комментарии, то извиняюсь.