Безопасность SMF

Автор Krazy, 12 мая 2014, 10:27:01

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

Krazy

Безопасность SMF
(Перевод статьи Understanding SMF Security)

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

1. Права доступа (Permissions)

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


  • Права должны учитываться перед отображением страницы. В первую очередь проверьте уровни доступа для пользователей и затем отобразите страницу.

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

2. Проверка сессии

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

2.1. Что такое проверка сессии?

В SMF существует 2 типа ссылок. Первый - это "отображение страницы" для выполнения определенных действий, второй тип - непосредственное "выполнение" определенных действий. 

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

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

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

forum.com/index.php?action=karma;sa=increase;u=156

Предполагается, что эта ссылка увеличит карму пользователя с идентификатором 156. И она прекрасно работает. Когда любой пользователь кликает на ссылке, пользователь 156 получает себе к карме 10 пунктов. Но такая ссылка небезопасна!

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


  • Вставить ложную ссылку: www.google.com,  и тогда любой, кто кликнет по ней, непреднамеренно увеличит карму пользователя 156.
  • Запрашивать изображение в сообщениях: [ img ]forum.com/index.php?action=karma;sa=increase;u=156[ /img ], которое будет отображаться пользователям при посещении ими этой страницы, каждый раз вызывая указанную ссылку и увеличивая карму 156-го пользователя.

Таким образом, чтобы не позволить ввести администратора и пользователей в заблуждение, нештатно вызывая извне такую ссылку, добавляйте после нее "sesc=<PHPSESID>".  Например:
forum.com/index.php?action=karma;sa=increase;u=156;sesc=d113eb64c27777ca1037d8c0ff51ae92

В этом случае каждый пользователь будет иметь свою собственную уникальную ссылку для увеличения кармы. Параметр sesc различен у каждого участника и изменяется каждый раз, когда пользователи логинятся на форуме. Значит вставка ложной гиперссылки www.google.com не будет работать. Такая ссылка сработает только тогда, когда пользователь с идентификатором d113eb64c27777ca1037d8c0ff51ae92 перейдет по ней.

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

2.2. Когда необходимо проверять сессии?

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

Приведем несколько примеров: (Следующие ссылки открывают новое окно перед выполнением фактических действий)


  • переместить тему
  • объединить тему
  • удалить пользователя (в профиле пользователя)
  • отправить сообщение и ответить на сообщения

(Следующие ссылки выполняют свои действия без открытия дополнительных страниц)


  • заблокировать тему
  • удалить тему
  • закрепить тему
  • отметить как прочитанную
  • уведомлять

Попробуйте и вы увидите, что данные ссылки имеют (или не имеют) идентификаторы сессии.

2.3. Что такое сессии формы?

Это второй тип проверок сессий в SMF, который применяется во время вставки и отправки форм таких как форма отправки сообщений или форма редактирования профиля.

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

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

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

Затем вы любым способом убеждаете администратора перейти по этой ссылке. Например так: "Эй, посмотри на это htt======p://mycoolsite.com". И когда администратор переходит на указанную страницу, он видит 2 кнопки: "Мне уже есть 18" и "Мне еще нет 18". Он нажимает на любую из них. И данные из формы отправлены!

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

ВСЕ формы должны содержать проверку сессий! Не существует таких форм, в которых не нужно проверять сессии!

2.4. Как я могу защитить формы?

Просто: используйте сессии формы. Вставляйте значение идентификатора сессии в форму
<input type="hidden" name="sc" value="d113eb64c27777ca1037d8c0ff51ae92" />
Когда форма будет отправлена пользователем, SMF проверит корректное ли значение сессии было получено или нет. Если нет, то SMF не будет выполнять эту функцию. Так как злоумышленник не может предугадать правильное значение идентификатора сессии администратора, то он и не сможет создать фальшивую форму.

2.5. Как выполнить проверку сессии в моем коде?

2.5.1. Использование сессий в URL

При разработке шаблонов или ссылок, которые будут отображаться пользователям, вы должны добавит "sesc=<PHPSESSID>" к ссылке. Этого можно добиться простой строчкой кода:  sesc=' . $context['session_id'] . '

Например:


echo '
<a href="' ,
$scripturl , '?action=karma;sa=increase;u=156;sesc=' , $context['session_id'] , '">' , $txt['increase'] , '</a>';


Или


// (do not forget to define the global $sc)
echo '
<a href="' ,
$scripturl , '?action=karma;sa=increase;u=156;sesc=' , $sc , '">' , $txt['increase'] , '</a>';


Теперь можете убедиться, что этот URL адрес действительно содержит идентификатор сессии.

2.5.2. Использование сессий в формах

Вы должны быть уверены, что форма содержит скрытый элемент между тегами <form> и </form>, который обычно вставлен до или после кнопки "Сохранить". Такой элемент
<input type="hidden" name="sc" value="d113eb64c27777ca1037d8c0ff51ae92" />
может быть вставлен следующим образом.


echo '
<input type="hidden" name="sc" value="', $context['session_id'], '" />';


или


// (do not forget to define the global $sc)
echo '
<input type="hidden" name="sc" value="', $sc, '" />';


Убедитесь, что форма содержит скрытый элемент. Если у вас есть несколько форм на странице, то каждая из них должна иметь свой "sc" элемент. 

2.5.3. Проверка URL сессий

Теперь рассмотрим наиболее важную часть проверки сессий. URL содержит сессию, но настоящая ли эта сессия?

Проверка сессии - это достаточно простой процесс. Вы просто добавляете одну строчку кода checkSession('get'); перед непосредственным выполнением каких-либо действий
Например:


// Assuming that you have already verified that $userID is valid,
function IncreaseKarmaBy10($userID)
{
global $db_prefix;

isAllowedTo('increase_karma'); // Always do the permission check!

checkSession('get'); // Verify that the URL has a "sesc" variable in it.

// Do the actual job
db_query("
UPDATE {$db_prefix}members
SET karmaGood = karmaGood + 10
WHERE ID_MEMBER = $userID
", __FILE__, __LINE__);
}


Функция checkSession('get');

  • проверяет содержит ли URL "sesc",
  • ничего не делает, если URL содержит правильный sesc,
  • выдает сообщение об ошибке "session verification failed" ("не пройдена проверка сессии"), если:

    • URL не содержит sesc
    • URL содержит неправильный sesc.

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

2.5.4. Проверка сессий формы

Проверка того, что форма имеет правильную сессию выполняет также просто, как и в случае с URL. Вы используете  checkSession(); чтобы увидеть, что данные формы содержат скрытый элемент "sc" с корректным значением.

Например:


// Assuming that you have already verified that $userID and $groupID is valid,
function ChangeMemberGroup($userID, $groupID)
{
global $db_prefix;

isAllowedTo('manage_members'); // Always do the permission check!

checkSession(); // Verify that the form has a "sc" element in it.

// Do the actual job
db_query("
UPDATE {$db_prefix}members
SET ID_GROUP = $groupID
WHERE ID_MEMBER = $userID
", __FILE__, __LINE__);
}


Функция checkSession()

  • проверяет содержит ли форма элемент "sc"
  • ничего не делает, если элемент "sc" имеет правильное значение
  • выдает ошибку "session verification failed", если форма:
    • не имеет параметра sc,
    • имеет неверный параметр sc.

При использовании данных систем верификаций сессии ваши формы и ссылки будут более защищенными. Не забывайте, что

  • ВСЕ прямые ссылки, приводящие к каким-либо непосредственным действиям, должны содержать "sesc". Ссылки, открывающие новые окна перед выполнением непосредственных действий могут не содержать  "sesc".
  • ВСЕ формы должны иметь элемент "sc".
  • Идентификатор сессии пользователя не может быть предугадан другим пользователем.

3. Проверки входных данных

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

3.1. Что такое проверка входных данных?

Проверка входных данных - это проверка на то, являются ли отправляемые в SMF данные действительно теми данными, которые ожидаются. Например, вы создали модификацию, увеличивающую карму пользователя на 10. Выполняющая это действие ссылка содержит идентификатор пользователя  forum.com/index.php?action=karma;sa=increase;===u=156===;sesc=d113eb64c27777ca1037d8c0ff51ae92
Вы посылаете идентификатор пользователя в базу данных следующим образом:


$userID = $_REQUEST['u'];
db_query("
UPDATE {$db_prefix}members
SET karmaGood = karmaGood + 10
WHERE ID_MEMBER = $userID
", __FILE__, __LINE__);


Но переменная $_REQUEST['u'] безопасна? Подумайте над следующим URL

forum.com/index.php?action=karma;sa=increase;===u=156+OR+1===;sesc=d113eb64c27777ca1037d8c0ff51ae92

Подстрока u=156+OR+1 повлечет за собой присвоение $_REQUEST['u'] = "156 OR 1". И когда это значение будет отправлено в базу данных, то выполнится следующий запрос:


"
UPDATE {$db_prefix}members
SET karmaGood = karmaGood + 10
WHERE ID_MEMBER = 156 OR 1
"


Результатом условия "OR 1" будет увеличение кармы у каждого пользователя форума, а не только у 156-го. Рассмотрим привет: у вас есть модификация, которая хранит дополнительное поле профиля в таблице пользователей. В этом поле указано название вашей любимой игры. Вы посылаете данные используя форму в вашем профиле. И эта форма отправляет $_POST['favorite_game'] в качестве входного параметра.

Вот пример вашего кода:


db_query("
UPDATE {$db_prefix}members
SET favoriteGame = '$_POST[favorite_game]'
WHERE ID_MEMBER = $ID_MEMBER", __FILE__, __LINE__);


Как это может быть использовано? В качестве входного параметра может быть отправлен вредоносный код, например так: CounterStrike', ID_GROUP = '1

Теперь вставьте это в запрос:


UPDATE {$db_prefix}members
SET favoriteGame = 'CounterStrike', ID_GROUP = '1'
WHERE ID_MEMBER = 156


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

В первом случае входной параметр "u" не был целочисленным значением. Во втором случае  "favorite_game" не был должным образом вставлен в запрос.

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


  • Целочисленные значения должны быть целочисленными значениями $_REQUEST['u'] = (int) $_REQUEST['u'];
  • Строки должны быть экранированы. По необходимости используйте
    $_POST['favorite_game'] = htmlspecialchars($_POST['favorite_game']);
    $_POST['favorite_game'] = addslashes($_POST['favorite_game']);
    $_POST['favorite_game'] = stripslashes($_POST['favorite_game']);
    $_POST['favorite_game'] = un_htmlspecialchars($_POST['favorite_game']);

Последняя функция определена в самом SMF. Она похожа на htmlspecialchars_decode в PHP. Смотрите определение функции на php.net для получения более подробной информации.

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

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