Как обнаружить click injection в performance-маркетинге? Обнаружение атак типа «click injection» требует анализа временных меток установки приложений на Android с использованием API Google Play Install Referrer. Необходимо выявлять случаи, когда зафиксированное время клика по рекламному объявлению наступает после начала загрузки пакета из Google Play или попадает в аномально короткий интервал по сравнению с базовыми показателями приложения и канала.
Click injection — это сложная форма мобильного рекламного фрода, характерная для устройств на базе Android, при которой вредоносные приложения отслеживают события установки в ОС, чтобы генерировать синтетические клики по рекламе во время загрузки целевого приложения. Используя задержку между началом скачивания и первым запуском, такой фрод перехватывает атрибуцию последнего клика у легитимных маркетинговых каналов или органического трафика.
| Термин | Определение | Связанные понятия | Тип поискового интента |
|---|---|---|---|
| Рекламный фрод | Обманная генерация невалидных кликов или синтетических конверсий для расходования рекламного бюджета. | Атрибуция | Информационный / Коммерческий |
| Click Injection | Специфический для Android метод фрода: генерация синтетических кликов во время установки пакета. | Google Play Install Referrer | Технический / Информационный |
| Google Play Install Referrer | API платформы, предоставляющее метаданные реферера и тайминги начала установки/клика; низкоуровневые контракты AIDL также содержат серверные временные параметры. | Performance-маркетинг | Информационный |
Почему click injection сложно обнаружить в атрибуции Android
Незаметная кража атрибуции: почему показатели конверсии кажутся нормальными
В цифровом performance-маркетинге фродовый трафик обычно выдает себя через ухудшение метрик вовлеченности после установки. Способы фальсификации конверсий, такие как фермы устройств, эмуляторы или подмена данных SDK, часто приводят к нетипичному поведению пользователей, если только активность после установки также не была сфабрикована. В неуправляемых средах фейковые пользователи не создают показов рекламы, не проходят этапы онбординга и не становятся платящими клиентами.
Click injection работает иначе. В схеме с перехватом кликов пользователь, скачивающий приложение — это реальный человек с выраженным намерением. Он самостоятельно нашел приложение, начал загрузку в Google Play и прошел стандартный онбординг. Поскольку пользователь подлинный, пост-инсталл аналитика выглядит естественно: показатели удержания (retention) с 1 по 30 день, частота сессий и паттерны покупок внутри приложения соответствуют норме.
Это делает click injection скрытым вектором атаки. Фрод не искажает пользовательский опыт и не ломает продуктовую аналитику; он лишь крадет атрибуцию. Рекламодатели продолжают платить комиссию за установку (CPI) или действие (CPA) мошенническим рекламным сетям, полагая, что эти площадки принесли качественных пользователей.
Экономические последствия: трата бюджета на органические установки
Основной целью для click injection является органический трафик. Когда органический пользователь ищет приложение в Google Play и нажимает «Установить», он привлекается без рекламных затрат. Генерируя синтетический клик во время загрузки, мошеннические сети крадут атрибуцию у этого органического инсталла.
Финансовые последствия проявляются в двух аспектах:
- Прямая потеря бюджета: Маркетинговые бюджеты расходуются на оплату выплат за естественные установки, которые не требовали продвижения.
- Искусственное занижение органических метрик: Поскольку органические конверсии классифицируются как платные, маркетинговые команды недооценивают реальный базовый объем органической узнаваемости и бренда.
Со временем это искажает оценку каналов продвижения, заставляя команды перераспределять бюджеты в пользу фродовых паблишеров, сокращая инвестиции в развитие бренда.
Почему стандартное отслеживание postback не видит перехват кликов
Стандартные S2S (сервер-сервер) пайплайны работают на модели атрибуции «последнего клика» (last-touch). При первом запуске приложения система атрибуции проверяет базу данных на наличие самого последнего клика в рамках заданного окна атрибуции, связанного с рекламным ID пользователя.
Если рекламная сеть сгенерировала синтетический клик за мгновение до открытия приложения, этот клик занимает финальную позицию в логе атрибуции. Логика postback, опирающаяся только на временную метку клика, не может самостоятельно определить, произошел ли этот клик до того, как пользователь зашел в магазин, или во время загрузки пакета на устройство.
Предотвращение click injection требует устранения этого «слепого пятна» путем получения таймингов на уровне ОС напрямую из инфраструктуры Google Play.
Разработчикам, ищущим легкие клиентские решения и SDK для атрибуции, стоит ознакомиться с пакетом мобильной аналитики SDK.
Как click injection использует события Android для перехвата конверсий
Анатомия атаки: вредоносные утилиты и фоновое наблюдение
Click injection полагается на вредоносные приложения, уже установленные на устройстве пользователя. Обычно это утилиты, замаскированные под фонарики, сканеры QR-кодов, очистители системы или казуальные игры, распространяемые через сторонние маркетплейсы.
После установки приложение запрашивает права на работу в фоновом режиме. Исторически такие приложения использовали события установки пакетов, чтобы отслеживать начало загрузки целевого софта. Несмотря на то, что новые версии Android ограничивают фоновую активность, вредоносное ПО продолжает искать доступные векторы для отслеживания установки новых пакетов.
[Вредоносная утилита в фоне]
│
├─► Шаг 1: Обнаруживает сигнал о начале установки
├─► Шаг 2: Получает имя пакета (например, com.example.app)
├─► Шаг 3: Запрашивает у бэкенда фрод-сети трекинг-ссылку
└─► Шаг 4: Программно генерирует синтетический клик через headless-запрос
Использование интерстициального окна: задержка между началом скачивания и запуском
Между моментом нажатия «Установить» в Google Play и моментом нажатия «Открыть» существует физическая задержка. Это окно состоит из трех этапов:
- Передача пакета: APK скачивается через Wi-Fi или мобильную сеть, длительность зависит от размера файла и скорости соединения.
- Проверка и установка: ОС Android сканирует пакет, проверяет подписи и распаковывает файлы.
- Задержка запуска: Пользователь видит иконку и нажимает на нее, что может произойти от пары секунд до нескольких часов.
Это окно — уязвимый временной коридор. Как только вредоносное приложение обнаруживает начало загрузки, у него есть достаточно времени, чтобы получить ссылку и сгенерировать клик до того, как целевое приложение будет впервые запущено.
Как мошенники манипулируют правилами атрибуции
Модели атрибуции по последнему клику отдают 100% успеха последнему зафиксированному клику перед установкой. Фродеры используют click injection, чтобы их метка времени клика стояла хронологически позже всех реальных касаний.
Если легитимный партнер показал рекламу и получил клик за несколько дней до этого (
При стандартной логике last-touch система атрибуции отдает конверсию фродовому клику, полностью игнорируя заслуги реального партнера.

Математика дельт времени реферера и инверсии кликов
Платформенные тайминги: время клика vs время начала установки
Противодействие click injection требует оценки хронологии установки относительно данных, предоставляемых платформой, а не клиентских часов.
Клиентская библиотека Google Play Install Referrer предоставляет два основных поля:
- Referrer Click Timestamp (
): Клиентская метка времени, когда был совершен клик по ссылке ( referrerClickTimestampSeconds). - Install Begin Timestamp (
): Клиентская метка времени начала установки в Google Play ( installBeginTimestampSeconds).
В AIDL-контракте Google также определяет серверные аналоги (referrer_click_timestamp_server_seconds и install_begin_timestamp_server_seconds). Backend-архитектуры используют их для сверки с данными рекламных сетей.
Расчет времени от клика до начала установки (CTIT)
Используя эти тайминги, системы атрибуции вычисляют Click-to-Install-Begin Time (
При легитимном пути пользователя клик предшествует началу установки:
При реальных действиях человека
Обнаружение инверсии: идентификация неконсистентных последовательностей
Click injection создает инверсию, при которой клик происходит после начала установки:
Таймлайн (t) ──►
[Пользователь нажал «Установить»] ───► [Начало установки] ──► [Первый запуск]
│ │ │
▼ ▼ ▼
t_download_click (Реальный) t_install_begin t_app_first_launch
▲ ▲
│ [ВРЕДОНОСНЫЙ КЛИК] │
└─── t_referrer_click ──────────┘
(CTIT_install_begin < 0: ОБНАРУЖЕНА ИНВЕРСИЯ)
Отрицательная дельта клика — это сильная аномалия, указывающая на то, что клик не предшествовал установке каузально. Важность этого сигнала должна оцениваться в совокупности с другими данными в рамках политики антифрода.
Как внедрить телеметрию Google Play Install Referrer в Android SDK
Добавление зависимости в build.gradle
Для захвата меток времени из магазина приложение должно включать официальную библиотеку Google Play Install Referrer.
Добавьте зависимость в build.gradle на уровне приложения:
dependencies {
implementation("com.android.installreferrer:installreferrer:2.2")
}
Подключение к InstallReferrerClient
InstallReferrerClient связывается с Google Play через сервис IPC. Поскольку данные сохраняются минимум 90 дней и не меняются между сессиями (кроме переустановки), клиентские приложения должны запрашивать эти данные при первом запуске и сохранять локально.
Ниже представлен Kotlin-код для работы с клиентом, вычисления дельты и локального кэширования:
```kotlin
// [CODE_BLOCK_01] Реализация Android Kotlin
package com.example.analytics.antifraud
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.RemoteException
import android.util.Log
import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
import com.android.installreferrer.api.ReferrerDetails
class PlayInstallReferrerManager(private val context: Context) {
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
private lateinit var referrerClient: InstallReferrerClient
fun retrieveInstallReferrerTelemetry(onTelemetryReady: (ReferrerTelemetryPayload) -> Unit) {
// Обеспечение идемпотентности: данные хранятся 90 дней, опрашиваем один раз
if (prefs.getBoolean(KEY_REFERRER_UPLOADED, false)) {
Log.d(TAG, "Данные Install Referrer уже доставлены.")
return
}
// Проверка локального кэша для повторной попытки отправки
if (prefs.getBoolean(KEY_REFERRER_CACHED, false)) {
val cachedPayload = getCachedPayload()
if (cachedPayload != null) {
Log.d(TAG, "Отправка кэшированного Install Referrer для ретрая.")
onTelemetryReady(cachedPayload)
return
}
}
referrerClient = InstallReferrerClient.newBuilder(context).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
try {
val response: ReferrerDetails = referrerClient.installReferrer
val clickTimestampSeconds = response.referrerClickTimestampSeconds
val installBeginTimestampSeconds = response.installBeginTimestampSeconds
val rawReferrerUrl = response.installReferrer
val isInstantApp = response.googlePlayInstantParam
val ctitDeltaSeconds = installBeginTimestampSeconds - clickTimestampSeconds
val isClickInversionDetected = ctitDeltaSeconds < 0
val sanitizedReferrer = validateAndSanitizeReferrer(rawReferrerUrl)
val payload = ReferrerTelemetryPayload(
referrerString = sanitizedReferrer,
clickTimestampSeconds = clickTimestampSeconds,
installBeginTimestampSeconds = installBeginTimestampSeconds,
ctitDeltaSeconds = ctitDeltaSeconds,
isClickInversionDetected = isClickInversionDetected,
isInstantApp = isInstantApp
)
cachePayloadLocally(payload)
Log.i(TAG, "Install Referrer получен: дельта CTIT=${ctitDeltaSeconds}с, инверсия=$isClickInversionDetected")
onTelemetryReady(payload)
} catch (e: RemoteException) {
Log.e(TAG, "Ошибка IPC с Google Play: ${e.message}")
} catch (e: SecurityException) {
Log.e(TAG, "Ошибка безопасности при привязке к Play Store: ${e.message}")
} catch (e: Exception) {
Log.e(TAG, "Ошибка чтения данных Install Referrer: ${e.message}")
} finally {
endConnectionSafely()
}
}
InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
Log.w(TAG, "API не поддерживается на данном устройстве.")
endConnectionSafely()
}
InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
Log.w(TAG, "Сервис Google Play недоступен.")
endConnectionSafely()
}
InstallReferrerClient.InstallReferrerResponse.DEVELOPER_ERROR -> {
Log.e(TAG, "Ошибка конфигурации разработчика.")
endConnectionSafely()
}
}
}
override fun onInstallReferrerServiceDisconnected() {
Log.d(TAG, "Сервис Install Referrer отключен.")
}
})
}
fun markTelemetryDelivered() {
prefs.edit()
.putBoolean(KEY_REFERRER_UPLOADED, true)
.remove(KEY_CACHED_REFERRER)
.remove(KEY_CACHED_CLICK_SEC)
.remove(KEY_CACHED_INSTALL_SEC)
.remove(KEY_CACHED_DELTA_SEC)
.remove(KEY_CACHED_INVERSION)
.remove(KEY_CACHED_INSTANT)
.apply()
Log.d(TAG, "Телеметрия подтверждена, кэш очищен.")
}
private fun endConnectionSafely() {
try {
if (::referrerClient.isInitialized && referrerClient.isReady) {
referrerClient.endConnection()
}
} catch (e: Exception) {
Log.w(TAG, "Ошибка закрытия клиента: ${e.message}")
}
}
private fun validateAndSanitizeReferrer(rawUrl: String?): String? {
if (rawUrl.isNullOrBlank() || rawUrl.length > 2048) return null
return try {
val uri = Uri.parse("https://dummy.local/?$rawUrl")
val allowedKeys = setOf("utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term", "channelCode")
val sanitizedParams = uri.queryParameterNames
.filter { it in allowedKeys }
.joinToString("&") { key -> "$key=${Uri.encode(uri.getQueryParameter(key))}" }
sanitizedParams.ifBlank { null }
} catch (e: Exception) {
null
}
}
private fun cachePayloadLocally(payload: ReferrerTelemetryPayload) {
prefs.edit()
.putBoolean(KEY_REFERRER_CACHED, true)
.putString(KEY_CACHED_REFERRER, payload.referrerString)
.putLong(KEY_CACHED_CLICK_SEC, payload.clickTimestampSeconds)
.putLong(KEY_CACHED_INSTALL_SEC, payload.installBeginTimestampSeconds)
.putLong(KEY_CACHED_DELTA_SEC, payload.ctitDeltaSeconds)
.putBoolean(KEY_CACHED_INVERSION, payload.isClickInversionDetected)
.putBoolean(KEY_CACHED_INSTANT, payload.isInstantApp)
.apply()
}
private fun getCachedPayload(): ReferrerTelemetryPayload? {
if (!prefs.getBoolean(KEY_REFERRER_CACHED, false)) return null
return ReferrerTelemetryPayload(
referrerString = prefs.getString(KEY_CACHED_REFERRER, null),
clickTimestampSeconds = prefs.getLong(KEY_CACHED_CLICK_SEC, 0L),
installBeginTimestampSeconds = prefs.getLong(KEY_CACHED_INSTALL_SEC, 0L),
ctitDeltaSeconds = prefs.getLong(KEY_CACHED_DELTA_SEC, 0L),
isClickInversionDetected = prefs.getBoolean(KEY_CACHED_INVERSION, false),
isInstantApp = prefs.getBoolean(KEY_CACHED_INSTANT, false)
)
}
companion object {
private const val TAG = "PlayReferrerManager"
private const val PREFS_NAME = "antifraud_referrer_prefs"
private const val KEY_REFERRER_CACHED = "key_play_referrer_cached"
private const val KEY_REFERRER_UPLOADED = "key_play_referrer_uploaded"
private const val KEY_CACHED_REFERRER = "key_cached_referrer_str"
private const val KEY_CACHED_CLICK_SEC = "key_cached_click_sec"
private const val KEY_CACHED_INSTALL_SEC = "key_cached_install_sec"
private const val KEY_CACHED_DELTA_SEC = "key_cached_delta_sec"
private const val KEY_CACHED_INVERSION = "key_cached_inversion"
private const val KEY_CACHED_INSTANT = "key_cached_instant"
}
}
data class ReferrerTelemetryPayload(
val referrerString: String?,
val clickTimestampSeconds: Long,
val installBeginTimestampSeconds: Long,
val ctitDeltaSeconds: Long,
val isClickInversionDetected: Boolean,
val isInstantApp: Boolean
)

Передача телеметрии реферера на бэкенд
Клиентская часть обеспечивает первичные данные, но окончательное решение об атрибуции принимает сервер. Устройства могут подвергаться подмене данных или перехвату прокси.
После извлечения ReferrerDetails SDK выполняет фильтрацию:
referrer_url: Парсится и проверяется по списку разрешенных ключей (utm_source,utm_campaign,channelCode).referrer_click_timestamp_seconds: Клиентский тайминг клика.install_begin_timestamp_seconds: Клиентский тайминг начала загрузки.google_play_instant: Булев флаг запуска через Google Play Instant.
Эти данные передаются по TLS на шлюз атрибуции для сверки с записями рекламных сетей.
Сравнение click injection и клик-спама
Контраст между методами атрибуционного фрода
Хотя оба метода являются видами перехвата атрибуции, они имеют разные сигнатуры.
| Параметр оценки | Click Injection (перехват установки) | Click Spamming (клик-флуд) | Легитимная атрибуция |
|---|---|---|---|
| Платформа | В основном Android | Кроссплатформенно | Кроссплатформенно |
| Дельта CTIT | Инвертированная ( |
Неинвертированная | Неотрицательная |
| MTTI (Время до установки) | Аномально короткое | Аномально длинное | Обычное распределение |
| Конверсия | От нормальной до высокой | Ниже базовой | Стандартная |
| Метод обнаружения | Сравнение таймингов Referrer | Моделирование распределения MTTI | Мультифакторная проверка |

Отличие всплесков инъекций от быстрых реальных загрузок
На высокоскоростных сетях (5G/Fiber) приложение может скачиваться очень быстро. Если система атрибуции ориентируется только на общее время (MTTI), реальные установки могут ошибочно помечаться как фрод.
API Google Play позволяет избежать этой ошибки. При реальной установке клик произошел до начала загрузки (
Когда необходима защита от click injection
Настройка мониторинга фрода в OpoInstall
OpoInstall предоставляет движок для контроля фрода, предназначенный для обнаружения попыток перехвата атрибуции.
Инженеры могут изучить документацию по мониторингу фрода для получения технических спецификаций.
Основные правила:
- Окно перехвата: Настройка порога MTTI. Установки, завершенные за аномально короткое время при противоречивых таймингах, помечаются как потенциальный фрод.
- Решение по атрибуции в реальном времени: Движок оценивает клики перед отправкой postback. Если инсталл помечен как фродовый, атрибуция может быть отклонена или перенаправлена в органическую категорию.
- Пороги аномалий IP и устройств: Ограничение количества установок с одного подсетевого IP или подозрительного устройства в течение 24 часов.
Аудит отчетов
Когда правила антифрода срабатывают, система логирует данные:
- Отчеты по IP и устройствам: Отслеживание подозрительных сетей и идентификаторов устройств.
- Отчеты MTTI: Визуализация лагов для сравнения каналов с агрегированными данными. Каналы с аномальными всплесками подлежат сверке с партнерами.
Подходящие и неподходящие условия для защиты
- Подходящие:
- Крупные Android-кампании, распространяемые через programmatic DSP, рекламные сети и CPA-брокеров.
- Приложения с большим объемом органики, подверженные «похищению» трафика.
- Кампании в рекламных каналах, не являющихся SAN (Self-Attributing Networks), где тайминги предоставляются сторонними паблишерами.
- Неподходящие:
- Чистый маркетинг на iOS: ОС не позволяет обычным приложениям отслеживать установку других приложений, делая классический click injection невозможным на не взломанных устройствах.
- Площадки, управляемые платформой (Apple/Google Search Ads): Атрибуция происходит внутри инфраструктуры магазина, что исключает стороннее вмешательство.
Распространенные заблуждения
- Заблуждение 1: Retention после установки покажет фрод: Поскольку click injection перехватывает реальных людей, показатели удержания и покупок выглядят нормальными.
- Заблуждение 2: Редиректы могут остановить инъекцию: Трекинг-ссылки управляют переходом в магазин, но не имеют доступа к событиям установки в ОС через минуты после перехода. Требуется интеграция с Google Play Install Referrer.
Часто задаваемые вопросы (FAQ)
Почему click injection уникален для Android?
Как API Google Play Install Referrer помогает обнаружить click injection?
Может ли click injection случаться при органических загрузках?
Итоги и система принятия решений
Click injection — это финансово опасная форма фрода, ворующая атрибуцию у реальных пользователей. Опора на retention-метрики или невалидированные клиентские тайминги оставляет Android-кампании уязвимыми.
Защита бюджета требует двухуровневой архитектуры: извлечение таймингов через API Google Play Install Referrer и использование гейтвеев атрибуции для блокировки в реальном времени. В паре с антифрод-движком OpoInstall это позволяет выявлять инверсию таймингов и повышать уверенность в том, что бюджеты распределяются эффективно.
Чтобы оценить, как унифицированная атрибуция и антифрод-мониторинг могут защитить ваши Android-кампании, ознакомьтесь с руководством по реализации атрибуции или настройте свое приложение в консоли разработчика OpoInstall.
Материалы по теме
-
Понятия: Рекламный фрод, Click Injection, Перехват установки, Время от клика до установки (CTIT), Среднее время до установки (MTTI)
-
Технологии: Google Play Install Referrer API, Play Integrity API, Архитектура Android SDK, Мониторинг фрода
-
Интерфейсы:
InstallReferrerClient(Google Play), Настройки правил OpoInstall, S2S Postbacks -
Официальная документация:
Share this article


