سؤال

الخلفية: حسنا، لقد قمت بتشغيل BBG Legacy في Ninjawars.net. هناك "هجوم" يمكن أن يقوم اللاعبون بإجراء لاعبين آخرين يتم تهيئته عبر منشور النموذج. في الأساس، يمكننا تبسيط الوضع على التظاهر بأن هناك صفحة، وتتيح تسميتها مهاجمة.PHP، مع نموذج "الهجوم" العملاق الذي يقدم إلى صفحة PHP أخرى، يتيح الاتصال عليه قبول appress_attack.php، والصفحة الثانية تؤدي الهجوم وظيفة، دعونا نقول قتل اللاعب الآخر 1 أو 2 أو 3. يعمل الخادم PHP5، postgresql، Apache

المشكلات:

  • إذا ضربت هذا الزر "الهجوم" الكبير، فهو يجلبني إلى قبول acceptack.php، يمكنني بعد ذلك ضرب تحديث ثلاث مرات، وإعادة إرسال كل مرة، للهجوم مرة أخرى ثلاث مرات على التوالي.
  • إذا قمت بفتح ثلاثة علامات تبويب في الصفحة الأولى، وضرب الهجوم على كل صفحة، في نهاية المطاف مع ثلاثة هجمات فورية تقتل اللاعبين 1 و 2 و 3 في وقت واحد، ويمكنني التحديث باستمرار للتكرار.
  • على الرغم من محاولاتي أن يكون لديك توقيت "أحدث هجوم" الذي يتم حفظه في قاعدة البيانات، يبدو أن اللاعبين قادرين على العمل من حوله، وربما فقط من خلال تحديث ثلاث علامات تبويب نسخ في طريقة متزامنة بما فيه الكفاية، حتى يتمكنوا جميعا على استرداد نفسه الموقت (على سبيل المثال 10: 00: 00: 0000 ص) وبالتالي المضي قدما في المعالجة الناتجة.

الحل مطلوب:

إذن كيف يمكنني منع نفس معالجة برنامج نصي معين من التشكيل مرة واحدة في الثلاث مرة في ثلاث نسخ؟

PHP، الهندسة الاجتماعية، وحلول JavaScript / JQuery Solutions المفضل (ربما في هذا النظام).

تحرير: بناء على الإجابات، إليك ما فعلته (محتمل، قبل اختبار الإجهاد) حلها: يبدو أن الإجابة الجلسة أبسط / الأكثر شمولية لتنفيذها، لذلك اعتدت على متجر البيانات. لقد اختبرته ويبدو أنه يعمل، ولكن قد تكون هناك طرق حولها أنني لست على علم بذلك.

$recent_attack = null;
$start_of_attack = microtime(true);
$attack_spacing = 0.2; // fraction of a second
if(SESSION::is_set('recent_attack')){
    $recent_attack = SESSION::get('recent_attack');
}

if($recent_attack && $recent_attack>($start_of_attack-$attack_spacing)){
    echo "<p>Even the best of ninjas cannot attack that quickly.</p>";
    echo "<a href='attack_player.php'>Return to combat</a>";
    SESSION::set('recent_attack', $start_of_attack);
    die();
} else {
    SESSION::set('recent_attack', $start_of_attack);
}

إذا كانت هناك طرق لتحسين ذلك أو الطرق التي يتم قابلة للاستغلال (ما وراء الواضح لي أن الاشياء التي تعتبر الاشياء ليست منفصلة جيدة في المنطق، فأنا أحب أن أعرف. على طول تلك الخطوط، المجتمع-Wiki-ed وبعد

هل كانت مفيدة؟

المحلول

على غرار حلول godeke. لا يمكن أن تولد رمزا مع حقل مخفي على شكل زر "الهجوم" وتخزين ذلك في جلسة؟ ثم على صفحة قبول المهاجمة. ستحقق ما إذا كانت $ _Post ['token'] == $ _session _session ['token'].

لذلك سيكون لديك شيء مشابه لهذا على صفحة قبول الهجوم.

if($_POST['token'] == $_SESSION['token']){

       echo 'no cheating!';
            // or redirect to the attach page
   }else{
         $_SESSION['token'] = $_POST['token'];
         // then perform the attack
   } 

نصائح أخرى

على الرغم من أن نمط الحصول على ما بعد إعادة التوجيه، إلا أن نمط الحصول على بعض المشاكل، إذا كانوا يعبون عمدا عملية التقديم، فأشك في أنه سيمنع المشكلة إلا ضد كسول (كما هو مذكور في المقال المرتبط، ستكون التقديمات قبل استجابة 302 متعددة كما redirect لم يحدث حتى الآن).

بدلا من ذلك، من المحتمل أن تكون أفضل وضع بعض المعلومات في صفحة الهجوم التي لا يتم استنساخها بسهولة. عندما تقبل الهجوم، ادفع الهجوم إلى جدول قائمة انتظار قاعدة البيانات. على وجه التحديد، قم بتخزين الرمز المميز للمعلومات المرسلة إلى صفحة الهجوم عند الانتظار والتحقق من معرفة ما إذا كان قد تم استخدام هذا الرمز المميز بالفعل قبل وضع الانتظار للهجوم.

سيكون مصدر الرموز البسيط هو نتائج تشغيل مولد أرقام عشوائي ووضعها في طاولة. اسحب الرقم التالي لكل تحميل صفحة هجوم وتحقق من أن هذا الرقم قد تم توزيعه مؤخرا. يمكنك إعادة فتح الرموز في صفحات المهاجمة وتنتهي صلاحية أي رموز رموز "غير مستخدمة" بناء على سياستك إلى متى يجب أن تكون هذه الصفحة متاحة قبل الذهاب "بعنصر".

وبهذه الطريقة، تولد مجموعة محدودة من الرموز الرائعة "صالحة"، تقوم بنشر هذه الرموز في صفحة الهجوم (واحدة لكل صفحة) وتحقق من أنه لم يتم استخدام الرمز المميز بالفعل في صفحة معالجة الهجوم. لإنشاء هجمات تكرار، سيتعين على اللاعب تحديد الرموز الصحيحة سارية ... تكرار نفس المنشور سوف تفشل لأن الرمز المميز قد استهلك. استخدم Bigint و PSEDO مولد أرقام عشوائي عشوائي ومساحة البحث من غير المرجح أن يكون من السهل التحايل. (لاحظ، ستحتاج إلى معاملة حول التحقق من صحة الرمز المميز وتحديثات لضمان النجاح بهذه الطريقة.)

إذا كان لديك حسابات مستخدمين تتطلب تسجيل الدخول، فيمكنك إنشاء وتخزين هذه الرموز في جدول المستخدم (مرة أخرى، باستخدام معاملة قاعدة بيانات ملفوفة حول هذه الخطوات). ثم سيكون لكل مستخدم رمزية صالحة واحدة في وقت واحد وسيتم اكتشاف التقديمات المتعددة بطريقة مماثلة.

يمكنك تجنب معظم عمليات إعادة التقديمات باستخدام بعد إعادة التوجيه نمط لنموذج المشاركات.

باختصار، بدلا من العودة attack_accept.php من المشاركة الأصلية، إرجاع استجابة 302 لإعادة توجيه المتصفح إلى attack_accept.php. وبعد الآن عندما يقوم المستخدم بإعادة تحميل الصفحة، فإنها مجرد إعادة تحميل طلب 302 ولا يوجد تقديم نموذج مكرر.

حل آخر، سيكون لتسلسل بيانات البريد (أحب JSON بنفسي) ثم هاشها، تخزين النتيجة في DB.

إذا قدم المستخدم نفس المعلومات مرتين، فستوفر التجزئة في قاعدة البيانات.

يجب عليك أيضا إضافة زمني إلى نفس الجدول، بحيث يمكن حذف / تحديث التجزئة بعد ساعات X

عينة php pseudo-code:

$hash = sha1(json_encode($_POST));
$results = $db->exec('SELECT timestamp FROM user_posts WHERE user_id=? AND hash=?', $user_id, $hash);

if ($results != null) {
    // check timestamp, allow if over 24 hours ago
    $ok = ($results['timestamp']+3600*24) < now();
} else {
    // no results, allow
    $ok = true;
}

if ($ok) {
    $db->exec('INSERT INTO user_posts (hash, timestamp) VALUES (?, ?)', $hash, now() );
} else {
    // show error page
    echo "your request has been denied!";
}

ملاحظة: سيظل هذا السماح بتقديم بيانات نشر مختلفة في فترة قصيرة من الزمن، ولكن هذا أيضا سهل التحقق.

يجب أن يكون هذا الحل مستحيلا في التحايل:

1) إضافة "NextAttackToken CHAR(32)"العمود إلى جدول اللاعبين الخاص بك، وإعطاء كل لاعب قيمة MD5 التي تم إنشاؤها عشوائيا.

2) على attack.php الصفحة، إضافة حقل مخفي "Current_token" مع الرمز المميز الحالي للاعب.

3) في accept_attack.php الصفحة، استخدم المنطق التالي لتحديد ما إذا كان اللاعب مسموح بالفعل بالهجوم:

// generate a new random token
$newToken = md5(microtime(true).rand());

// player is spamming if he has attacked less than 30 seconds ago
$maxTimer = date('Y-m-d H:i:s', strtotime('-30 seconds'));

// this update will only work if the player is allowed to attack
$query = "UPDATE player SET NextAttackToken = '$newToken'
               WHERE PlayerID = $_SESSION[PlayerID]
               AND PlayerLastAttack < '$maxTimer'
               AND NextAttackToken = '$_GET[current_token])'
         ";
$result = mysql_query($query);
if(mysql_affected_rows($result)) {
    echo "Player is allowed to attack\n";
}
else {
    echo "Player is spamming! Invalid token or submitted too soon.\n";
}

يعمل هذا الحل لأن MySQL لا يمكن لأداء تحديث واحد فقط على الطاولة في وقت واحد، وحتى إذا كانت هناك 100 طلب غير مصاب بالضبط في نفس الوقت بالضبط، فإن التحديث الأول من MySQL سيغير الرمز المميز ووقف 99 تحديثات أخرى من التأثير على أي صفوف وبعد

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top