Безопасный метод авторизации на PHP

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

1. Модель (клиент) 
Регистрация
— логин (a-z0-9)
— пароль
Вход
— логин
— пароль
Cookie
— уникальный идентификатор юзера
— хэш
Модель (сервер) MySQL
Таблица users
user_id (int(11))
user_login (Varchar(30))
user_password (varchar(32))
user_hash (varchar(32))
user_ip (int(10)) по умолчанию 0При регистрации в базу данных записываеться логин пользователя и пароль(в двойном md5 шифровании)

При авторизация, сравниваеться логин и пароль, если они верны, то генерируеться случайная строка, которая хешируеться и добавляеться в БД в строку user_hash. Также записываеться IP адрес пользователя(но это у нас будет опциональным, так как кто-то сидит через Proxy, а у кого-то IP динамический… тут уже пользователь сам будет выбирать безопасность или удобство). В куки пользователя мы записываем его уникальный индетификатор и сгенерированный hash.

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

2. Практика
--
— Структура таблицы `users`

CREATE TABLE `users` (
`user_id` int(11) unsigned NOT NULL auto_increment,
`user_login` varchar(30) NOT NULL,
`user_password` varchar(32) NOT NULL,
`user_hash` varchar(32) NOT NULL,
`user_ip` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ;

register.php 
<?
// Страница регситрации нового пользователя

# Соединямся с БД
mysql_connect(«localhost»«myhost»«myhost»);
mysql_select_db(«testtable»);

if(isset($_POST[‘submit’]))
{
$err = array();

# проверям логин
if(!preg_match(«/^[a-zA-Z0-9]+$/»,$_POST[‘login’]))
{
$err[] = «Логин может состоять только из букв английского алфавита и цифр»;
}

if(strlen($_POST[‘login’]) < or strlen($_POST[‘login’]) > 30)
{
$err[] = «Логин должен быть не меньше 3-х символов и не больше 30»;
}

# проверяем, не сущестует ли пользователя с таким именем
$query mysql_query(«SELECT COUNT(user_id) FROM users WHERE user_login='».mysql_real_escape_string($_POST[‘login’]).«‘»);
if(
mysql_result($query0) > 0)
{
$err[] = «Пользователь с таким логином уже существует в базе данных»;
}

# Если нет ошибок, то добавляем в БД нового пользователя
if(count($err) == 0)
{

$login $_POST[‘login’];

# Убераем лишние пробелы и делаем двойное шифрование
$password md5(md5(trim($_POST[‘password’])));

mysql_query(«INSERT INTO users SET user_login='».$login.«‘, user_password='».$password.«‘»);
header(«Location: login.php»); exit();
}
else
{
print 
«<b>При регистрации произошли следующие ошибки:</b><br>»;
foreach(
$err AS $error)
{
print 
$error.«<br>»;
}
}
}
?>

<form method=»POST»>
Логин <input name=»login» type=»text»><br>
Пароль <input name=»password» type=»password»><br>
<input name=»submit» type=»submit» value=»Зарегистрироваться»>
</form>

login.php

<?
// Страница авторизации

# Функция для генерации случайной строки
function generateCode($length=6) {
$chars «abcdefghijklmnopqrstuvwxyzABCDEFGHI JKLMNOPRQSTUVWXYZ0123456789»;
$code «»;
$clen strlen($chars) — 1;  
while (strlen($code) < $length) {
$code .= $chars[mt_rand(0,$clen)];  
}
return 
$code;
}

# Соединямся с БД
mysql_connect(«localhost»«myhost»«myhost»);
mysql_select_db(«testtable»);

if(isset($_POST[‘submit’]))
{
# Вытаскиваем из БД запись, у которой логин равняеться введенному
$query mysql_query(«SELECT user_id, user_password FROM users WHERE user_login='».mysql_real_escape_string($_POST[‘login’]).«‘ LIMIT 1»);
$data mysql_fetch_assoc($query);

# Соавниваем пароли
if($data[‘user_password’] === md5(md5($_POST[‘password’])))
{
# Генерируем случайное число и шифруем его
$hash md5(generateCode(10));

if(!@$_POST[‘not_attach_ip’])
{
# Если пользователя выбрал привязку к IP
# Переводим IP в строку
$insip «, user_ip=INET_ATON(‘».$_SERVER[‘REMOTE_ADDR’].«‘)»;
}

# Записываем в БД новый хеш авторизации и IP
mysql_query(«UPDATE users SET user_hash='».$hash.«‘ «.$insip.» WHERE user_id='».$data[‘user_id’].«‘»);

# Ставим куки
setcookie(«id»$data[‘user_id’], time()+60*60*24*30);
setcookie(«hash»$hashtime()+60*60*24*30);

# Переадресовываем браузер на страницу проверки нашего скрипта
header(«Location: check.php»); exit();
}
else
{
print 
«Вы ввели неправильный логин/пароль»;
}
}
?>
<form method=»POST»>
Логин <input name=»login» type=»text»><br>
Пароль <input name=»password» type=»password»><br>
Не прикреплять к IP(не безопасно) <input type=»checkbox» name=»not_attach_ip»><br>
<input name=»submit» type=»submit» value=»Войти»>
</form>

check.php

<?
// Скрипт проверки

# Соединямся с БД
mysql_connect(«localhost»«myhost»«myhost»);
mysql_select_db(«testtable»);

if (isset($_COOKIE[‘id’]) and isset($_COOKIE[‘hash’]))
{
$query mysql_query(«SELECT *,INET_NTOA(user_ip) FROM users WHERE user_id = ‘».intval($_COOKIE[‘id’]).«‘ LIMIT 1»);
$userdata mysql_fetch_assoc($query);

if(($userdata[‘user_hash’] !== $_COOKIE[‘hash’]) or ($userdata[‘user_id’] !== $_COOKIE[‘id’])
or ((
$userdata[‘user_ip’] !== $_SERVER[‘REMOTE_ADDR’])  and ($userdata[‘user_ip’] !== «0»)))
{
setcookie(«id»«»time() — 3600*24*30*12«/»);
setcookie(«hash»«»time() — 3600*24*30*12«/»);
print 
«Хм, что-то не получилось»;
}
else
{
print 
«Привет, «.$userdata[‘user_login’].«. Всё работает!»;
}
}
else
{
print 
«Включите куки»;
}
?>

Для защиты формы логина от перебора, можно использовать капчу.

Хочу отметить, что здесь я рассматривал авторизацию основоную на cookies, не стоит в комментариях кричать, что сессии лучше/удобнее и т.д. Спасибо.

Примечания:
1. mysql_select_db(«testtable»); // нелогичное название базы, testdb лучше
2. Нужно использовать mysql_real_escape_string()
3. Убирать лишние пробелы нет смысла, так как их не пропустит вот эта строка:
if(!preg_match(«/^[a-zA-Z0-9]+$/»,$_POST[‘login’]))

sipp

sipp – мощная утилита для генерации нагрузки на SIP оборудование. Обычно sipp используется для проверки отказоустойчивости систем IP-телефонии, выявления максимально-допустимой нагрузки или ddos-а конкурентов 🙂 Сценарий сессии в sipp описывается в XML файле. Можно воспользоваться одним из множества сценариев распространяемых в комплекте с sipp или создать свой.

Кроме тестирования сигнализации (SIP) sipp способен тестировать и медиа нагрузку. Для этого существуют два модуля: PCAP play и RTP echo. PCAP play – проигрывает заранее записанный сетевым анализатором (например wireshark) медиа файл. RTP echo – позволяет sipp отсылать обратно все полученные RTP потоки.

Пример использования sipp

 sipp 10.10.10.1 -s 12345 -i 10.10.10.2 -d 2h -l 60 -aa -mi 10.10.10.2 -rtp_echo -nd -r 10

10.10.10.1 – IP адрес SIP сервера, на который следует слать запросы.

-s 12345 – Указывает номер который будет вызван. Может быть числом или текстом. Значение по умолчанию – service

-i 10.10.10.2 – Локальный IP адрес. Этот адрес будет использован в SIP сообщениях в качестве адреса источника сообщений. По умолчанию используется адрес 127.0.0.1.

-d 2h – Устанавливает длительность звонков. В данном случае звонки будут длиться 2 часа. Длительность по умолчанию – 1 секунда.

-l 60 – Ограничивает максимальное количество одновременных звонков – 60.

-aa – Включает автоматические ответы 200 OK на сообщения INFO, UPDATE и NOTIFY.

-mi 10.10.10.2 – Устанавливает локальный IP для RTP.

-rtp_echo – Включает режим RTP эха. Все RTP пакеты полученные от удалённой стороны – отправляются обратно.

-nd – Отключает стандартную обработку неожиданных ситуаций – sipp будет прерывать звонки в случае получения неправильных SIP сообщений.

-r 10 – Устанавливает максимальную «скорость звонков» (CPS) в данном случае – не более 10 звонков в секунду.

Максимальной скоростью вызовов можно управлять во время работы sipp с помощью клавиш «+» и «-» – повышая и понижая её соответственно. Вообще, опустив параметры -aa -mi 10.10.10.2 -rtp_echo -nd – мы получаем отличное средство для тестирования отказоустойчивости и максимального CPS у SIP proxy.

Настройка Asterisk

Для того, что бы Asterisk принимал звонки от sipp, необходимо создать в SIP.conf специальный SIP-peer с именем sipp. К сожалению, заставить sipp совершать вызовы от имени существующего пользователя – нельзя. В стандартных сценариях sipp всегда представляется как sipp. Добавляем в SIP.conf запись:

 [sipp]
 type=friend
 context=in
 username=sipp
 host=10.10.10.2
 dtmfmode=rfc2833
 disallow=all
 allow=ulaw,alaw
 insecure=port,invite

Важными моментом, является наличие кодека ulaw в списке разрешенных т.к. именно его анонсирует sipp. Если 711u не будет в списке разрешённых кодеков, то Asterisk отклонит вызов от sipp. Вторым важным моментом, является строка insecure=port,invite. Данная строка заставляет Asterisk авторизовать sipp не по паролю, а по IP адресу указанному в поле host. Кроме записи в SIP.conf, можно создать специальный контекст в extensions.conf для обработки тестовых звонков от sipp.

Следующий пример принимает звонки на «номер» service – именно этот идентификатор используется по умолчанию:

 [in]
 exten => service,1,MusicOnHold()

Вот и всё. Успехов в стресс тестах! 🙂

P.S. Документация по sipp — http://sipp.sourceforge.net/doc3.0/reference.html

GRUB 2 — ошибка 15

Изредка, особенно при невнимательной настройке загрузчика grub2 возникает ситуация, при которой система полностью отказывается грузиться, а на экране консоли возникает ошибка — “Error 15: File not found!”. Для решения проблемы необходимо внести изменения в файл /boot/grub/menu.lst, указав правильный раздел для загрузки — раздел root.

С начала загружаемся с Live CD и открываем терминал.

Проверяем, какие разделы доступны в системе:

$ sudo fdisk -l | grep -i linux

Обычно это раздел системы и swap-раздел. Нас интересует раздел, содержащий /root.

/dev/hda5  1276  1397  979902    82  Linux swap / Solaris
/dev/hda6  1398  2550  9261441   83  Linux

/dev/hda6 — в нашем примере это раздел root. /dev/hda6 прописывается в grub, как (hd0,5). К примеру, если ваша система находится в /dev/hda1, то при загрузке нужно прописывать — (hd0,0). В grub прописывается значение на единицу меньшее, чем указано в разделе.

Откройте файл menu.lst и проверьте, что /dev/hda6 и (hd0,5) соответствуют разделу root, и никаких лишних разделов не указано. Это основная причина появления ошибки 15. В нашем примере root-раздел должен соответствовать разделу /dev/hda6, ниже указан пример файла /boot/grub/menu.lst:

title       Debian GNU/Linux, kernel 2.6.26-2-686
root        (hd0,5)
kernel      /boot/vmlinuz-2.6.26-2-686 root=/dev/hda6 ro
initrd      /boot/initrd.img-2.6.26-2-686
title       Debian GNU/Linux, kernel 2.6.26-2-686 (single-user mode)
root        (hd0,5)
kernel      /boot/vmlinuz-2.6.26-2-686 root=/dev/hda6 ro single
initrd      /boot/initrd.img-2.6.26-2-686

Далее желательно проверить файл /etc/fstab на соответствие разделов и типов файловых систем:

# /etc/fstab: static file system information.
#
# <file system> <mount point>  <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults          0     0
/dev/hda6       /               ext3    errors=remount-ro 0     1
/dev/hda5       none            swap    sw                0     0
/dev/hdc        /media/cdrom0   udf,iso9660 user,noauto   0     0

После перезагрузки, система должна нормально загрузиться.

Переустановка загрузчика

После загрузки LiveCD, открываем терминал и монтируем разделы системы:

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

# fdisk -l

Монтирование обычных систем:

# mount /dev/sda2 /mnt/

Если /boot примонтирован отдельно:

# mount /dev/sda1 /mnt/boot

Монтирование специальных систем:

# mount --bind /dev /mnt/dev
# mount --bind /dev/pts /mnt/dev/pts
# mount --bind /proc /mnt/proc
# mount --bind /sys /mnt/sys

Заходим в chroot-систему:

# chroot /mnt

Если отсутствует файл /boot/grub/grub.cfg, или есть проблемы с настройкой:

# update-grub

Переустанавливаем GRUB2 :

# grub-install /dev/sda

(Внимание! Номер после названия раздела не пишется!)

Проверяем правильность настроек:

# grub-install --recheck /dev/sda

Выходим из chroot-системы:

# exit

# umount /mnt/sys
# umount /mnt/proc
# umount /mnt/dev/pts
# umount /mnt/dev
# umount /mnt/boot
# umount /mnt

# reboot

После перезагрузки всё должно заработать! 🙂

 

Создание RSS-канала


RSS-канал — это простой текстовый файл. Общая структура такая:

<?xml version="1.0" ?>
<rss version="2.0">
<channel>

</channel>
</rss>

1. Определите содержимое тега channel.

Содержимое сhannel может быть одинаковым для всех RSS-каналов Вашего сайта.

Эти теги должны быть обязательно:

title: название канала, может быть таким же, как название главной страницы сайта.
link: URL сайта, например: www.891rpm.arthead.ru/.
description: описание Вашего канала, примерно 200 символов.

<channel>
 <title>One title</title>
 <link>http://www.891rpm.arthead.ru/</link>
 <description>описание Вашего канала</description>
</channel>

2. Добавьте изображение.

Это необязательно, но желательно.

Сделайте небольшое изображение (например, 88×31) в формате gif или jpg или png и сохраните в том же каталоге, что RSS файл.

url — это адрес самого изображения.
link — адрес страницы которая откроется, если кликнуть по изображению.

<image>
 <url>http://www.891rpm.arthead.ru/i/a.png</url>
 <title>Fisrt title</title>
 <link>http://www.891rpm.arthead.ru/</link>
</image>

3. Добавьте новый элемент.

Теперь мы добавим информацию о веб-странице.

Это тег item, подэлемент тега channel. В RSS-канале должен быть как минимум один такой элемент:

title: название статьи.
link: URL веб-страницы.
description: описание этой веб-страницы, около 200 символов.

<item>
<title>Master title</title>
<link>http://www.891rpm.arthead.ru/master/</link>
<description>Master test ))</description>
<guid>http://www.891rpm.arthead.ru/master/</guid>
</item>

4. Добавьте ещё несколько подобных элементов.

Можно добавить ещё несколько подобных элементов, обычно, не более 10.

5. Загрузите файл на сервер

Поместите сделанный файл rss.xml на сайт.

6. Проверка файла

Вы можете использовать интерактивный валидатор RSS-канала. Для этого существует неплохой сервис.

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

7. Сделайте его доступным

Для этого надо добавить текстовую или графическую ссылку на главную сраницу Вашего веб-сайта. При клике по ссылке должен открываться Ваш файл RSS-канала.

Например так:

<a type="application/rss+xml" href="http://www.891rpm.arthead.ru/rss.xml">
 RSS-канал этого сайта
 </a>

8. Обновление канала

При публикации новых веб-страниц на сайте, просто добавляйте в RSS-канал новые элементы и удаляйте старые, оставляя их количество неизменным (например, 10 или 15).

Пример полного файла RSS-канала.

<?xml version="1.0" encoding="windows-1251"?>
<rss version="2.0">
<channel>
 <title>Development on 891rpm.arthead.ru</title>
 <link>http://www.891rpm.arthead.ru/</link>
 <description>Сайт разработчика и администратора.</description>
 <language>en-ru</language>
 <copyright>2011. Copyright by 891rpm.arthead.ru</copyright>
 <image>
  <url>http://www.891rpm.arthead.ru/i/a.png</url>
  <title>First title</title>
  <link>http://www.891rpm.arthead.ru/</link>
 </image>
 <item>
  <title>Second title</title>
  <link>http://www.891rpm.arthead.ru/feed/</link>
  <description>Many, many any text's ))</description>
  <guid>http://www.891rpm.arthead.ru/feed/</guid>
 </item>
</channel>
</rss>

Как браузеры узнают, что на сайте есть RSS-канал?

Вы создали RSS-канал и сохранили его в каком-нибудь каталоге вашего сайта.
Вы должны сообщить браузеру, когда он откроет страницу вашего сайта, о наличии этого файла и его местоположении. Тогда Firefox будет показывать значок RSS-канала в адресной строке, Internet Explorer на панели команд.

Чтобы активировать их, надо добавить следующую строку в исходный код страницы, в любом месте внутри секции <head> </head>:

 <link
 rel="alternate"
 type="application/rss+xml"
 href="http://www.891rpm.arthead.ru/rss.xml"
 title="Название канала"
 />

Не забудьте заменить в URL www.891rpm.arthead.ru на имя Вашего домена и путь к Вашему файлу и его имя.

Настройка RSS-ленты в WordPress

Сегодня мы с Вами ознакомимся с настройками RSS-ленты в WordPress, а также с процессом настройки трансляции RSS-ленты на сервис FeedBurner.Com. Сервис Feedburner, позволяет собирать статистику подписчиков на  RSS-ленту блога, транслировать ее в различных форматах, организовать подписку на  обновления блога через E-Mail и многое другое.

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

Сначала необходимо зарегистрироваться на сервисе, для этого заходим на сайт FeedBurner.com, в разделе “Языки” сразу установим “Русский”, чтобы процесс настройки был понятнее и проще.

После этого мы попадаем на главную страницу сервиса с формой для прожигания RSS. В форму вводим адрес нашей RSS-ленты. Если Вы настраивали, “Постоянные ссылки” в Вашем блоге, значит адрес RSS-ленты будет выглядеть примерно так: http://test.blogmir.ru/feed/ именно этот адрес и нужно вводить в форму.

Если Вы планируете публиковать подкасты на Вашем блоге, можете установить галочку “Я подкастер!”, нажимаем кнопку “Далее”, попадаем на форму регистрации на сервисе:

После нажатия на кнопку “Activate Feed” попадаем на страницу с сообщением о том, что RSS-лента активирована, также на нам сообщают адрес нашей RSS-ленты на сервисе FeedBurner.

Адрес этот необходимо запомнить, чтобы в дальнейшем настроить переадресацию всех наших подписчиков, на  FeedBurner.

Нажимаем кнопку “Next”, страница предварительной настройки RSS-ленты, в зависимости от Ваших предпочтений, можете включать или не включать дополнительные сервисы статистики.

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

Открываем закладку “Оптимизируй”, пункт “Browser Friendly”, изменяем язык интерфейса на ClearFeed (Russian)

Не забываем сохранять настройки.

Пункт “SmartFeed” — нажимаем кнопку “Активировать”:

Переходим на закладку “Публикуй”, пункт “FeedCount”, нажимаем кнопку “Активировать”. Вы можете выбрать тип счетчика и его цвет, в поле будет сгенерирован код, который можно вставить в шаблон блога.

После того как Вы выбрали цвет и тип счетчика, нажимаем на кнопку “Сохранить”.

Открываем пункт “Подписки по электронной почте”, в списке провайдеров выбираем FeedBurner

Нажимаем кнопку “Активировать”, после этого появится окошко с кодом формы для подписки на  RSS-ленту по E-Mail, в списке необходимо выбрать русский язык и в самом низу нажать кнопку “Сохранить”

Полученный код можно вставить в шаблон блога для отображения формы подписки на  RSS через E-Mail.

На этом настройку RSS-ленты можно считать законченной.

Теперь для настройки автоматической переадресации Ваших подписчиков на RSS-ленту, настроенную на FeedBurner, необходимо установить и настроить плагин WordPress, который будет этим заниматься.

Скачиваем плагин, загружаем его на сервер в папку /wp-content/plugins/ и активируем в панели администрирование блога, раздел “Плагины.

Переходим в раздел “Параметры” — “FeedBurner”, в поле вводим адрес, который нам выдал FeedBurner, должно получится вот так:

Нажимаем кнопку “Save”, теперь все, кто будут обращаться к Вашему стандартному адресу RSS-ленты, вида http://myblog.com/feed/ будут автоматически переадресовываться на сервис FeedBurner для подписки.

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

Работа с tcpdump

Tcpdump чрезвычайно удобный сетевой анализатор, очень помогающий в работе как сетевым администраторам, так и безопасникам. Естественно что для получения максимальной информации при работе с tcpdump, просто необходимо иметь представления о стеке протоколов TCP/IP. Для удобства можно использовать более удобные и интеллектуальные программы, например Wareshark, но часто возникают ситуации когда на тестируемую машину не представляется возможным установить дополнительные сервисы, и тогда tcpdump просто незаменим, не будит же админ, ради анализа пакетов, ставить на unix’овый сервак X-Windows тем более что в большинстве unix’овых систем, утилита tcpdump идет по умолчанию.
Понимание протокола TCP/IP дает широкое пространство для использование анализатора и устранения неисправностей и неполадок в работе сети, за счет разбора пакетов. Поскольку оптимальное использование данной утилиты требует хорошего понимания сетевых протоколов и их работы, то получается забавная ситуация, в которой инженеру в любом случае необходимо знать и понимать механизмы передачи данных в сети. т.ч. tcpdump полезна во все отношениях: как устранения неисправностей, так и самообразования.

Ниже приводятся несколько опций, которые помогут наиболее полно и подробно использовать эту утилиту, тем более что все эти опочки и ключики довольно легко забываются, и данные примеры весьма полезны, для освежевания памяти.
Первый используемый ключик -n который запрещает попытки преобразования адресов в доменные имена, тем самым выдавая нам чистые IP адреса с портами.
Второй это -X который задает для каждого пакета вывод как hex (шестнадцатеричная система) так и ASCII содержимого пакета.

И последняя опция это -S которая заменяет вывод относительной TCP нумерации, на абсолютную. Смысл в том, что при относительной нумерации некоторые проблемы могут скрыться от вашего внимания.

Нужно понимать, что основное преимущество tcpdump перед другими утилитами заключается в том, что в ней возможен подробный и ручной разбор пакетов. Также нужно помнить что по умолчанию tcpdump использует только первые 68 байт пакета, т.ч. если необходимо видеть больше, то следует использовать ключ -s number , где number количество байт которые следует захватить. В случае задания number 0 , произойдет полный захват пакета, поэтому лучше использовать значение 1514, что даст полный захват стандартного, для сетей Ethernet, пакета.

Список наиболее часто используемых ключей:

  • -c : задается проверка размера файла захвата перед каждой очередной записью захваченного пакета, если размер больше, то файл сохраняется и запись идет в новый файл
  • -e : выводится ethernet заголовок (канальный уровень) в каждой строке дампа
  • -i any : прослушивание всех интерфейсов, на случай если вам необходим весь трафик.
  • -n : запрещает преобразование адресов в доменные или символьные имена
  • -nn : запрещает преобразование адресов и портов в доменные или символьные имена
  • -q : Красткий вывод информации, за счет уменьшения вывода информации о протоколе.
  • -X : выводит как hex так и ASCII содержимое пакета
  • -v, -vv, -vvv : задает вывод дополнительной информации о захваченных пакетах, что дает возможность более широкого анализа.

Несколько примеров для использования:

# Стандартный вывод пакетов
 tcpdump -nS
 # Расширенный стандартный вывод
 tcpdump -nnvvS
 # Глубокий разбор пакета
 tcpdump -nnvvXS
 # Наиболее подробная информация о трафике
 tcpdump -nnvvXSs 1514

Выражения позволяют производить более целевое сканирование и задавать типы трафика. Умение использовать выражения делает tcpdump весьма продуктивным инструментом, в рукам сисадмина. Существует три основных типа выражений: type, dir, and proto.

Опции выражения type бывают host, net и port.

Для выражения направления задаваемого dir существующие опции src, dst, src or dst, и src and dst.

Несколько стандартных выражений:

host // анализ трафика на основе IP адреса ( также работает с символьными именами, если не задано -n)

 tcpdump host 1.2.3.4

src, dst // анализ трафика только для определенного назначения или передатчика

 tcpdump src 2.3.4.5
 tcpdump dst 3.4.5.6

net // захват трафика принадлежащего определенной сети

 tcpdump net 1.2.3.0/24

proto // работает с tcp, udp, и icmp. Нужно помнить что proto не упоминается

 tcpdump icmp

port // анализ трафика с определенного порта (входящего или исходящего)

 tcpdump port 3389

src, dst port // фильтр базируется на входящем или исходящем порту

 tcpdump src port 1025
 tcpdump dst port 3389

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

AND
and or &&
OR
or or ||
EXCEPT
not or !

TCP трафик с ресурса 10.15.123.33 с портом назвачения 3379:

# tcpdump -nnvvS tcp and src 10.15.123.33 and dst port 3379

Трафик переходящий из сети 192.168 с назначением на сети 10 или 172.16:

# tcpdump -nvX src net 192.168.0.0/16 and dst net 10.0.0.0/8 or 172.16.0.0/16

Non-ICMP трафик с точкой назначения 192.168.0.2 и из сети 172.16:

# tcpdump -nvvXSs 1514 dst 192.168.0.2 and src net 172.16.0.0/16 and not icmp

Трафик с хостов Eros or Ares , но не идущий на стандартный SSH порт (для данного выражения требуется разрешение имен):

# tcpdump -vv src eros or ares and not dst port 22

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

Также следует помнить о том, что мы можем строить запросы, включающие группы и пулы опций, заданных в одном запросе. Но для того чтобы утилита tcpdump обращала на них внимание их нужно помещать в скобки, беря выражение в одиночные кавычки:
Трафик идущий с хоста 10.0.2.4 на порты назначения 3379 или 22:

 # tcpdump ’src 10.0.2.4 and (dst port 3379 or 22)’

Также возможно производить фильтрацию на основе TCP флагов, например для вычленения SYN или RST пакетов:

Анализировать все URG пакеты:

 # tcpdump ‘tcp[13] & 32 != 0′

Анализировать все ACK пакеты:

 # tcpdump ‘tcp[13] & 16 != 0′

Анализировать все PSH пакеты:

 # tcpdump ‘tcp[13] & 8 != 0′

Анализировать все RST пакеты:

 # tcpdump ‘tcp[13] & 4 != 0′

Анализировать все SYN пакеты:

 # tcpdump ‘tcp[13] & 2 != 0′

Анализировать все FIN пакеты:

 # tcpdump ‘tcp[13] & 1 != 0′

Анализировать все SYN-ACK пакеты:

 # tcpdump ‘tcp[13] = 18′

Нужно помнить что фильтр обрабатывает данный запрос следующим образом, проверяет 13 байт заголовка TCP пакета и заданный байт флага, на соответствие условию, например:

 tcpdump ‘tcp[13] & 2 != 0′

– просмотр и сравнение 13 и 2 байтов заголовка, на условие что данные не равны 0, т.е. заданы 1.

Анализ специализированного трафика:

Анализировать весь IPv6 трафик:

 # tcpdump ip6

Анализировать весь трафик с имеющимися флагами SYN и RST:

 # tcpdump ‘tcp[13] = 6′

Анализировать весь трафик с имеющимся “evil bit”:

 # tcpdump ‘ip[6] & 128 != 0′

Естественно что приведенные примеры не могут охватить всю полноту использования столь мощного инструмента как tcpdump.

Решение проблем при размещении Cisco 7942G за NAT’ом

Весьма проблематично заставить работать телефон Cisco 7942 или Cisco 7962, если он находится за NAT-ом. Телефон не может выполнить регистрацию, а без этого не получится ни звонить, ни принимать звонки.

Проблема вызвана некоторыми особенностями в реализации SIP-стека на телефонах, которые интеграторы телефонии на Asterisk называют багами, а сами Cisco-разработчики называют фичей. Речь идет о инициирующих SIP-портах, которые начинаются с 49000.

При подключении из-за NAT, сервер Asterisk считает пир nat=yes устройством, из-за чего использует симметричный SIP/RTP и шлет ответы ровно на тот порт, с которого пришло соединение. Но на деле сама Cisco 7942 ждет ответа не на порту отправления, а на порту 5060, а точнее, на порту, указанном в <voipControlPort>. Из-за различия портов связь установить не удается, а сама Cisco 7942 упорно отвечает сообщениями типа ICMP Port Unreachable.

Решение проблемы:

1. (Опционально) Указать в теге <voipControlPort> порт, отличный от 5060. Например, 5091. Это связано с тем. что в дальнейшем мы этот порт будем пробрасывать на роутере.

2. Закрепить Cisco 7942 на статическом IP-адресе, либо присвоить статическую аренду на DHCP-сервере.

3. Прописать секцию
<natEnabled>false</natEnabled>
<natAddress></natAddress>

4. На сервере Asterisk в настройках экстеншена прописать опцию nat=no

5. На роутере, который пускает телефон в Интернет, прописать статический проброс UDP порта, который у вас указан в <voipControlPort> на внутренний IP телефона Cisco 7942G. Как правило, эта секция в веб-интерфейсе роутера называется «Virtual Server» или «NAT 1:1».

6. Перезагрузить телефон нажатием Settings-> **#**.
Убедиться, что телефон работает. Если чуда не случилось, то поможет разобраться в проблеме tcpdump. Возможно, будет иметь смысл игра с опиями nat=no и nat=never

* — Надо заметить, что IP может быть и динамическим. В этом случае нужно будет настроить роутер так, чтобы он обновлял динамическую DNS-запись, например, на сервисе DynDNS. Тогда в это поле вписывается FQDN-запись вида <natAddress>somename.dyndns.org</natAddress>. При этом нужно учитывать, что телефон резолвит это имя в IP всего один раз, при старте телефона. Если внешний IP изменится, телефон об этом не узнает, из-за чего будут наблюдаться проблемы односторонней слышимости или отсутствия регистрации.