# IP-адреса

## Що це таке

IP-адреса (англійською IP address) — це число, яке протокол IP записує в кожен
пакет, щоб указати, куди той прямує, поруч із другою адресою, що вказує, звідки
він прийшов. RFC 791, специфікація IP версії 4 від 1981 року, називає її
інтернет-адресою і розводить поняття так: назва каже, що ми шукаємо, а «адреса
вказує, де воно є». Назву, наприклад домен, спершу треба перетворити на адресу,
і цим займається [DNS](/uk/glossary/#dns). Маршрут, тобто шлях між двома
адресами, лишається на маршрутизаторах посередині.

В IPv4 адреса має 32 біти, тобто чотири октети, і записується як чотири
десяткові числа від 0 до 255, розділені крапками: 192.0.2.10. RFC 791 ділив ці
біти на номер мережі й локальну частину, а три фіксовані класи визначали, де
проходить межа. Більшості організацій класи пасували погано: мережа класу C
вміщала щонайбільше 254 хости, мережа класу B — до 65 534, тож номери класу B
закінчувалися, а таблиці маршрутизації росли. У вересні 1993 року RFC 1519
замінив класи на CIDR, який дозволяє провести межу після будь-якого біта й
записує її позицію після скісної риски. 192.0.2.0/24 — це блок, у якого перші 24
біти зафіксовані: 256 адрес.

IPv6 розширює адресу до 128 бітів. Її записують як вісім груп до чотирьох
шістнадцяткових цифр, розділених двокрапками, а RFC 4291 дозволяє стиснути одну
послідовність нульових груп до `::`, тож 2001:db8:0:0:0:0:2:1 можна записати й
як 2001:db8::2:1. Оскільки ту саму адресу відтак можна було записати багатьма
способами, згодом RFC 5952 закріпив одну рекомендовану форму: малі літери,
жодних нулів на початку групи і `::` для найдовшої послідовності нульових груп.

Адреса належить мережевому інтерфейсу, а не машині. Уже RFC 791 казав, що один
хост мусить уміти діяти як кілька, з кількома інтернет-адресами, і що деякі
хости мають кілька фізичних інтерфейсів. RFC 4291 вимагає, щоб кожен інтерфейс
IPv6 мав принаймні одну локальну адресу каналу (link-local), чинну лише в його
власному сегменті мережі, незалежно від інших адрес, які він має. Тож хост у
мережі, де працюють обидві версії, має кілька адрес одночасно, а ще адреси петлі
(loopback), якими звертається сам до себе: блок 127.0.0.0/8 в IPv4 і `::1` в
IPv6.

Не кожна адреса досяжна звідусіль. RFC 1918 виділяє для приватних мереж три
блоки IPv4, 10.0.0.0/8, 172.16.0.0/12 і 192.168.0.0/16, і каже, що інформацію
маршрутизації про них не можна передавати між організаціями. Тож пристрій з
такою адресою виходить в інтернет лише через щось, що виступає замість нього,
наприклад через NAT, який RFC 3022 описує як перетворення приватної адреси на
публічну на виході. Ще три блоки, 192.0.2.0/24, 198.51.100.0/24 і
203.0.113.0/24, RFC 5737 зарезервував для документації, а RFC 3849 робить те
саме для 2001:db8::/32 в IPv6. Усі адреси-приклади в цій статті взято з цих
документаційних діапазонів.

## Чому це важливо

Адреса — це єдине, що сервер завжди знає про клієнта, бо без неї відповіді не
було б куди йти. Тому вона стає типовим ключем для багатьох рішень: правила
[брандмауера](/uk/glossary/#firewall) дозволяють або блокують за адресою,
обмеження частоти запитів рахує запити на кожну адресу, журнал доступу пише її в
кожному рядку, а скарга на зловживання її називає. Кожне з цих рішень працює
лише настільки добре, наскільки адреса справді ідентифікує те, що, як ви
думаєте, вона ідентифікує.

Ідентифікує вона менше, ніж здається. За NAT усі пристрої в офісі чи
домогосподарстві можуть виходити в інтернет з однієї публічної адреси IPv4, тож
блокування чи ліміт, спрямовані на один із них, зачіпають усіх. За проксі або
[CDN](/uk/glossary/#cdn) сервер бачить у кожному запиті адресу проксі, а власна
адреса клієнта надходить, якщо надходить узагалі, у заголовку, який написав
хтось інший. Правило, написане проти адреси, — це правило про позицію в мережі,
а не про людину.

Водночас вона розкриває більше, ніж зазвичай думають. RFC 7239, який
стандартизував заголовок `Forwarded`, яким проксі передають адресу клієнта далі,
зазначає в розділі про приватність, що IP-адреса може однозначно ідентифікувати
клієнта, показати, яким оператором він користується, і дати приблизне уявлення
про те, де він перебуває. Тож журнал, повний адрес, — це не лише інструмент, а й
відповідальність, і добра причина вирішити, що ви зберігатимете, ще до того, як
почнете зберігати.

А тепер їх два види. Усе, що розбирає, зберігає чи порівнює адреси, мусить
однаково приймати IPv4 і IPv6, бо клієнт у мережі, яка пропонує обидві версії,
може прийти через будь-яку з них. Код, написаний тоді, коли кожна адреса
вміщалася в п'ятнадцять символів із цифр і крапок, ламається на першому
відвідувачеві з IPv6, і зазвичай далеко від місця, де адресу прочитали.

## Як це виглядає на практиці

У стандартній бібліотеці Python є модуль `ipaddress`, який розбирає обидві
версії і бере на себе арифметику, тож рядки ніколи не доводиться ділити вручну.
В інтерактивному сеансі:

```text
>>> import ipaddress
>>> ipaddress.ip_address("203.0.113.57")
IPv4Address('203.0.113.57')
>>> ipaddress.ip_address("2001:0DB8:0000:0000:0000:0000:0002:0001")
IPv6Address('2001:db8::2:1')
>>> ipaddress.ip_network("203.0.113.57/24", strict=False)
IPv4Network('203.0.113.0/24')
>>> ipaddress.ip_network("2001:db8:42:7::5/48", strict=False)
IPv6Network('2001:db8:42::/48')
```

Другий виклик показує нормалізацію, яку рекомендує RFC 5952: довгий запис
великими літерами і короткий малими — це та сама адреса, і модуль виводить
канонічну форму. Рядок на кшталт 192.0.2.300 модуль відхиляє з `ValueError`, бо
300 не вміщається в октет. Останні два виклики перетворюють адресу на мережу
навколо неї. Без `strict=False` модуль відхилив би й їх, бо в 203.0.113.57/24
встановлено біти хоста; з ним частина хоста обнуляється, і лишається сама
мережа.

Саме таке обнулення цей сайт застосовує до адрес своїх відвідувачів.
[Сторінка про приватність](/uk/privacy/) каже, що IP-адреса ніколи не
зберігається повністю: її скорочують у момент запису рядка журналу, і остання
частина замінюється нулями ще до того, як щось потрапить на диск, тож збережене
вказує на приблизну мережу, у якій цілий офіс і ціла вулиця можуть виглядати
однаково. Сторінка додає, що сервер записує адресу у двох полях і розмиває
обидва, а заголовки, які можуть містити власну адресу, — ті, що додає
корпоративний проксі або мережа доставки вмісту, — видаляє ще до запису рядка.
Скільки бітів лишається, вона не каже; /24 і /48 вище — це ілюстрація цієї
статті, а не налаштування сайту. Але суть від цього не змінюється. Адресу, яку
ніколи не записують повністю, неможливо повністю й розголосити, а скорочення на
вході не залежить від завдання з очищення, яке одного дня може перестати
запускатися.

## Де виникають проблеми

**Довіряти заголовку щодо адреси клієнта.** `X-Forwarded-For` і стандартний
заголовок `Forwarded` — звичайні заголовки запиту, і RFC 7239 попереджає, що
кожен вузол на шляху до сервера, зокрема й сам клієнт, може змінити `Forwarded`.
Код, який бере першу адресу зі списку, дозволяє будь-якому клієнтові вибрати
собі адресу, а з нею і власний ліміт, і власне місце в списку дозволених.
Використовуйте адресу TCP-з'єднання, якщо тільки запит не пройшов через проксі,
який ви експлуатуєте самі, і тоді читайте лише те, що додали ваші власні проксі,
рахуючи справа.

**Вважати адресу людиною.** Оскільки одну адресу можуть ділити багато
користувачів, бан адреси може заблокувати цілий офіс, а ліміт, розрахований на
одну людину, душить цілу школу. Використовуйте адреси для грубих рішень,
задавайте ліміти з огляду на спільні адреси, а все персональне прив'язуйте до
облікового запису, у який людина ввійшла.

**Порівнювати адреси як рядки.** 2001:db8::2:1 і 2001:DB8:0:0:0:0:2:1 — це одна
адреса і два різні рядки, тож список дозволених, який порівнює текст, прийме
одну й відхилить другу. Розбирайте обидві сторони перед порівнянням або
зберігайте лише форму з RFC 5952.

**Розбирати лише IPv4.** Регулярний вираз для чотирьох чисел із крапками,
стовпець на п'ятнадцять символів або код, що відділяє хост від порту за
останньою двокрапкою, — усе це ламається на IPv6. Використовуйте справжній
парсер і тип чи довжину стовпця, придатні для обох версій. Коли поруч з адресою
IPv6 пишуть порт, RFC 5952 рекомендує квадратні дужки, як у [2001:db8::1]:443,
бо інакше останню групу можна прочитати як частину адреси.

**Тримати повні адреси довше, ніж потрібно.** Журнали, аналітичні таблиці й
резервні копії роками накопичують адреси, якщо їх ніщо не зупиняє. Вирішіть, яка
точність вам потрібна, скорочуйте адресу під час запису, а не згодом, і задайте
решті термін придатності.

**Використовувати справжні адреси в прикладах.** Приклад конфігурації, тестові
дані чи знімок екрана зі справжньою адресою когось іншого скеровують
скопійований трафік до незнайомця. RFC 5737 резервує свої три блоки саме для
того, щоб зменшити такі конфлікти й плутанину; користуйтеся ними, а для IPv6 —
2001:db8::/32.

## Що почитати далі

- [RFC 791: Internet Protocol](https://www.rfc-editor.org/rfc/rfc791.html) —
  специфікація IPv4 1981 року з 32-бітною адресою і розрізненням назв, адрес і
  маршрутів.
- [RFC 4291: IP Version 6 Addressing Architecture](https://www.rfc-editor.org/rfc/rfc4291.html)
  — типи адрес IPv6, їхні текстові форми і вимога мати локальну адресу каналу.
- [RFC 5952: A Recommendation for IPv6 Address Text Representation](https://www.rfc-editor.org/rfc/rfc5952.html)
  — один канонічний спосіб записувати адресу IPv6 і проблеми, яких завдають
  довільні варіанти запису.
- [RFC 7239: Forwarded HTTP Extension](https://www.rfc-editor.org/rfc/rfc7239.html)
  — як проксі передають адресу клієнта далі, чому результатові не можна просто
  довіряти і чого це коштує приватності.
- [RFC 5737: IPv4 Address Blocks Reserved for Documentation](https://www.rfc-editor.org/rfc/rfc5737.html)
  — три діапазони, призначені для прикладів.
- [ipaddress: IPv4/IPv6 manipulation library](https://docs.python.org/3/library/ipaddress.html)
  — стандартний модуль Python для розбору, порівняння й групування адрес.

<https://engineer.company/uk/glossary/ip-address/>
