company_banner

SafetyNet Attestation — описание и реализация проверки на PHP

  • Tutorial

В эту тему пришлось детально погрузиться во время работы над обеспечением стандартных механизмов верификации устройств для разных мобильных платформ. Задача сводилась к разработке полноценной реализацию проверки JWS-токенов по протоколу SafetyNet на серверной стороне.

После многочасовых поисков и скрупулёзного изучения официальной документации Google решил поделиться полученным опытом. Потому что, кроме официальной документации, я нашел только отрывочные описания частных примеров реализации на разных ЯП. И ни намека на комплексное объяснение особенностей проверки по SafetyNet на сервере.

Статья будет полезна разработчикам, которые хотят подробнее разобраться с технологией верификации устройств по протоколу SafetyNet Attestation. Для изучения описательной части не обязательно знать какой-либо язык программирования. Я сознательно убрал примеры кода, чтобы сфокусироваться именно на алгоритмах проверки. Сам пример реализации на PHP сформулирован в виде подключаемой через composer библиотеки и будет описан ниже.

А в конце статьи — ссылка на разработанную мной библиотеку на PHP, которая обеспечивает полный цикл верификации JWS.

Дисклеймер: материал в явном виде содержит перевод официальной документации от Google с разъяснениями и описанием особенностей реализации, с которыми я столкнулся.

О технологии

Технология SafetyNet Attestation разработана Google как средство предоставления разработчикам мобильных приложений информации о надёжности приложения клиента при взаимодействии с сервером, который обслуживает мобильное приложение. Для этого в протоколе взаимодействия предусмотрен удостоверяющий сервис от Google, обеспечивающий верификацию, и представлены рекомендации по проверке ответа от удостоверяющего центра на стороне сервера.

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

Что позволяет проверить технология:

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

  2. Что в процессе взаимодействия клиента и сервера нет больше никого, кроме вашего приложения и сервера.

  3. Что операционная система мобильного устройства не претерпела изменений, критичных для обеспечения безопасного обмена с сервером (не «заручено» — не взломано, а также то, что устройство прошло аттестацию совместимости с Android).

В каких случаях механизм не применим или не имеет смысла:

  1. На устройстве пользователя отсутствует интернет, нет возможности связаться с удостоверяющим центром. В таком случае API выдаст ошибку на клиенте, и мы не сможем получить подписанный токен для проверки на сервере.

  2. Если попытаемся выполнить верификацию подписанного токена в самом мобильном приложении (без участия сервера), так как проверка клиента не должна происходить на клиенте. Если у вас приложение без Backend, или вы в принципе не планируете верификацию SafetyNet на серверной части приложения, то нет смысла устанавливать и настраивать этот механизм проверки.

  3. Если требуется детальное понимание статусов модификации системы, на которой работает мобильное приложение. В протокол заложен механизм однозначного определения модификации устройства. Он состоит из двух переменных: ctsProfileMatch и basicIntegrity. Об их назначении — чуть ниже.

Остальные пункты в рамках этой статьи, на мой взгляд, менее интересны. Общий принцип такой: если вам нужно что-то очень точное или что-то, что обезопасит контент, — ищите другой (дополнительный) способ защиты. Аналогично в случае, когда вы не собираетесь реализовывать проверку в нормальном порядке, как это задумано протоколом, или ваше приложение опирается на созданные уязвимости в конфигурации устройства.

Схематично процесс проверки клиента можно представить в виде схемы:

Рассмотрим поэтапно процесс верификации устройств по протоколу:

  1. Инициация процесса проверки со стороны клиента.Отправка запроса от клиента на Backend на генерацию уникального идентификатора проверки (nonce) сессии. В процессе выполнения запроса на сервере генерируется ключ (nonce) сессии, сохраняется и передаётся на клиент для последующей проверки.

  2. Генерация JWS-токена на стороне удостоверяющего центра.Клиент, получив nonce, отправляет его на удостоверяющий центр вместе со служебной информацией. Затем в качестве ответа клиенту возвращается JWS, содержащий информацию о клиенте, время генерации токена, информацию о приложении (хеши сертификатов, которыми подписывается приложение в процессе публикации в Google Store), информацию о том, чем был подписан ответ (сигнатуру). О JWS, его структуре и прочих подробностях расскажу дальше в статье.

  3. Затем клиент передаёт JWS в неизменном виде на Backend для проверки. А на стороне сервера формируется ответ с информацией о результате прохождения аттестации.После получения статуса проверки JWS на стороне сервера обычно сохраняют факт проверки и, опираясь на него, влияют на функциональность положительным или негативным образом. Например, клиентам, не прошедшим аттестацию, можно отключить возможность писать комментарии, голосовать за рейтинг, совершать иные активности, которые могут повлиять на качество контента приложения или создавать угрозу и неудобство другим пользователям.

Описание процесса верификации на стороне сервера JWS от удостоверяющего центра

Документация Google в рамках тестирования на сервере предлагает организовать online-механизм верификации JWS, при котором с сервера приложения отправляется запрос с JWS на удостоверяющий сервис Google. А в ответе от сервиса Google содержится полный результат проверки JWS.

Но данный метод проверки JWS для промышленного использования не рекомендуются. И даже больше: для каждого приложения существует ограничение в виде 10 000 запросов в сутки (подробнее об ограничениях — здесь), после которых вы выгребите квоту и перестанете получать от него вменяемый ответ. Только информацию об ошибке.

Далее расскажу обо всём алгоритме верификации JWS, в том числе о верификации самих сертификатов (проверке цепочки сертификатов).

Подробнее о JWS

JWS представляет собой три текстовых (base64 зашифрованных) выражения, разделенные точками (header.body.signature):

Например:

eyJhbGciOiJSUzI1NiIsICJ4NWMiOiBbInZlcnlzZWN1cmVwdWJsaWNzZXJ0Y2hhaW4xIiwgInZlcnlzZWN1cmVwdWJsaWNzZXJ0Y2hhaW4yIl19.ewogICJub25jZSI6ICJ2ZXJ5c2VjdXJlbm91bmNlIiwKICAidGltZXN0YW1wTXMiOiAxNTM5ODg4NjUzNTAzLAogICJhcGtQYWNrYWdlTmFtZSI6ICJ2ZXJ5Lmdvb2QuYXBwIiwKICAiYXBrRGlnZXN0U2hhMjU2IjogInh5eHl4eXh5eHl4eXh5eHl5eHl4eXg9IiwKICAiY3RzUHJvZmlsZU1hdGNoIjogdHJ1ZSwKICAiYXBrQ2VydGlmaWNhdGVEaWdlc3RTaGEyNTYiOiBbCiAgICAieHl4eXh5eHl4eXh5eHl4eXh5eD09PT09Lz0iCiAgXSwKICAiYmFzaWNJbnRlZ3JpdHkiOiB0cnVlCn0=.c2lnbmF0dXJl

В данном примере после расшифровки base64 получим:

Header :

json_decode(
	base64_decode(
		“eyJhbGciOiJSUzI1NiIsICJ4NWMiOiBbInZlcnlzZWN1cmVwdWJsaWNzZXJ0Y2hhaW4xIiwgInZlcnlzZWN1cmVwdWJsaWNzZXJ0Y2hhaW4yIl19”
	)
)

=

{
	"alg":"RS256",
	"x5c":[
	"verysecurepublicsertchain1",
	"verysecurepublicsertchain2"
	]
}

Body:

json_decode(
	base64_decode(
		“ewogICJub25jZSI6ICJ2ZXJ5c2VjdXJlbm91bmNlIiwKICAidGltZXN0YW1wTXMiOiAxNTM5ODg4NjUzNTAzLAogICJhcGtQYWNrYWdlTmFtZSI6ICJ2ZXJ5Lmdvb2QuYXBwIiwKICAiYXBrRGlnZXN0U2hhMjU2IjogInh5eHl4eXh5eHl4eXh5eHl5eHl4eXg9IiwKICAiY3RzUHJvZmlsZU1hdGNoIjogdHJ1ZSwKICAiYXBrQ2VydGlmaWNhdGVEaWdlc3RTaGEyNTYiOiBbCiAgICAieHl4eXh5eHl4eXh5eHl4eXh5eD09PT09Lz0iCiAgXSwKICAiYmFzaWNJbnRlZ3JpdHkiOiB0cnVlCn0=”
	)
)

=

{
	"nonce":"verysecurenounce",
	"timestampMs":1539888653503,
	"apkPackageName":"very.good.app",
	"apkDigestSha256":"xyxyxyxyxyxyxyxyyxyxyx=",
	"ctsProfileMatch":true,
	"apkCertificateDigestSha256":[
		"xyxyxyxyxyxyxyxyxyx=====/="
	],
	"basicIntegrity":true
}

Signature

json_decode(
	base64_decode(
		“c2lnbmF0dXJl”
	)
)

= 

“signature”

Остановимся на том, что именно содержится во всем JWS.

Header:

  • alg — алгоритм, которым зашифрованы Header и Body JWS. Нужен для проверки сигнатуры.

  • x5c — публичная часть сертификата (или цепочка сертификатов). Также нужен для проверки сигнатуры.

Body:

  • nonce — произвольная строка полученная с сервера и сохранённая на нём же.

  • timestampMs — время начала аттестации.

  • apkPackageName — название приложения, которое запросило аттестацию.

  • apkDigestSha256 — хеш подписи приложения, которое загружено в Google Play.

  • ctsProfileMatch — флаг, показывающий прошло ли устройство пользователя верификацию в системе безопасности Google (основной и самый жёсткий критерий, по которому можно понять было ли устройство заручено и прошло ли оно сертификацию в Google).

  •  apkCertificateDigestSha256 — хеш сертификата (цепочки сертификатов), которыми подписано приложение в Google Play.

  • basicIntegrity — более мягкий (по сравнению с ctsProfileMatch) критерий целостности установки.

Signature

Бинарная сигнатура, с помощью которой можно сделать заключение, что тело сообщения JWS было подписано с использованием сертификатов (цепочки сертификатов) указанных в Header, и с использованием известного нам приватного ключа. Ключевое — позволяет понять, что в цепочке взаимодействия нет никого, кроме нас и удостоверяющего центра Google.

Проверка сертификатов

Перейдём к непосредственной проверки каждой части полученного JWS. Начнём с сертификатов и алгоритма шифрования:

1. Проверяем, что алгоритм, с помощью которого подписано тело, нами поддерживается:

[$checkMethod, $algorithm] = JWT::$supported_algs[$statement->getHeader()->getAlgorithm()];

if ($checkMethod != 'openssl') {
   throw new CheckSignatureException('Not supported algorithm function');
}

2. Проверяем, что сертификат (цепочка сертификатов), содержащиеся в Header (поле x5c), удовлетворяют нас по содержимому (загружаются в качестве публичных ключей):

private function extractAlgorithm(array $headers): string
{
   if (empty($headers['alg'])) {
       throw new EmptyAlgorithmField('Empty alg field in headers');
   }

   return $headers['alg'];
}

private function extractCertificateChain(array $headers): X509
{
   if (empty($headers['x5c'])) {
       throw new MissingCertificates('Missing certificates');
   }

   $x509 = new X509();
   if ($x509->loadX509(array_shift($headers['x5c'])) === false) {
       throw new CertificateLoadError('Failed to load certificate');
   }

   while ($textCertificate = array_shift($headers['x5c'])) {
       if ($x509->loadCA($textCertificate) === false) {
           throw new CertificateCALoadError('Failed to load certificate');
       }
   }

   if ($x509->loadCA(RootGoogleCertService::rootCertificate()) === false) {
       throw new RootCertificateError('Failed to load Root-CA certificate');
   }

   return $x509;
}

3. Валидируем сигнатуру сертификата (цепочки сертификатов):

private function guardCertificateChain(StatementHeader $header): bool
{
   if (!$header->getCertificateChain()->validateSignature()) {
       throw new CertificateChainError('Certificate chain signature is not valid');
   }

   return true;
}

4. Сверяем hostname подписавшего сервера с сервером аттестации Google (ISSUINGHOSTNAME = 'attest.android.com'):

private function guardAttestHostname(StatementHeader $header): bool
{
   $commonNames = $header->getCertificateChain()->getDNProp('CN');
   $issuingHostname = $commonNames[0] ?? null;

   if ($issuingHostname !== self::ISSUING_HOSTNAME) {
       throw new CertificateHostnameError(
           'Certificate isn\'t issued for the hostname ' . self::ISSUING_HOSTNAME
       );
   }

   return true;
}

Верификация тела JWS

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

1. Проверка nonce.

Тут все просто. Распаковали JWS, получили в Body nonce и сверили с тем, что у нас сохранено на сервере:

private function guardNonce(Nonce $nonce, StatementBody $statementBody): bool
{
   $statementNonce = $statementBody->getNonce();

   if (!$statementNonce->isEqual($nonce)) {
       throw new WrongNonce('Invalid nonce');
   }

   return true;
}

2. Проверяем заручено ли устройство, с которого происходит запрос.

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

Есть два параметра, на основе которых можно принимать решение о надежности устройства: ctsProfileMatch и basicIntegrity. ctsProfileMatch — более строгий критерий, он определяет сертифицировано ли устройство в Google Play и верифицировано ли устройство в сервисе проверки безопасности Google. basicIntegrity — определяет, что устройство не было скомпрометировано.

private function guardDeviceIsNotRooted(StatementBody $statementBody): bool
{
   $ctsProfileMatch = $statementBody->getCtsProfileMatch();
   $basicIntegrity = $statementBody->getBasicIntegrity();

   if (empty($ctsProfileMatch) || !$ctsProfileMatch) {
       throw new ProfileMatchFieldError('Device is rooted');
   }

   if (empty($basicIntegrity) || !$basicIntegrity) {
       throw new BasicIntegrityFieldError('Device can be rooted');
   }

   return true;
}

3. Проверяем время начала прохождения аттестации.

Тоже ничего сложного. Нужно проверить, что с момента ответа от сервера Google прошло немного времени. По сути, нет чётких критериев прохождения теста — с реферальным значением нужно определиться самим.

private function guardTimestamp(StatementBody $statementBody): bool
{
   $timestampDiff = $this->config->getTimeStampDiffInterval();
   $timestampMs = $statementBody->getTimestampMs();

   if (abs(microtime(true) * 1000 - $timestampMs) > $timestampDiff) {
       throw new TimestampFieldError('TimestampMS and the current time is more than ' . $timestampDiff . ' MS');
   }

   return true;
}

4. Проверяем подпись приложения.

Здесь тоже два параметра: apkDigestSha256 и apkCertificateDigestSha256. Но apkDigestSha256 самой Google помечен как нерекомендуемый способ проверки. С марта 2018 года они начали добавлять мета-информацию в приложения — из-за чего ваш хеш подписи приложения может не сходиться с тем, который будет приходить в JWS (подробнее — здесь).

Поэтому единственным способом проверки остается проверка хеша подписи приложения apkCertificateDigestSha256. Фактически этот параметр нужно сравнить с теми sha1 ключа, которым подписываете apk при загрузке в Google Play.

private function guardApkCertificateDigestSha256(StatementBody $statementBody): bool
{
   $apkCertificateDigestSha256 = $this->config->getApkCertificateDigestSha256();
   $testApkCertificateDigestSha256 = $statementBody->getApkCertificateDigestSha256();

   if (empty($testApkCertificateDigestSha256)) {
       throw new ApkDigestShaError('Empty apkCertificateDigestSha256 field');
   }

   $configSha256 = [];
   foreach ($apkCertificateDigestSha256 as $sha256) {
       $configSha256[] = base64_encode(hex2bin($sha256));
   }

   foreach ($testApkCertificateDigestSha256 as $digestSha) {
       if (in_array($digestSha, $configSha256)) {
           return true;
       }
   }

   throw new ApkDigestShaError('apkCertificateDigestSha256 is not valid');
}

5. Проверяем имя приложения, запросившего аттестацию.

Сверяем название приложения в JWS с известным названием нашего приложения.

private function guardApkPackageName(StatementBody $statementBody): bool
{
   $apkPackageName = $this->config->getApkPackageName();
   $testApkPackageName = $statementBody->getApkPackageName();

   if (empty($testApkPackageName)) {
       throw new ApkNameError('Empty apkPackageName field');
   }

   if (!in_array($testApkPackageName, $apkPackageName)) {
       throw new ApkNameError('apkPackageName ' . $testApkPackageName. ' not equal ' . join(", ", $apkPackageName));
   }

   return true;
}

Верификация сигнатуры

Здесь нужно совершить одно действие, которое даст нам понимание того, что Header и Body ответа JWS подписаны сервером авторизации Google. Для этого в исходном виде склеиваем Header c Body (с разделителем в виде ".") и проверяем сигнатуру:

protected function guardSignature(Statement $statement): bool
{
   $jwsHeaders = $statement->getRawHeaders();
   $jwsBody = $statement->getRawBody();

   $signData = $jwsHeaders . '.' . $jwsBody;

   $stringPublicKey = (string)$statement->getHeader()->getCertificateChain()->getPublicKey();

   [$checkMethod, $algorithm] = JWT::$supported_algs[$statement->getHeader()->getAlgorithm()];

   if ($checkMethod != 'openssl') {
       throw new CheckSignatureException('Not supported algorithm function');
   }

   if (openssl_verify($signData, $statement->getSignature(), $stringPublicKey, $algorithm) < 1) {
       throw new CheckSignatureException('Signature is invalid');
   }

   return true;
}

Вместо заключения. Библиотека на PHP

Уже после решения задачи и отдельно от нашей кодовой базы, я разработал библиотеку на PHP, которая обеспечивает полный цикл верификации JWS.

Её можно скачать из Packagist и использовать в своих проектах.

FunCorp
Разработка развлекательных сервисов

Comments 10

    +6
    Большая работа! Полезный результат в виде библиотеки. Хорошо бы добавить открытую лицензию, для безупречности.
      +3
      Спасибо! В версии 0.0.2 свободная MIT лицензия
      +3
      Проверяем, что алгоритм, с помощью которого подписано тело, нами поддерживается:

      И тут мы натыкаемся один из самых неожиданных дефектов JWT: он позволяет использовать откровенно слабые алгоритмы.
      There have been ways to exploit JWT libraries by replacing RS256 with HS256 and using the known public key as the HMAC-SHA256 key, thereby allowing arbitrary token forgery.

      Мне кажется, стоит дополнительно укоротить список допустимых алгоритмов.
        +2
        Да это верно. По сути следует ограничить список допустимых алгоритмов, подсоберу комментарии и сделаю в следующей версии ограничение вместе с остальными правками. Спасибо!
        0
        Не достаточно подробное описание процесса верификации порождает некоторые вопросы:
        Клиент начинает процесс и получает nonce с сервера, и он же (клиент) отправляет nonce гуглу.
        — Сервер с гуглом не общается при этом?

        Клиенту возвращается JWS, с информацией о нем же самом, т.е. о клиенте.
        — Сервер и тут не общается с гуглом? Нужна ли информация клиенту о нем же? И почему время начинается от гугла, а не от сервера?

        Клиент отправляет JWS в неизменном виде на Backend для проверки.
        — На этом этапе Клиент точно не меняет ничего? даже время?

        На стороне сервера: анализ того что прошло через руки клиента? И тут сервер тоже не общается с гуглом? (ну хотя бы проверки ради)
        Вся верификация клиента происходит на основании данных полученных от клиента, или прошедших через клиента, т.е. еще до того как будет сделан вывод по верификации, сервер полагает что эти данные верны и неподдельны, презумпция?

        И что позволяет проверить технология:
        Что именно вы являетесь автором приложения, которое сейчас взаимодействует с сервером.
        — Конечно же я, но только вот кто же я, то ли я это клиент, то ли не клиент?
        Что в процессе взаимодействия клиента и сервера нет больше никого, кроме вашего приложения и сервера.
        — На чем основан такой вывод? из статьи ничего этого не видно.

        Есть два параметра, на основе которых можно принимать решение о надежности устройства: ctsProfileMatch и basicIntegrity.
        ctsProfileMatch — более строгий критерий, он определяет сертифицировано ли устройство в Google Play и верифицировано ли устройство в сервисе проверки безопасности Google.
        basicIntegrity — определяет, что устройство не было скомпрометировано.
        — И тут не все ясно, сервис может подтвердить что когда то устройство было зарегистрировано у них, всего лишь. А было ли оно скомпрометировано или нет откуда гуглу это ведомо?

        Что операционная система мобильного устройства не претерпела изменений, критичных для обеспечения безопасного обмена с сервером (не «заручено» — не взломано, а также то, что устройство прошло аттестацию совместимости с Android).
        — Вообще ничем не подтвержденный предосудительный рекламный вывод, ведущий в обман доверчивого читателя?

        И еще вопрос: сервер самостоятельно без гугла не может провести всю процедуру, рассчитывая и опираясь на себя самого, а не на чей то посторонний авторитет, и доверяя только себе?
        Это предложение гугла, который собственно и заинтересован в сборе информации не обязательно его касающейся, стоит ли идти в капкан?
          +3
          Клиент начинает процесс и получает nonce с сервера, и он же (клиент) отправляет nonce гуглу.
          — Сервер с гуглом не общается при этом?

          На этом шаге действительно не общается. Полученный от сервера nonce отправляется Google
          Клиенту возвращается JWS, с информацией о нем же самом, т.е. о клиенте.
          — Сервер и тут не общается с гуглом? Нужна ли информация клиенту о нем же? И почему время начинается от гугла, а не от сервера?

          Клиенту нет не нужна. Но нужно серверу для того что бы понять что об этом клиенте думает Google.
          Клиент отправляет JWS в неизменном виде на Backend для проверки.
          — На этом этапе Клиент точно не меняет ничего? даже время?

          Клиент ничего не меняет и отправляет JWS в неизмененном виде. Если клиент что то поменяет — ему придется подписать JWS своим каким-нибудь сертификатом — о чем мы сразу узнаем на бекенде и отклоним аттестацию.
          На стороне сервера: анализ того что прошло через руки клиента? И тут сервер тоже не общается с гуглом? (ну хотя бы проверки ради)
          Вся верификация клиента происходит на основании данных полученных от клиента, или прошедших через клиента, т.е. еще до того как будет сделан вывод по верификации, сервер полагает что эти данные верны и неподдельны, презумпция?

          Как я написал выше — если клиент попробует что то поменять ему придется подписать новый JWS своим сертификатом — о чем мы узнаем при проверке цепочки сертификатов и верификации сигнатуры. Это нам гарантирует что данные не претерпели изменения в процессе передачи. И нужно понимать что эта процедура изначально предусмотрена для того что бы сделать заключение о клиенте. Если он верификацию не пройдет — значит не пройдет, мы сделаем соответствующий вывод о приложении которое сейчас взаимодействует с сервером. И да в этом процессе сервер по прежнему никак не общается с Google сервисом, это и не нужно нам вполне достаточно проверить цепочку сертификатов и сигнатуру что бы понять что никто кроме гугла не подписал JWS.

          И что позволяет проверить технология:
          Что именно вы являетесь автором приложения, которое сейчас взаимодействует с сервером.
          — Конечно же я, но только вот кто же я, то ли я это клиент, то ли не клиент?

          Предполагается что «я» — это компания которая контролирует и серверную часть и отвечает за код запущенный на клиенте в том виде в котором он доступен из Google Play.
          Что в процессе взаимодействия клиента и сервера нет больше никого, кроме вашего приложения и сервера.
          — На чем основан такой вывод? из статьи ничего этого не видно.

          На том что в цепочке сертификатов нет ничего лишнего (никто не модифицировал тело JWS) — ни чьих кроме Google сертификатов. И на основании того что валидируется сигнатура с использованием корневого Google сертификата.

          Есть два параметра, на основе которых можно принимать решение о надежности устройства: ctsProfileMatch и basicIntegrity.
          ctsProfileMatch — более строгий критерий, он определяет сертифицировано ли устройство в Google Play и верифицировано ли устройство в сервисе проверки безопасности Google.
          basicIntegrity — определяет, что устройство не было скомпрометировано.
          — И тут не все ясно, сервис может подтвердить что когда то устройство было зарегистрировано у них, всего лишь. А было ли оно скомпрометировано или нет откуда гуглу это ведомо?

          Что операционная система мобильного устройства не претерпела изменений, критичных для обеспечения безопасного обмена с сервером (не «заручено» — не взломано, а также то, что устройство прошло аттестацию совместимости с Android).
          — Вообще ничем не подтвержденный предосудительный рекламный вывод, ведущий в обман доверчивого читателя?

          Тут к сожалению мне сказать нечего — это алгоритмы Google. При реализации верификации я опирался исключительно на документацию. По сути сам Google говорит что данный способ не может быть самостоятельным и полностью опираться на него при принятии решения о надежности клиента не стоит. Вы (Мы) всегда в праве придумать собственным механизм оценки клиента, кто ж мешает.
          И еще вопрос: сервер самостоятельно без гугла не может провести всю процедуру, рассчитывая и опираясь на себя самого, а не на чей то посторонний авторитет, и доверяя только себе?
          Это предложение гугла, который собственно и заинтересован в сборе информации не обязательно его касающейся, стоит ли идти в капкан?

          Ну частично ответил выше. А про капкан — это очень интересный вопрос) Этот вопрос стоит задать самому себе, каждому из нас, стоя в очереди в кассу за новым телефоном/планшетом/чайником.
          Может и в принципе не стоит использовать этот алгоритм — придумывайте свой. Другой вопрос в стоимости решения.
          0
          Убрал комментарий под ответ предыдущему собеседнику
            +2
            Остается только поблагодарить Вас и за статью, и за ответ. И привести свое восприятие темы:
            Даже MITM атака для такой схемы не нужна, поскольку человеком посередине является сам клиент.
            Парадокс? гугл получает от клиента запрос от том насколько он клиент «хороший»,
            смотрит (проверяет) в своих «черных списках» если ли что на клиента, и отправляет опять же клиенту! свою резолюцию,
            и он (клиент) якобы должен (обязан) ничего не меняя передать сей вердикт (справку) на сервер для выводов по своей пригодности.
            Желая иметь оперативные данные о своем клиенте сервер при любом к нему обращении гоняет клиента в гугл, а тот знай пишет в свои БД, время обращения, ip, город, и все остальное.

            «API безопасного просмотра позволяет вашему приложению проверять, помечен ли URL, используемый в приложении, Google как угроза.»
            Во первых о какой угрозе идет речь, и во вторых для этого вовсе не обязательно серверу каждый раз использовать ресурсы клиента для подобной проверки, серверу достаточно самому напрямую без ухищрений спросить об этом у всезнающего сервиса.

            Об Аттестации устройства:
            «люди, использующие модифицированную версию Android, не смогут использовать приложения, реализующие проверку.»
            Очень уж кратко, и наверно по этому поводу Вы верно написали: «И ни намека на комплексное объяснение особенностей проверки по SafetyNet на сервере».
            Если в данном контексте речь идет о бесплатных копиях программ (пиратских) то это ли забота сервера, или все же это забота гугла, который пытается этим предложением распространить свое влияние на клиента бойкотированием через сервер ему вовсе не принадлежащий, но изъявивший желание сотрудничать против клиента?
            Что сделал клиент серверу? всего лишь спросил какой то контент, и уже виноват?
            Клиент отправляет на сервер нормальный, не хакерский запрос, и в ответ ему рычание по науськиванию управляющего сервиса?

            Еще раз о том что позволяет проверить технология:
            «в процессе взаимодействия клиента и сервера нет больше никого, кроме вашего приложения и сервера», а об этом ничего у developer.android.com/training/safetynet/attestation.

            «Но нужно серверу для того что бы понять что об этом клиенте думает Google.» — и это тоже цель гугла, чтобы все спрашивали у него его мнение! (зависимость)
            А если по этой теме еще и дополнять (популяризировать) алгоритмы Google — вопрос. Полезно ли это? Клиенту?

            Придумывайте свой алгоритм — абсолютно верно. И не с точки зрения «стоимости», а потому что это будет собственный алгоритм, работающий для вас и клиента, не на кого то постороннего.
              +3
              Спасибо за развернутую дискуссию. По сути просто несколько добавлений:
              Во первых о какой угрозе идет речь, и во вторых для этого вовсе не обязательно серверу каждый раз использовать ресурсы клиента для подобной проверки, серверу достаточно самому напрямую без ухищрений спросить об этом у всезнающего сервиса.

              Проверку вполне достаточно провести один раз при инициации процесса обмена. Это позволит отделить дешевого бота от разумного белкового пользователя. Ключевое слово «дешевого».
              Про «спросить об этом у всезнающего сервиса» — да у Google есть интерфейс валидации на их стороне. Но этот интерфейс имеет RateLimit в 10 000 запросов в сутки и как написано в статье — для более-менее нагруженного сервиса — это очень мало, из-за чего приходится реализовывать полноценную проверку собственными силами (опять же по рекомендации Google)
                0
                от разумного белкового пользователя )))) слышу )))))))

            Only users with full accounts can post comments. Log in, please.