Как проверить подлинность AJAX-запроса - PullRequest
34 голосов
/ 07 января 2010

Я разрабатываю веб-сайт, на котором пользователи решают головоломки как можно быстрее. JavaScript используется для определения времени каждой головоломки, а количество миллисекунд отправляется на сервер через AJAX после завершения головоломки. Как я могу убедиться, что время, полученное сервером, не было подделано пользователем?

Я не думаю, что токен аутентификации на основе сеанса (тип, используемый для форм в Rails) достаточен, потому что мне нужно аутентифицировать source значения, а не только легитимность запроса.

Есть ли способ криптографической подписи запроса? Я не могу думать ни о чем, что не могло бы быть продублировано хакером. Любой JavaScript, в силу своего открытого, клиентского характера, подвержен фальсификации? Я собираюсь использовать что-то, что компилируется, например, Flash? (Yikes.) Или есть какой-то способ скрыть секретный ключ? Или что-то еще, о чем я не думал?

Обновление: чтобы уточнить, я не хочу наказывать людей с медленными сетевыми подключениями (и скорость сети следует считать несовместимой), поэтому время должно быть на 100% на стороне клиента (таймер запускается только тогда, когда мы пользователь может видеть загадку). Кроме того, в это вовлечены деньги, поэтому никакое «доверие пользователя» недопустимо.

Ответы [ 15 ]

12 голосов
/ 07 января 2010

Вы не можете гарантировать безопасность таймингов криптографически, потому что браузер клиента не может выполнять безопасные вычисления. Любое средство для шифрования на / с сервера может быть обойдено путем корректировки фактического времени.

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

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

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

10 голосов
/ 18 января 2010

Этот подход, очевидно, делает предположения и не является непобедимым. Все вычисления выполняются на клиенте, и сервер выполняет некоторые фоновые проверки, чтобы выяснить, мог ли запрос быть подделан. Как и любой другой подход, основанный на клиентах, он не является детерминированным, но делает его очень сложным для лживого клиента.

Основное предположение состоит в том, что долгоживущие HTTP-соединения намного быстрее для передачи данных, даже незначительные в некоторых случаях в зависимости от контекста приложения. Он используется в большинстве систем онлайн-торговли, так как цены на акции могут меняться несколько раз в течение секунды, и это самый быстрый способ передать текущую цену пользователям. Вы можете прочитать больше о HTTP Streaming или Comet здесь.

Начните с создания полнодуплексного ajax-соединения между клиентом и сервером. На сервере есть выделенная линия для связи с клиентом, и клиент, очевидно, может общаться с сервером. Сервер отправляет головоломку и другие сообщения клиенту по этой выделенной линии. Предполагается, что клиент должен подтвердить получение каждого сообщения на сервер вместе со своей локальной меткой времени.

На сервере генерируйте случайные токены (могут быть просто разными целыми числами) после того, как загадка была отправлена, запишите время, когда был создан каждый токен, и передайте его клиенту. Клиент видит сообщение и должен немедленно ретранслировать этот токен вместе с местным временем получения. Чтобы сделать его непредсказуемым для клиента, генерируйте эти серверные токены с произвольными интервалами, например, между 1 и n мс.

Клиент может отправлять на сервер три типа сообщений:

PUZZLE_RECEIVED
TOKEN_RECEIVED
PUZZLE_COMPLETED

И два типа сообщений, которые сервер отправляет клиенту:

PUZZLE_SENT
TOKEN_SENT

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

Теперь, когда сервер получает квитанцию ​​на сообщение, которое он отправил, запишите время клиента, содержащееся в этом сообщении. Поскольку токен также был ретранслирован обратно в этом сообщении, мы можем сопоставить его с соответствующим токеном на сервере. В конце головоломки клиент отправляет на сервер сообщение PUZZLE_COMPLETED с местным временем. Время завершения головоломки будет:

PUZZLE_COMPLETED.time - PUZZLE_RECEIVED.time

Затем дважды проверьте, рассчитав разницу во времени для каждого отправленного и полученного сообщения.

PUZZLE_RECEIVED.time - PUZZLE_SENT.time
TOKEN_RECEIVED.time - TOKEN_SENT.time

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

8 голосов
/ 18 января 2010

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

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

К сожалению, вы не можете сделать компенсацию задержки, так как это может быть взломано. Однако в соединении, которое не используется ни для чего другого, задержка запроса, подобного этому, будет сильно омрачена временем, затрачиваемым человеком на решение головоломки, я не думаю, что это будет иметь большое значение.

(Причина: 200 мс считается очень плохим лагом, и это среднее время реакции человека. Кратчайшая возможная «головоломка» для человека - визуальный тест скорости реакции, и в этом случае плохое отставание будет иметь 100% разметка их результатов. Таким образом, в качестве временного решения это 2-OPT. Любая более сложная головоломка будет менее подвержена влиянию задержки.)

Я бы также разместил на странице баннер, говорящий о том, что нельзя использовать подключение к Интернету для чего-либо другого, играя на максимально возможной скорости, возможно, связываясь с тестером скорости / задержки.

4 голосов
/ 12 января 2010

Невозможно запустить и остановить таймер на стороне клиента, не опасаясь манипуляций ...

Все, что вы выполняете на клиенте, может быть изменено / остановлено / пропущено ..

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

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

Время должно начать на сервере и остановить на сервере.

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

..

3 голосов
/ 07 января 2010

Вы должны использовать здесь время на стороне сервера. Вот как бы я это сделал:

Сделайте AJAX-запрос на готовый документ для проверки связи с сервером. Когда код на стороне сервера получает эхо-запрос, сохраните время на стороне сервера в качестве переменной сеанса (убедившись, что переменная еще не существует). Когда они закончат тест, снова возьмите время на стороне сервера и сравните его с переменной сеанса, чтобы определить их продолжительность. Удалите переменную сеанса.

Почему это работает:

  • Вы не запускаете таймер до , они видят тест
  • Сетевая задержка учитывается, поскольку таймер не запускается, пока не поступит запрос AJAX (если у них медленное соединение, запрос AJAX будет медленным)
  • Ping не является спуфингом, потому что вы уверены, что переменная сеанса не существует перед сохранением

РЕДАКТИРОВАТЬ: Я хотел бы добавить, что вы могли бы продолжать сохранять время на стороне клиента, и включить его в последний пост. Затем вы можете сравнить его с вашим рассчитанным временем на стороне сервера. Если они достаточно близки, то вы можете доверять клиенту время.

3 голосов
/ 07 января 2010

В зависимости от реализации на стороне сервера, вы можете разместить функцию синхронизации на стороне сервера. Запишите время, когда был сделан запрос веб-страницы (вы можете поместить его в базу данных, если хотите), а затем, когда ответ получен, получить текущее время и выполнить некоторую арифметику, чтобы получить продолжительность ответа. Вы можете хранить время в объекте сеанса, если вам нравится, а не в базе данных, хотя я не знаю слишком много о его целостности там.

2 голосов
/ 16 июня 2010

Это сложная проблема, потому что она принципиально неразрешима, поэтому вам нужно обойти компромиссы, чтобы сделать все возможное. С технической стороны было высказано несколько хороших замечаний, в том числе: (а) не тратьте свое время на размышления о том, что компиляция в Flash, Windows, Silverlight, JVM или что-либо еще действительно поможет, (б) сначала передайте зашифрованную реальную полезную нагрузку головоломки затем, когда фактическое узкое место передает только один ключ, (c) задержка даже на 56 КБ при отправке пары сотен байтов незначительна по сравнению со временем реакции человека.

Одна вещь, которую я не заметил, это:

Использование постфактум аудита и отслеживания пользователей. Так работают настоящие казино. Это, как мне сказали, большая часть того, как PayPal заработал свои деньги. В обоих случаях, вместо того, чтобы делать большую безопасность до факта, они очень внимательно следят за всем об их игроках, используют много технологий (статистика, обнаружение паттернов и т. Д.), Чтобы отмечать подозрительных игроков, и они проводят расследование. И в казино, и в PayPal вы не можете сразу получить свои деньги. Вы должны обналичить свои фишки или подождать, пока деньги поступят из системы PayPal в ваш настоящий банк. Ваша система должна работать таким же образом - на самом деле они не могут получать деньги как минимум в течение как минимум нескольких дней (дольше, если вы не можете настроить достаточно быструю систему аудита), что дает вам время, чтобы потенциально отобрать их выигрыш. У вас есть адвокат, верно? Кроме того, казино и PayPal знают вашу реальную личность (обязательно для того, чтобы торговать деньгами), чтобы они могли преследовать вас на законных основаниях - или, что более важно, удерживать потенциальных злоумышленников, поскольку они могут преследовать их легально.

В сочетании с другими советами этого может быть достаточно, чтобы полностью исключить мошенничество.

Если вы обнаружите, что это не так, поставьте себе цель не полностью устранить мошенничество, а сохранить его на приемлемом уровне. Вроде как с 99,99% безотказной работы. Да, как сказал один из авторов, если хотя бы один человек может скомпрометировать это, все облажались, но при хорошей системе аудита злоумышленник не сможет постоянно обманывать. Если они могут обмануть 1 в 1000 раз, если им повезет, и они обнаружат, что не могут обмануть более одного или двух раз, прежде чем их поймают, это не будет проблемой, так как очень немногие обманут, и у любого данного честного пользователя будет крайне низкий риск быть обманутым чрезвычайно малым количеством мошенничества. Это будет незаметно. Если у вас когда-либо случился реальный обман, причиняющий боль честному пользователю, старайтесь изо всех сил, чтобы честный пользователь чувствовал удовлетворение результатом до степени, не пропорциональной ценности этого отдельного клиента. Таким образом, все будут уверены в вашей безопасности. Они знают, что им не о чем беспокоиться.

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

2 голосов
/ 18 января 2010

- Изменить:

Это решение несколько ошибочно, как указывает ZoFrex ниже.

- Старый:

Вот способ (но вам нужно выполнить некоторое профилирование).

Отправьте ряд «проблем», которые должен решить JavaScript, пока они играют в головоломку. Раньше у меня было число N достаточного размера, такое, что оно является результатом: prime1 * prime2. Это вынуждает клиента вычислять число (вы можете получить код для выполнения этого в JavaScript), и это займет время (именно здесь приходят профилирующие клиенты и отправляют простые числа соответствующего размера [очевидно, это открывает вас для атак деградации) , но тем не менее]).

Затем вы просто отправляете, скажем, 500 из этих простых задач (или другого типа), и позволяете JavaScript решать их в фоновом режиме. Он создаст список решений, и когда вы отправите завершенное значение, вы также отправите этот список. Из общего количества предоставленных ответов вы можете определить, сколько времени они потратили на головоломку.

Минусы:

  • Требуется профилирование для определения возможностей различных клиентов (и, следовательно, сложности проблем)
  • Может быть атакован понижением рейтинга
  • Немного сложнее
  • Вычисление JavaScript может прервать общее решение головоломки
  • Можно написать бота, чтобы быстрее решать проблемы, чем JS

Плюсы:

  • Расчеты необходимо сделать для отправки формы
  • При правильном применении предотвратит все атаки, кроме нетривиальных

Ясно, что на него можно атаковать (все предлагаемые ответы), но я думаю, что это разумно. По крайней мере, было бы интересно поработать:)

В конце концов, вам нужно установить клиентскую систему с большей безопасностью. И обратите внимание, что Flash, конечно, не это; тривиально декомпилировать. Фактически, когда-то здесь, в Австралии, проводился тест IQ, и он контролировался через приложение Flash, которое проводилось в прямом эфире на телевидении. Конечно, победителем стал программист, интересно, почему: P

- Изменить:

OP, Кроме того, я связал его в комментарии к этому сообщению, но просто если вы пропустите его, вас как бы интересует Hashcash , цель которого показать, что клиент завершил некоторое количество «Работа». Даже если моя реализация не подходит, вы можете найти обзор этой области плодотворным.

2 голосов
/ 14 января 2010

Вы задали несколько вопросов в своем первоначальном вопросе, я отвечу только на один из них:

Должен ли я использовать что-то, что компилируется, например, Flash? (Yikes.)

Да. С учетом ваших критериев: 1) точность 100%, и 2) нет возможности вмешательства пользователя, у вас есть для использования скомпилированного двоичного файла.

Хотя это не обязательно должно быть flash - я бы предложил java-апплет, если мысль о Flash заставляет вас говорить «Yikes».

1 голос
/ 13 января 2010

Как отметили несколько других:

  1. Вы должны использовать серверное время, потому что клиентское время уязвимо для манипуляций.
  2. Проверка времени на сервере потенциально накажет людей с медленными сетевыми подключениями или людей, которые находятся далеко.

Ответом на проблему является использование протокола синхронизации времени между клиентом и сервером, аналогичного протоколу, который использует NTP. Работая вместе, клиент и сервер определяют величину задержки, вызванную задержкой в ​​сети. Затем это учитывается при определении времени каждого пользователя.

Алгоритмы NTP сложны и разрабатываются годами. Но простой подход ниже; Я думаю, что протокол должен работать, но вы можете проверить его.

Попросите клиента измерить время прохождения туда и обратно с помощью двух последовательных HTTP-запросов XMLRPC. Каждый пинг возвращает одноразовый номер. Второй пинг требует одноразового номера от первого пинга, который гарантирует, что они последовательны. Время головоломки начинается, когда от клиента отправляется второй HTTP-пинг. Сервер метит время каждого запроса и предполагает, что головоломка отображается на полпути между получением первого и второго запроса.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...