Как отличить мошенника от настоящего посетителя (реферер и не только)
В прошлой статье мы построили турникет (редирект) и научились выдавать каждому посетителю уникальный билетик (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 и проверка «настоящий ли посетитель» превращают наш простой турникет в умного охранника, которого куки-стаффингом уже не обманешь.
Комментарии
Пока никто не высказался. Будьте первым.