Валидация адресов в работе с IP-адресами

сеть windows

Лю­бое при­ложе­ние, которое хоть как‑то работа­ет с сетью, дол­жно валиди­ровать пра­виль­ность IP-адре­сов. Это слож­нее, чем может показать­ся. Здесь лег­ко впасть в край­нос­ти: при излишне стро­гой валида­ции поль­зователь не смо­жет ввес­ти вер­ные дан­ные, при недос­таточ­ной — ока­жет­ся наеди­не с низ­коуров­невыми сооб­щени­ями об ошиб­ках (если они вооб­ще переда­ются).

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

РЕКОМЕНДУЕМ:
Сетевые утилиты командной строки

Валидация адресов

Ошиб­ки в адре­сах могут появить­ся тре­мя спо­соба­ми:

  • опе­чат­ки;
  • не­допо­нима­ние;
  • на­мерен­ные попыт­ки сло­мать при­ложе­ние.

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

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

Проверки по форме

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

В IPv4 слож­ности начина­ются со стан­дарта на этот фор­мат — такого стан­дарта не сущес­тву­ет. Фор­мат dot-decimal ( 0.<wbr />0.<wbr />0.<wbr />0255.<wbr />255.<wbr />255.<wbr />255) — общепри­нятый, но не стан­дар­тный. Стан­дарт IPv4 не содер­жит никаких упо­мина­ний о фор­мате записи адре­сов вооб­ще. Никакой дру­гой RFC тоже ничего не говорит о фор­мате адре­сов IPv4, так что общепри­нятый фор­мат — это не более чем сог­лашение.

И это даже не единс­твен­ное сог­лашение. Фун­кция inet_aton(<wbr />) поз­воля­ет не писать нулевые раз­ряды в кон­це адре­са, нап­ример 192.<wbr />0.<wbr />2 <wbr />= <wbr />192.<wbr />0.<wbr />2.<wbr />0. Кро­ме того, она поз­воля­ет вво­дить адрес одним целым чис­лом, 511 <wbr />= <wbr />0.<wbr />0.<wbr />1.<wbr />255.

Мо­жет ли адрес хос­та закан­чивать­ся на ноль? Конеч­но, может — в любой сети раз­мером боль­ше /23 най­дет­ся хотя бы один такой. Нап­ример, 192.<wbr />168.<wbr />0.<wbr />0/<wbr />23 содер­жит адре­са хос­тов 192.<wbr />168.<wbr />0.<wbr />1192.<wbr />168.<wbr />1.<wbr />254, вклю­чая 192.<wbr />168.<wbr />1.<wbr />0.

Ес­ли огра­ничить­ся под­дер­жкой толь­ко пол­ного dot-decimal из четырех групп, без воз­можнос­ти опус­кать нулевые раз­ряды, то выраже­ние (\<wbr />d+)\.(\<wbr />d+)\.(\<wbr />d+)\.(\<wbr />d+) может пой­мать зна­читель­ную часть опе­чаток. Если задать­ся целью, мож­но сос­тавить выраже­ние для любого допус­тимого адре­са, хотя оно и будет доволь­но гро­моз­дким. Луч­ше вос­поль­зовать­ся тем, что его лег­ко раз­делить на груп­пы, и явно про­верить, что каж­дая из них попада­ет в диапа­зон 0–255:

С IPv6 все одновре­мен­но про­ще и слож­нее. Про­ще потому, что авто­ры IPv6 учли опыт IPv4 и добави­ли фор­мат записи адре­сов в RFC 4291. О любых аль­тер­натив­ных фор­матах мож­но сме­ло говорить, что они про­тив стан­дарта, и игно­риро­вать. С дру­гой сто­роны, сами фор­маты слож­нее. Основную слож­ность пред­став­ляет сок­ращен­ная запись: груп­пы нулевых раз­рядов мож­но заменять на сим­вол :<wbr />:, нап­ример 2001:<wbr />db8::<wbr />1 вмес­то 2001:<wbr />db8:<wbr />0:<wbr />0:<wbr />0:<wbr />0:<wbr />0:<wbr />1. Для поль­зовате­ля это, безус­ловно, удоб­но, но для раз­работ­чика все ров­но наобо­рот: раз­делить адрес на груп­пы по дво­ето­чию невоз­можно, нуж­на замет­но более слож­ная логика. К тому же стан­дарт зап­реща­ет исполь­зовать :<wbr />: боль­ше одно­го раза в одном адре­се, что еще силь­нее усложня­ет задачу.

РЕКОМЕНДУЕМ:
Выбор оборудования для создания скоростной домашней сети

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

Проверки по существу

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

Нуж­ные про­вер­ки будут зависеть от того, как будет исполь­зовать­ся адрес. Нап­ример, пусть поль­зователь хотел ввес­ти в поле адре­са сер­вера DNS зна­чение 124.<wbr />1.<wbr />2.<wbr />3, но опе­чат­ка прев­ратила его в  224.<wbr />1.<wbr />2.<wbr />3. Про­вер­ка фор­мата эту опе­чат­ку не пой­мает — фор­мат пра­виль­ный. Одна­ко этот адрес никак не может быть адре­сом сер­вера DNS, пос­коль­ку сеть 224.<wbr />0.<wbr />0.<wbr />0/<wbr />4 зарезер­вирова­на для мно­гоад­ресной мар­шру­тиза­ции, которую DNS не исполь­зует никог­да.

Ес­ли ты хочешь отсе­ять все адре­са, которые не могут быть адре­сами хос­тов в пуб­личном интерне­те, поч­ти пол­ный спи­сок зарезер­вирован­ных сетей мож­но най­ти в RFC 5735 (Special use IPv4 addresses). «Поч­ти пол­ный» он потому, что не вклю­чает сеть 100.<wbr />64.<wbr />0.<wbr />0/<wbr />10, выделен­ную для CG-NAT (RFC 6598). Сов­сем пол­ный спи­сок всех зарезер­вирован­ных диапа­зонов IPv4 и IPv6 мож­но най­ти в RFC 6890, одна­ко он не так удоб­но орга­низо­ван.

При этом нуж­но обра­тить вни­мание на мас­ки под­сетей. Некото­рые полага­ют, что сеть для час­тно­го исполь­зования — 172.<wbr />16.<wbr />0.<wbr />0/<wbr />16 (<wbr />172.<wbr />16.<wbr />0.<wbr />0172.<wbr />16.<wbr />255.<wbr />255). Чте­ние RFC5735 лег­ко раз­веет этот миф: на самом деле она замет­но боль­ше, 172.<wbr />16.<wbr />0.<wbr />0/<wbr />12 (<wbr />172.<wbr />16.<wbr />0.<wbr />1172.<wbr />31.<wbr />255.<wbr />254). Реаль­ный при­мер этой ошиб­ки в GoatCounter — скрипт сбо­ра ста­тис­тики оши­боч­но счи­тал посеще­ния изнутри локаль­ной сети.

Нуж­но так­же учи­тывать, что «зарезер­вирован­ные для исполь­зования в будущем» сети могут перес­тать быть зарезер­вирован­ными. Сети из RFC 5735 зарезер­вирова­ны нав­сегда и в этом смыс­ле безопас­ны. А вот авто­ры неког­да популяр­ной сре­ди гей­меров вир­туаль­ной сети Hamachi ког­да‑то счи­тали, что сеть 5.<wbr />0.<wbr />0.<wbr />0/<wbr />8 мож­но исполь­зовать для сво­их нужд, потому что она была зарезер­вирова­на для будуще­го исполь­зования, — пока будущее не нас­тупило и IANA не выдели­ла эту сеть RIPE.

Библиотеки

netaddr

В стан­дар­тной биб­лиоте­ке Python 3 уже есть модуль ipaddress, но, если есть воз­можность пос­тавить сто­рон­нюю биб­лиоте­ку, netaddr может силь­но упростить жизнь. К при­меру, в ней есть встро­енные фун­кции для про­вер­ки при­над­лежнос­ти адре­са к зарезер­вирован­ным диапа­зонам.

Да­же если бы этих фун­кций не было, мы мог­ли бы лег­ко реали­зовать их сами. Биб­лиоте­ка очень гра­мот­но исполь­зует ма­гичес­кие методы, что­бы сде­лать интерфейс таким же удоб­ным, как у встро­енных объ­ектов Python. Нап­ример, про­вер­ку при­над­лежнос­ти адре­са к сети или диапа­зону мож­но выпол­нить опе­рато­ром in, так что работать с ними не слож­нее, чем со спис­ками или сло­варя­ми.

libcidr

Да­же для чис­того С мож­но най­ти биб­лиоте­ку с удоб­ным интерфей­сом, такую как libcidr Мэттью Фул­лера. В Debian ее мож­но пос­тавить из репози­тори­ев. Для при­мера напишем про­вер­ку при­над­лежнос­ти адре­са к сети multicast и положим ее в файл is_multicast.<wbr />c.

Заключение

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

Понравилась статья? Поделиться с друзьями:
Добавить комментарий