Вчера случился казус на нашем Блоге, глюкнула система комментариев статей. Был сбой связи и скрипт засчитал несколько попыток добавления. В итоге вместо одного комментария появилось четыре. Такое часто встречается на разных сайтах, где нет защиты от этих глюков и откровенного флуда. До сего момента не было и у нас, так как блог у нас самодельный, разработан на наших уроках, служит примером для подражания )
Но раз казус произошел, нужно что то с ним делать. А за одно извлечем из этой неприятности пользу - напишем сюда же статью. Всем будет полезно.
Итак, обрисуем проблему. Представим себе жииирного, лоснящегося тролля, у которого сосет под ложечкой и который ищет себе жертву на завтрак. И вот его глазки заблестели, он нашел некую гостевую книгу:
Ну от F5 сейчас худо бедно все защитились, но в браузере еще есть кнопка "назад". И если тролль пользуется продвинутым браузером типа Оперы или Хрома, то запостив единожды какую-нибудь чушь, он с помощью комбинации клавиш alt+left может заполонить всю книгу однообразным сообщениями, сиречь флудом.
Кроме того, как уже говорилось, это может произойти не со зла. Из-за сбоя связи или иных глюков, от нас не зависящих. Особенно этим грешат телефоны почему то.
Так вот, есть очень простой и действенный способ этого избежать - прошивка формы сессией. В теории делается так. В момент открытия формы генерируется рандомное число, которое пишется в сессию и скрытое поле формы. Когда комментарий добавляется, сверяем эти два числа (подобие капчи). Если совпадают, замечательно. Нет - давай до свидания.
Посмотрим на простеньком примере:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<?php
// Нам понадобится сессия session_start(); // Если форма отправлена, небольшая валидация if(!empty($_POST) && !empty($_POST['text'])) { // Сверяем контрольные числа if($_POST['filum'] == $_SESSION['filum']) { // Если все в порядке - постим file_put_contents('example.txt', $_POST['text']."\n", FILE_APPEND); } // Уничтожаем сессию unset($_SESSION['filum']); }
// В скрытое поле и сессию пишем контрольное число $filum = $_SESSION['filum'] = rand(1000, 10000);
// Смотрим, что получилось echo nl2br(htmlspecialchars(@file_get_contents('example.txt'))); ?>
<form action="" method="post"> <input name="text" type="text" /> <input name="filum" type="hidden" value="<?php echo $filum; ?>" /> <input name="ok" type="submit" value="Пуск" /> </form>
|
Я намеренно не стал ставить редирект, что бы можно было увидеть механизм работы скрипта. Сейчас можно попробовать в Опере воспользоваться кнопкой "назад" и посмотреть, возможно ли заняться флудом. В боевом применении редирект обязателен, прошивка формы не спасает от F5, если есть валидация и возврат данных в форму. Она уберегает только от флуда и глюков.
Теперь применительно к блогу, рассматриваемому на наших уроках. Добавляем в форму добавления комментариев скрытое поле:
skins/tpl/blog/form.tpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!-- /skins/tpl/blog/form.tpl begin -->
<?php if(empty($tpl_ready)){ ?>
<br />
<input class="but" name="form[array1][0]" type="submit" value="Написать комментарий">
<?php }else{ ?>
<br />
<?php include IRB_ROOT .'/skins/tpl/bbcode.tpl' ?>
<textarea id="text" name="form[value1]" cols="80" rows="8"></textarea>
<input name="form[value2]" type="hidden" value="<?php echo $tpl_id_parent; ?>" />
<!-- Добавим еще одно скрытое поле для антифлуда -->
<input name="form[value3]" type="hidden" value="<?php echo $tpl_filum; ?>" />
<br />
<input class="but" name="ok" type="submit" value="Отправить" />
<?php } ?>
<!-- /skins/tpl/blog/form.tpl end -->
|
Ну и контрольное число в скрытое поле и сессию:
models/blog_model.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/** * Метод представления. * @param string $template * @param string $mod * @param string $link * @return string */ public function createRows($template, $mod, $link) { global $GET, $POST; $rows = ''; $tpl = getTpl($template); foreach($this->res as $row) { $this->title = !empty($row['m_title'])
? htmlChars($row['m_title']) : ''; $this->keywords = !empty($row['m_keywords'])
? htmlChars($row['m_keywords']) : ''; $this->description = !empty($row['m_description'])
? htmlChars($row['m_description']) : ''; $row['title'] = htmlChars($row['title']); $row['link'] = $link; // Если открыта форма if($row['ready'] = !empty($POST['array1'])) { // Добавим в сессию и скрытое поле рандомное число $row['filum'] = $_SESSION['filum'] = rand(1000, 10000); }
|
А теперь в контроллере в блок добавления комментариев поставим проверку:
controllers/blog_controller.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// Добавим проверку на совпадение контрольного числа if($ok && $POST['value3'] == $_SESSION['filum']) { $error = $comm -> addComment($POST['value1'], $POST['value2'], $_SESSION['userdata']['id'], $_SESSION['userdata']['login'] ); if(empty($error)) {// Если все в порядке, уничтожаем сессионную переменную unset($_SESSION['filum']); // И очищаем форму reDirect(); } } elseif($ok) $info[] = IRB_LANG_ERROR_USERDATA .'<br>';
|
Ну вот и всё, нам не страшен серый волк.
Николай aka twin
Роман