IT

Как отличить мошенника от настоящего посетителя (реферер и не только)

IT · · 5 мин чтения

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

В этой статье мы научим нашего охранника отличать настоящего посетителя, который правда кликнул, от такой скрытой пустышки.

В чём подвох

Давайте вспомним, как выглядит обман. Вы открываете обычную страницу — ну, например, посмотреть смешные картинки. А на этой странице мошенник спрятал крошечное невидимое «окошко» (по-умному оно называется iframe) и незаметно загрузил в нём наш турникет /go.

Что происходит? Турникет честно срабатывает, выдаёт билетик и кладёт вам печеньку — хотя вы никуда не нажимали. Вы просто смотрели картинки! Получается, наш охранник обмануть можно: он не видит разницы между живым посетителем и такой пустышкой.

Значит, охраннику нужно научиться понимать: этот человек правда зашёл через главный вход — или его «внесли» тайком в кармане?

Что такое реферер

Когда вы переходите с одной страницы на другую, ваш браузер обычно шепчет новому сайту: «Кстати, этот человек пришёл вот отсюда». Эта записка «откуда пришёл» и называется реферер (от английского referrer — «направивший»).

Например, вы были в блоге у Васи, нажали на ссылку и попали в магазин. Браузер шепнёт магазину: «Он пришёл из блога Васи». Удобно!

Реферер помогает охраннику. Если посетитель пришёл из нормального, ожидаемого места — это хороший знак. Если реферер пустой или какой-то подозрительный — стоит насторожиться.

Проверить реферер на Python очень просто:

@app.route("/go")
def turniket():
    # браузер сам присылает записку "откуда пришёл"
    otkuda = request.headers.get("Referer")

    print("Посетитель пришёл из:", otkuda)
    # ... дальше решаем, доверять ему или нет

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

Честный свидетель — заголовки Sec-Fetch

И такой свидетель есть! Современные браузеры вместе с каждым запросом присылают маленькие честные подсказки, которые называются Sec-Fetch. Самое приятное: их нельзя подделать со страницы. Даже если мошенник очень старается, браузер всё равно скажет охраннику правду.

Браузер как бы тихо сообщает: «Знаешь, что именно к тебе сейчас пришло?»

  • «Это настоящий человек, который перешёл по ссылке и открыл целую страницу» → Sec-Fetch-Dest: document и Sec-Fetch-Mode: navigate.
  • «А это просто картинка грузится в уголке» → Sec-Fetch-Dest: image.
  • «А это то самое невидимое окошко-пустышка (iframe)» → Sec-Fetch-Dest: iframe.

Видите? Браузер сам честно рассказывает, живой это посетитель или пустышка. Мошенник спрятал турникет в невидимом окошке — и браузер тут же выдаёт его: «Это iframe!». Охраннику остаётся только не пускать таких.

Вот как охранник проверяет это на Python:

from flask import Flask, request, redirect, make_response
import secrets

app = Flask(__name__)
zhurnal = {}

@app.route("/go")
def turniket():
    partner = request.args.get("partner")
    target = request.args.get("target")

    # спрашиваем у браузера честную подсказку
    kuda = request.headers.get("Sec-Fetch-Dest")   # document / image / iframe ...
    kak = request.headers.get("Sec-Fetch-Mode")    # navigate / no-cors ...

    # настоящий посетитель — это открытие ЦЕЛОЙ страницы по переходу
    nastoyashchiy = (kuda == "document" and kak == "navigate")

    if not nastoyashchiy:
        # это пустышка (iframe, картинка и т.п.) — билет НЕ выдаём,
        # просто тихо отправляем дальше без печеньки
        return redirect(target)

    # а вот теперь всё честно — выдаём билет как обычно
    click_id = secrets.token_hex(16)
    zhurnal[click_id] = {"partner": partner, "ispolzovan": False}

    otvet = make_response(redirect(target))
    otvet.set_cookie("click_id", click_id, max_age=2592000,
                     httponly=True, samesite="Lax")
    return otvet

Обратите внимание на главную мысль: билетик теперь выдаётся не всем подряд, а только настоящим посетителям. Пустышку охранник пропускает дальше без печеньки — и мошенник остаётся ни с чем.

А если браузер очень старый?

Иногда честной подсказки Sec-Fetch может не быть — например, у очень старого браузера. В этом случае охранник не доверяет гостю слепо. Он показывает крошечную страничку-проверку, на которой сам гость (точнее, его браузер) отвечает на простой вопрос: «Ты сейчас — целая страница или прячешься в чужом окошке?»

stranichka_proverki = """
<script>
  if (window.top !== window.self) {
    // мы внутри чужого невидимого окошка — это пустышка, ничего не делаем
  } else {
    // мы — настоящая целая страница, можно подтвердить переход
    location.replace("/go/confirm" + window.location.search);
  }
</script>
"""

Тут используется простой факт: если страница честно открыта сама по себе, то window.top и window.self — это одно и то же. А если её спрятали в чужом окошке — они разные. Так пустышка сама себя выдаёт.

Ещё один способ — просто присмотреться к цифрам

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

На что смотреть:

  • Тысячи «посетителей», но почти нет покупок. Если партнёр приводит гигантскую толпу, а покупает почти никто — это очень странно. У честных блогеров так не бывает.
  • Слишком много людей из одного и того же места (один и тот же компьютер, один адрес). Живые люди так не приходят.
  • Резкий скачок из ниоткуда. Вчера партнёр приводил пять человек, а сегодня вдруг пятьдесят тысяч. Повод проверить вручную.

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

Коротко

  • Реферер — это записка «откуда пришёл посетитель». Полезная, но её можно потерять или подделать, поэтому доверять только ей нельзя.
  • Заголовки Sec-Fetch — честный свидетель от самого браузера. Их нельзя подделать со страницы, и они прямо говорят: настоящий это посетитель (document + navigate) или пустышка (iframe, image).
  • Билетик теперь выдаётся только настоящим посетителям, а пустышки проходят без печеньки.
  • Для совсем старых браузеров есть запасная страничка-проверка с window.top.
  • А поверх всего — наблюдательность за цифрами: странные партнёры с морем кликов и без покупок отправляются на ручную проверку.

Вместе редирект, ClickID и проверка «настоящий ли посетитель» превращают наш простой турникет в умного охранника, которого куки-стаффингом уже не обманешь.

Комментарии

Пока никто не высказался. Будьте первым.