Разработка
День, когда мы (почти) потеряли тысячу клиентов
Это один из самых интересных случаев, которые нам пришлось решать в команде Qonto по Android, не только из-за неожиданно низкоуровневого характера первопричины, но и благодаря широкому межфункциональному сотрудничеству.
Все началось с сообщения об ошибке. Такого типа сообщения, которое вы видите в пятницу днем и откладываете до следующего понедельника. В тот день мы не обратили на него особого внимания, а зря.
Пользователи Android 6 и 7 не могли войти в систему или совершить перевод из-за обновления корневого сертификата SSL, выданного Let’s Encrypt. Нам нужно было разблокировать этих пользователей.
Здесь я хочу поделиться нашим опытом преодоления бури, вызванной взмахом крыла этой бабочки.
Что привело к блокировке 1000 клиентов
Будучи платежной организацией, Qonto должна соответствовать таким нормативным требованиям, как пересмотренная Директива о платежных услугах или PSD2. Одно из ключевых требований PSD2 заключается в том, чтобы каждое учреждение внедрило строгую аутентификацию клиента (Strong Customer Authentication, SCA). SCA призвана гарантировать, что лицо, инициирующее конфиденциальное действие, такое как перевод или оплата картой, является легитимным пользователем. Она требует использования как минимум двух из трех независимых факторов аутентификации:
- Что-то, что знает пользователь: это может быть пароль, PIN-код или любой другой фактор, основанный на знаниях.
- То, что есть у пользователя: Это относится к физическому оборудованию, такому как смартфон, токен или карта, которыми владеет пользователь.
- То, чем является пользователь: Это биометрическая аутентификация, например по отпечаткам пальцев или распознаванию лица.
Например, чтобы подтвердить платеж по карте, пользователь должен взять свое устройство (то, что у него есть), подтвердить платеж и ввести свой пароль или отпечаток пальца (то, что он знает или представляет собой).
Тяжелая неделя
Мы рассмотрели сообщение об ошибке. Речь шла о том, что клиентка не смогла удостоверить свой телефон, чтобы пройти SCA. После изучения дела стало ясно, что затронута не только первый вход, но и все действия, связанные с SCA. Сервис, отвечающий за одобрение конфиденциальных действий, таких как оплата картой, предоставленный партнером, не работал у части наших пользователей.
Вы можете представить, с какими проблемами сталкиваются пользователи, когда не могут получить доступ к своему аккаунту:
- Им нужно завершить транзакцию до выходных, но она не может быть обработана.
- Онлайн-платеж, на который полагается их бизнес, не может быть одобрен.
- Их деятельность приостанавливается.
Поскольку в наших системах не было никаких недавних изменений, которые могли бы объяснить проблему, мы прибегли к методу Сократа. Мы задавали себе открытые вопросы и записывали ответы, рисуя дерево возможных причин. В итоге все ветви оказались тупиковыми.
Поскольку с нашей стороны все выглядело не так, мы обратились к партнеру с этой проблемой. Они указали нам на статью, в которой говорилось о смене сертификата, произведенной Let’s Encrypt.
Эта проблема с сертификатом препятствовала всем соединениям между SDK и сервером?
Мы проверили в Kibana, что у затронутых пользователей работают устройства с Android 6 и 7.0. Мы также получили событие CertPathValidatorException
в Crashlytics.
Бинго! Мы нашли первопричину проблемы.
Let’s Encrypt сменила свой корневой сертификат. Новый оказался несовместим с устройствами на базе Android ниже 7.0. SCA-серверы Qonto используют сертификаты, подписанные Let’s Encrypt. Поэтому затронутые устройства Android больше не могли доверять серверу, что приводило к возникновению CertPathValidatorException
при каждом запросе.
Сопряжение устройств не могло быть осуществлено. Не могли выполняться и запросы на выполнение важных действий.
Запуск обратного отсчета
Наша первая идея заключалась в том, чтобы попросить клиента добавить недостающий сертификат вручную. Это было слишком сложно; клиент вряд ли будет устанавливать файл сертификата в настройках безопасности Android.
Единственным приемлемым вариантом решения проблемы был откат изменения сертификата, сделанного нашим партнером. Совместная работа была ключевым фактором, гарантирующим, что все остальные клиенты не пострадают от этого отката, специфичного для Qonto.
Как только наш партнер отменил изменения, мы начали планировать окончательное исправление.
Сообщение от CloudFlare предупредило нас о надвигающейся аналогичной проблеме с нашим собственным API. Это подчеркнуло срочность разработки надежного решения.
Мы оценили несколько вариантов:
- Использование другого корневого сертификата: наша команда SRE отказалась от этой идеи из-за ее потенциального влияния на всю нашу систему. Нам нужно было реализовать исправление на стороне фронтэнда.
- Отказ от поддержки Android 6 и 7: этот вариант был неприемлем, поскольку он слишком сильно повлиял бы на тысячу наших клиентов, которые используют эти версии. Им необходимо было их мобильное устройство для выполнения важных действий.
- Поддержка только Android 7: первоначально рассматривалась, но была отклонена по той же причине, что и предыдущий пункт. Отказ от поддержки Android 6 был невозможен.
- Поддержка Android 6 и 7: мы выбрали этот путь, потому что он позволил нам сохранить поддержку более широкого спектра устройств без существенных сбоев.
Для устройств под управлением Android 7 мы использовали опции конфигурации безопасности, представленные в этой версии. Конфигурация безопасности Android позволяет нам явно указывать доверенные сертификаты. Эта конфигурация также применяется ко всем библиотекам, которые мы интегрируем и используем в приложении. Вот файл network_security_config.xml
, который мы представили:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config> <trust-anchors> <certificates src="@raw/isrgrootx1"/> <certificates src="system"/> </trust-anchors> </base-config> </network-security-config>
Для Android 6 мы использовали библиотеку okhttp-tls от Square, чтобы создать пользовательскую фабрику sslSocketFactory
. Эта фабрика дала нам сокетное соединение, способное доверять сертификату нашего сервера для установления безопасного и зашифрованного соединения.
fun OkHttpClient.Builder.trustISRGRootX1CertificateForApi23(): OkHttpClient.Builder { if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.M) { // Convert the PEM in plain string format to a Certificate object val certificate = convertPemToCertificate() // Load certificate in a builder, being appended to the platform ones. val certificates = HandshakeCertificates.Builder() .addPlatformTrustedCertificates() .addTrustedCertificate( certificate as X509Certificate, ) .build() sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager) } // Return the builder for further configuration. return this }
Как мы убедились, что наши изменения решили проблему
Нам нужна была среда для тестирования сетевой конфигурации. Нам нужна была такая среда, которая не помешала бы нескольким сотням разработчиков, уже использующих приложение, развернутое в staging среде.
Наша идея заключалась в том, чтобы предоставить определенный IP-адрес, ведущий к обратному прокси, нацеленному на обычные конечные точки.
Прокси-сервер будет обслуживать новый корневой сертификат. Наши машины и эмуляторы будут полагаться на файл /etc/hosts, чтобы переопределить конфигурацию сети и указать конкретный IP. Таким образом, весь маршрут соединения использует сертификаты, подписанные новым корневым центром сертификации Let’s Encrypt.
Развертывание исправления
Особенность мобильного мира заключается в том, что у пользователей есть разные версии приложения. Все наши пользователи не обновляют свои приложения в одно и то же время.
Нам нужно было убедиться, что ни у кого на телефоне не установлены ошибочные версии, чтобы можно было применить смену сертификата. Для этого мы закрыли версии приложений, установленные до появления исправления, чтобы избежать блокировки пользователей через новую конфигурацию сети.
Что мы узнали
Это один из самых интересных случаев, которые нам пришлось решать в команде Qonto по Android, не только из-за неожиданно низкоуровневого характера первопричины, но и благодаря широкому межфункциональному сотрудничеству, которое потребовалось для ее решения:
- Разработчики Android должны были обновить приложение.
- Инженеры по надежности должны были настроить тестовую среду и применить изменения в сертификатах.
- Писатели, пишущие о пользовательском опыте, должны были подготовить коммуникационные материалы на пяти языках.
- Менеджеры по работе с клиентами должны были запустить коммуникационные кампании на четырех рынках.
- Менеджеры по работе с клиентами должны были решать проблемы, возникающие у наших клиентов.
- Команда нашего партнера также была вовлечена в работу, решая проблемы на своей стороне.
- Перенос первого расследования с пятницы на следующий понедельник поставил бизнес наших клиентов в сложную ситуацию во время выходных. Мы никогда не забудем, что за простым сообщением об ошибке может скрываться ситуация, требующая быстрого решения.
О компании Qonto
Qonto упрощает ежедневные банковские операции для малого и среднего бизнеса и фрилансеров благодаря онлайн-счету для бизнеса, который сочетается с инструментами для выставления счетов, ведения бухгалтерского учета и управления расходами.
Благодаря инновационному продукту, быстрой круглосуточной поддержке клиентов и понятным ценам Qonto стал лидером на своем рынке с более чем 500,000 клиентов.
Созданная в 2016 году Александром Протом и Стивом Анави, компания Qonto сегодня работает на 8 европейских рынках (Франция, Германия, Италия, Испания, Австрия, Бельгия, Нидерланды и Португалия) и насчитывает более 1,600 сотрудников.
С момента своего создания Qonto привлекла 622 миллиона евро от известных инвесторов.