Лучший способ гарантировать правильные значения параметров Ajax-вызовов

Я работаю над веб-сайтом, который требует некоторых вызовов ajax, чтобы повысить гибкость и производительность. Мои призывы ajax относятся к системе ранжирования. Мне нужно три входных значения для обработки с помощью ajax (storeID, clientID, orderID). Чтобы отправить действие с помощью ajax, я хочу убедиться, что переданные значения параметров не были изменены пользователями, использующими веб-инструменты. Поэтому я думал о трех разных способах гарантировать, что отправленная информация не изменилась:

  1. Отправьте дополнительное значение, которое является шифрованием всех данных, отправленных вместе. Поэтому на стороне сервера при обработке ajax я могу повторно зашифровать отправленные данные и посмотреть, соответствует ли результат шифрования отправляемому значению шифрования.

  2. Отправлять все данные в виде одного зашифрованного значения. Затем на сервере при выполнении ajax я могу расшифровать данные и снова присвоить значения.

  3. Отправляйте только идентификатор orderID и его шифрование, а затем используя метод (1), убедитесь, что идентификатор orderID не изменен и с использованием запроса базы данных извлекайте две другие данные.

Вот мое мнение по каждому из трех способов:

  1. Расходует память, так как я должен отправить orderID, clientID, storeID, encryptedID. Более того, информация, отслеживаемая при вызове ajax, даст людям информацию о том, что происходит, когда они оценивают заказ.

  2. Я проверил онлайн для mcrypt_encrypt и mcrypt_decrypt, но я никогда не использовал их. Я видел, что они производят длинную строку, но я предпочитаю, чтобы мои данные передавались короткими или выглядели как зашифрованные данные md5. Есть ли лучшие методы?

  3. Это элегантный способ, он выглядит прямолинейно, но для этого требуется некоторое вмешательство MySQL, которое может занять много времени, особенно когда данные будут расти в будущем.

Так какой, по вашему мнению, лучше? Если у вас есть больше способов, я ценю, если вы поделитесь ими здесь. спасибо

Пример сценария, который я хочу избежать: нажатие кнопки отправит форму с использованием AJAX, передав идентификатор продукта. Пользователь переходит к исходному коду и меняет идентификатор продукта с X на Y. Теперь пользователь нажимает кнопку и форма отправляется, и на продукт ID Y влияет. Как вы можете видеть, присваиваемые значения параметров не защищены и могут быть изменены. Я ищу способ гарантировать, что переданные значения параметров верны и не изменены.

PS : Этот вопрос касается не обработки CSRF.

Solutions Collecting From Web of "Лучший способ гарантировать правильные значения параметров Ajax-вызовов"

Достаточно простой способ состоял бы в том, чтобы salt базовый только хеш-вперед (т. Е. Sha / md5 и т. Д.) Трех параметров против секретного ключа, хранящегося на сервере (и никогда не подвергался воздействию пользователей каким-либо образом).

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

Например:

 define('SUPER_SECRET_KEY', 'foobar123'); // our 007 secret key to hash requests against function generateToken($storeID, $clientID, $orderID) { return hash('sha256', SUPER_SECRET_KEY . "/{$storeID}/{$clientID}/{$orderID}"); } function validateToken($token, $storeID, $clientID, $orderID) { return generateToken($storeID, $clientID, $orderID) === $token; } 

Теперь это, как я уже сказал, очень простая отправная точка. Использование значения статической salt не будет очень эффективным, и вы все равно оставите себя открытым для повторных атак (например, кто-то, спам, тот же самый токен / clientID / storeID / orderID снова и снова).

Один из способов борьбы с атаками повторного воспроизведения – генерировать nonce для каждого запроса, чтобы действовать как salt .

Другой супер базовый пример с использованием nonces , тяжелый псевдокод впереди:

 function generateNonce() { return hash('sha512', mt_rand() . time()); // please make a more secure nonce generator than this :) } // We need a way to retrieve the nonce for a particular request: function generateLookup($storeID, $clientID, $orderID) { return hash('sha256', "{$storeID}/{$clientID}/{$orderID}"); } function generateToken($nonce, $storeID, $clientID, $orderID) { $lookup = generateLookup($storeID, $clientID, $orderID); // Store the $nonce and $lookup somewhere (ie a database) $db->query("INSERT INTO ... (nonce,lookup,expired) VALUES ('$nonce','$lookup','N')"); return hash('sha256', "{$nonce}/$storeID/$clientID/$orderID"); } function validateToken($token, $storeID, $clientID, $orderID) { $lookup = generateLookup($storeID, $clientID, $orderID); $rows = $db->query("SELECT nonce FROM ... WHERE lookup = '$lookup' AND expired = 'N'"); // again, pseudocode for retrieving the nonce from the persistent store if (count($rows) !== 0) { $nonce = $rows[0]['nonce']; // expire the nonce (note: these database queries should be in a transaction) $db->query("UPDATE ... SET expired = 'Y' WHERE $lookup = '$lookup' AND nonce = '$nonce'"); return generateToken($nonce, $storeID, $clientID, $orderID) === $token; } return false; } 

Тогда ваш основной рабочий процесс станет следующим:

В GET

 $data = // retrieve data $token = generateToken(generateNonce(), $data['storeID'], $data['clientID'], $data['orderID']); // output the $token value in a hidden field in your form 

В ПОСТ

 if (validateToken($_POST['token'], $_POST['storeID'], $_POST['clientID'], $_POST['orderID'])) { // All good } else { echo "Back, ye filthy hacker!"; } 

Каждый пытается привести вас к этим моментам:

На сервере ….

  1. Вы знаете, кто пользователь (auth).
  2. Вы должны определить, к чему может обратиться пользователь (ACL).
  3. Вы должны подтвердить, что запрашиваемый продукт существует и разрешен для доступа пользователем.
  4. Если пользователю разрешено вносить изменения в продукт, то по определению они могут вносить изменения в X-продукт, и если они делают это с информацией о продукте Y, потому что они беспорядочны в источнике, то это находится на пользователе.
  5. Если это связано с корзиной, то вы должны игнорировать все другие данные, переданные пользователем (например, цена) и загружать только данные этого продукта. Таким образом, они не могут купить Y-продукт по цене X-продукта. И тогда, если они войдут в идентификатор продукта Y в Firebug, кого это волнует?

И если вы не делаете 2 и 3, вы полностью теряете свое время на эту идею. Независимо от того, что вы придумали, будет только добавление неясности к вашей незащищенности.

Если вы хотите использовать шифрование, вы можете использовать функции Mcrypt с помощью шифрования Rijndael. Это дает вам блок из 128 бит для игры.

Предположим, что ваши три идентификатора (storeID, clientID, orderID) по 32 бита, вы можете упаковать их вместе с 4-символьной строкой, чтобы сформировать 128-битный блок. Строка будет использоваться для проверки дешифрованных данных позже.

 $block = pack('a4LLL', 'MyID', $storeID, $clientID, $orderID); 

Затем создайте случайный IV и зашифруйте блок:

 $mod = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); $iv = openssl_random_pseudo_bytes(mcrypt_enc_get_iv_size($mod)); $mySecretKey = 'LA72U/aLEOcDoN0rqtM5sehNNjd7TUSILiBgI8bej6o='; mcrypt_generic_init($mod, base64_decode($mySecretKey), $iv); $encrypted = mcrypt_generic($mod, $block); mcrypt_generic_deinit($mod); mcrypt_module_close($mod); 

Вам нужно будет закодировать зашифрованный блок и IV, прежде чем использовать их на стороне клиента. Использование base64 и замена символов / и + дает вам хорошее значение, которое можно отправить как параметр GET:

 $encodedBlock = substr(strtr(base64_encode($encrypted), '/+', '-_'), 0, -2); $encodedIV = substr(strtr(base64_encode($iv), '/+', '-_'), 0, -2); 

Позже, когда эти значения будут отправлены на ваши сценарии ajax, просто измените процесс:

 $encrypted = base64_decode(strtr($_GET['block'], '-_', '/+') . '=='); $iv = base64_decode(strtr($_GET['iv'], '-_', '/+') . '=='); $mod = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); mcrypt_generic_init($mod, base64_decode($mySecretKey), $iv); $block = mdecrypt_generic($mod, $encrypted); mcrypt_generic_deinit($mod); mcrypt_module_close($mod); $data = unpack('a4str/Lstore/Lclient/Lorder', $block); if ($data['str'] != 'MyID') throw new Exception('Something fishy is going on'); 

Если все идет хорошо, данные будут содержать $ data ['store'], $ data ['client'] и $ data ['order'].

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

Я действительно не думаю, что шифрование поможет вам здесь.

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

Я думаю, вам просто нужно проверить запрос на стороне сервера. Вы должны иметь возможность идентифицировать пользователя через переменные сеанса. Служба ajax может затем проверить запрос обычным способом.

Что касается вопросов безопасности, вам нужно держать вещи простыми.

Здесь простой факт состоит в том, что любое содержимое клиентской стороны может быть изменено.

Так что вам нужна проверка на стороне сервера .

Сервер сделал оригинальную форму, и эта форма была установлена ​​на странице, посвященной одному конкретному пользователю. Эта форма должна иметь значки som CSRF, гарантируя, что размещенные данные поступают со страницы, созданной сервером, а не с другого веб-сайта, а не с какого-либо автоматизированного сценария и т. Д.

Таким образом, первый шаг состоит в том, чтобы обеспечить, чтобы все ваши формы содержали некоторые маркеры, и что все формы, созданные вашим сервером, могут проверять токены токсов на определенное время и гарантировать, что токены соответствуют пользователю / форме, сгенерированной ранее. Это означает что-то вроде кеша форм на стороне сервера, с некоторыми серьезными задачами очистки в этом кеше, и вам также понадобятся сообщения hanlde (в ajax здесь) для опубликованной формы, содержащей устаревшие токены (извините, что вы так долго ждали длинный). Некоторые более простые подходы используют только пользовательский сеанс для хранения токенов анти-CSRF, вам необходимо найти решение, соответствующее всем вашим потребностям (анонимные формы, общие формы, многоэтапные формы и т. Д.),

После того, как у вас есть анти-CSRF-маркерная система, легко хранить на стороне сервера вещи, которые не позволяют пользователю изменять элементы формы. Такие вещи, как ваш «id», могут быть просто обработаны в кеше формы и даже не отправлены на клиентскую сторону. Вы также можете сохранить некоторую подпись элементов формы и т. Д.

Вы поехали тяжело без причин.

Вы почти никогда не должны устанавливать связь между запросом из браузера и следующими. Правильная логика (основа REST ) будет

  • по запросу, отправить в браузер информацию, связанную с идентификатором orderID
  • обрабатывать запрос на модификацию браузером, не беспокоясь о предыдущих запросах : просто проверьте с помощью базы данных, что запрос модификации действителен и согласован и в этом случае обрабатывает его

Извлечение двух значений из БД не должно вас беспокоить: если оно медленное, это означает, что вы не делаете это неправильно, и это можно решить.

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

Допустим, вы используете библиотеку javscript md5, просто вычислите хеш с помощью:

 hash=md5('sometext'+storeID+clientID+orderID); 

затем отправьте этот хэш с помощью AJAX и, наконец, на сервере вычислите его снова и сравните.

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

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

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

Например, если форма применяется только к одному продукту за раз, сохраните значение на стороне сервера (например, в переменной $SESSION ). Когда они отправляют форму, она может применяться только к этому продукту и будет влиять только на то, что они могут делать с пользовательским интерфейсом.

Если идентификатор клиента специфичен для пользователя, храните его на стороне сервера и вообще не отправляйте его клиенту.

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

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

Изменить: после комментариев по другим ответам, есть много способов реализовать это, но вот один из них, чтобы вы подумали:

Вы можете легко обрабатывать сразу несколько форм: используйте mcrypt_create_iv для генерации случайного токена. Скажем, 20 символов. Храните его в массиве сеансов

 $formtoken = mcrypt_create_iv(20); $_SESSION['infos'][$formtoken] = array($value,$anothervalue); 

Затем отправьте $formtoken в браузер. Каждый раз, когда браузер отправляется через AJAX, отправляет новый токен формы и удаляет данные, хранящиеся с предыдущим, поэтому он больше не действует в качестве представления. Единственная информация, которую вам нужно отправить, – это токен, который пользователь не может манипулировать, потому что он генерируется каждый раз, когда он запрашивает форму, и действителен только для этой формы, и другие данные, которые они могут манипулировать в любом случае.

Прежде всего, http не защищен никакими средствами. Следовательно:

Используйте https для защиты связи между клиентом и сервером.

Используя https, вы, например, защищаете сеанс пользователя от угона .

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

Храните данные, не связанные с конфиденциальностью, незатронутыми.

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

Данные, поступающие от клиента, не защищены

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

  • законный пользователь
  • троянец законного пользователя, захваченный ПК
  • или пакетов данных, введенных от третьего лица.

Проверьте клиентский -> сервер -> клиентский цикл

Чтобы убедиться, что данные от клиента действительно поступают от клиента, требуется, чтобы клиент передал некоторые данные, предоставленные с помощью сервера.

Предположим, что клиент позволяет передавать определенные значения V1, V2, ... с помощью http , которые до того, как они были переданы на сервер.

Если запрос приходит через обычные прямые клики по ссылкам браузера или косвенные вызовы ajax, это не имеет значения.

Знак со статической секцией на стороне сервера

Поскольку значения V1, V2, ..., Vn исходят от сервера, подпишите значения, поступающие из приложения вашего сервера.

Пойте, используя секретную TheSecret стороне TheSecret , например, значительно длинное значение:

 SignValue := md5( V1 + V2 + ... Vn + TheSecret ) 

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

Подписать секретный серверный секрет

Помимо постоянной тайны TheSecret , вы можете генерировать новый секрет каждый день. DailySecret может быть сгенерирован следующим образом:

 DailySecret := TheSecret + md5( TheSecret + date( 'Ymd' ) ) SignValue := md5( V1 + V2 + ... Vn + DailySecret ) 

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

Но если вы не можете этого сделать, вы также можете просто присвоить значения на сервере, а ваш аякс принимает только хэшированные значения. Если клиент меняет хэш, он становится недействительным, поэтому вы идете. Это стандартная практика для предотвращения манипулирования идентификаторами.

Надеюсь это поможет.