Статьи Шифрование имен полей формы

Discussion in 'Статьи' started by GreenBear, 17 May 2006.

  1. GreenBear

    GreenBear наркоман с медалью

    Joined:
    7 May 2005
    Messages:
    2,547
    Likes Received:
    1,398
    Reputations:
    612
    Большинство спамерских программ-авторассыльщико работают по следущему принципу:
    1. В базу заносятся адрес скрипта, обрабатывающего сообщение, подаваемое на доску/форум/гостевую и т.д., а так-же названия полей в форме и их описание
    2. Авторассыльщик отправляет запросы на адреса из базы с данными полей.

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

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

    Инклюдный файл инициализации/реинициализации ключей (листинг "InitKeys.inc.php"):

    PHP:
    <?php
    $_SESSION
    ['Keys[1]'] = rand(1000,9999);
    $_SESSION['Keys[2]'] = rand(1000,9999);
    $_SESSION['Keys[3]'] = rand(10,99);
    $_SESSION['Keys[4]'] = rand(65,90);
    ?>
    Количество ключей может быть любым (в пределах разумного), но 4 ключа думаю вполне хватит, т.к. подобрать их очень сложно (учитывая еще время доступа к серверу, которое всегда немного, но отличается, вследствии чего подобрать последовательность генерации практически невозможно)
    Четвертый ключ нужен для того, чтобы имя переменной было правильным, но об этом пониже.

    Исходник функции расширенного хеширования строки (имени поля формы)("Encrypt.inc.php"):
    PHP:
    <?php
    function EncryptField($Source)
    {
    $Source chr($_SESSION['Keys[4]']).md5(crypt($_SESSION['Keys[1]'].$Source.$_SESSION['Keys[2]'],$_SESSION['Keys[3]']));
    return 
    $Source;
    }
    ?>
    $Source = chr($_SESSION['Keys[4]'])
    Эта конструкция добавляет в начало сгенерированной строки символ латинского алфавита, чтобы не было такой ситуации, когда имя переменной начинается с цифры, что является неправильным именем переменной и могло бы вызвать ошибку времени исполнения
    После обработки этой функцией строки, содержащей имя переменной формы, получается шифрованная необратимым способом строка (например, "U242908f3ba2cc1e35faac91c45de38fc")
    Расшифровать ее очень сложно, т.к. ключи находятся на сервере и не передаются клиенту.

    Теперь о применении этого метода:
    Есть два способа обработки формы.
    Первый - форму (например, файл "Form.php") обрабатывает другой скрипт (например, "Handler.php").
    И второй - скрипт обработки включен в файл с формой (физически или же инклюдом внешнего файла)

    Вначале первый способ.

    Листинг страницы формы ("Form.php"):
    PHP:
    <?php
    session_start
    (); // Инициализация сессии
    include_once("InitKeys.inc.php"); // Инициализация ключей (реинициализация при каждой перегузки страницы)
    include_once("Encrypt.inc.php"); // Объявление функции хеширования
    ?>

    <form action="Handler.php" method="POST">
    <input type="text" name=<? echo '"'.EncryptField("Name").'"' ?>> // Именем поля формы будет хеш от строки "Name"
    <input type="text" name=<? echo '"'.EncryptField("Message").'"' ?>> // Именем поля формы будет хеш от строки "Message"
    <input type="submit">
    </form>
    Листинг обработчика ("Handler.php"):
    PHP:
    <?php
    session_start
    (); // Инициализация сессии
    if (!isset($_SESSION['Keys[1]']))
    {
    // Ключи не были инициированы, что означает то, что было прямое обращение к скрипту обработчика.
    // Здесь можно просто завершить работу скрипта, или сохранить в логах содержимое пост-массива, ип адрес, дату и т.д.
    }
    include_once(
    "Encrypt.inc.php"); // Объявление функции хеширования
    $DEC_N EncryptField("Name"); // Находим хеш от строки "Name" при данном наборе ключей
    $DEC_M EncryptField("Message"); // Находим хеш от строки "Message" при данном наборе ключей
    include("InitKeys.inc.php"); // Реинициализируем ключи, чтобы избежать повторного обращения к скрипту с теми же ключами, но в обход вызывающего скрипта Form.php
    if ((!isset($_POST[$DEC_N])) OR (!isset($_POST[$DEC_M]))) // Проверяем, есть ли в пост-массиве переменные с найденными хешами
    {
    // Нет таких переменных (или одной из них). Это свидетельствует о двух ситуациях: Либо это уже не первое обращение к скрипту напрямик, либо просто кто-то несколько раз нажимает на кнопку отправки запроса на страницу "Form.php"
    // Можно тоже либо просто завершить работу, либо ничего не делать ,т.е. не записывать сообщение в базу (а вдруг это просто пльзователи, которые нажали несколько раз на кнопку отправки запроса), либо заносим в лог.
    }
    else
    {
    // Данные пришли через форму, и можно их обрабатывать так, как надо, т.е. запрос прошел проверку
    }
    ?>
    Теперь второй способ (Форма и обработчик формы в одном файле)
    Отличие этого способа от предыдущего в том, чтобы избежать двойной реинициализации ключей при отправке запроса

    Листинг файла "FormAndHandler.php":
    PHP:
    <?
    session_start();
    if (
    $_SERVER["REQUEST_METHOD"] != "POST"// Инициализируем ключи только в том случае, если не было запроса
    include_once("InitKeys.inc.php");
    include_once(
    "Encrypt.inc.php");
    ?>
    // Дальше идет содержимое страницы
    ...
    ...
    // А тут идет форма
    <form action="Handler.php" method="POST">
    <input type="text" name=<? echo '"'.EncryptField("Name").'"' ?>>
    <input type="text" name=<? echo '"'.EncryptField("Message").'"' ?>>
    <input type="submit">
    </form>
    .... // Тоже все, что угодно...
    // Тут обработчик формы (или инклюд на обработчик) (он может идти и в самом начале файла, кому как нравится)
    <?
    $DEC_N = EncryptField("Name"); // Находим хеш от строки "Name" при данном наборе ключей
    $DEC_M = EncryptField("Message"); // Находим хеш от строки "Message" при данном наборе ключей
    include("InitKeys.inc.php"); // Реинициализируем ключи
    if ((!isset($_POST[$DEC_N])) OR (!isset($_POST[$DEC_M]))) // Проверяем, есть ли в пост-массиве переменные с найденными хешами
    {
    // Нет таких переменных (или одной из них).
    // Можно тоже либо просто завершить работу, либо ничего не делать ,т.е. не записывать сообщение в базу, либо заносим в лог.
    }
    else
    {
    // Данные пришли через форму, и можно их обрабатывать так, как надо, т.е. запрос прошел проверку
    }
    ?>
    Теперь о уязвимости данного способа (а точнее о том, как его можно обойти): для этого нужно при каждом обращении парсить страницу и извлекать названия полей форм. Но во-первых, это накладно с точки зрения затрат времени и трафика, от чего спамеры не прийдут в восторг, и во-вторых, эту уязвимость тоже можно устранить.
    Автор: Xander, http://tsbs.ru/forum/index.php?showuser=220
     
  2. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    Интересная статейка. При использовании с другими приемами может неплохо защитить от разных спамеров и флудеров.
     
  3. censored!

    censored! Green member

    Joined:
    2 Nov 2004
    Messages:
    1,160
    Likes Received:
    299
    Reputations:
    156
    Проще не на поля, а на кнопку Субмит ключи вешать. Плюс - чтобы она скакала (меняла свое положение).
    Тогда обходим и прямой посыл пакета, и клики в одно и тоже место.

    Так делает прога ForumPoster (оч. хороша для спама гостевых на Народ_Ру).

    Но пока самый эффективный способ - это защита изображением с кодом. Правда - и от изображения зависит. Где-то просто числа - которые легко распознаются, а где-то - не то что программа, сам с трудом глазами распознаешь.
    И еще более эффектный - так это стиль этого изображения менять раз в неделю. Чтобы не поспевали "распознавальщик" переписывать.
     
    _________________________
    #3 censored!, 17 May 2006
    Last edited: 17 May 2006