Безпека by design. Частина 8. (Роль процесу доставки коду у безпеці)

12 вересня 2023 1 хвилина Автор: Lady Liberty

Інтеграція Тестування Безпеки: Ключ до Надійності

Більшість програмістів погоджуються, що тестування повинно бути невід’ємною частиною процесу розробки, щоб уникнути можливих помилок після написання коду. Методології, такі як тест-керована розробка (TDD) і поведінкова розробка (BDD), стали стандартом, де виконується велика кількість тестів під час кожного етапу розробки. Дивно, що цей підхід часто застосовується тільки до тестів, які не стосуються безпеки, можливо через те, що безпека не завжди є в приоритеті. Проте ми вважаємо це нелогічним. Тести безпеки мають таку ж важливість, як і інші, і повинні проводитися регулярно. Це не означає, що потрібно проводити тестування на проникнення кожного разу, коли ви робите зміни в коді.

Тут потрібен інший підхід до мислення. Заходи безпеки повинні бути природньо інтегровані в процес доставки коду і застосовуватися при кожній зміні, і це буде предметом нашого розділу. Кожен розділ цієї глави, хоч і незалежний, буде пронизаний загальною темою: як інтегрувати різні тести безпеки в процес доставки коду. На відміну від попередніх тем, тут може знадобитися цільове планування заходів безпеки. Однак, застосовуючи це в повсякденній роботі, ви будете отримувати краще уявлення про безпеку вашого програмного забезпечення перед його виходом в продакшн. Перш ніж докладніше розглядати ці аспекти, давайте визначимо, що таке процес доставки коду.

Використовуйте конвеєр доставки коду

Процес доставки коду (або конвеєр) –  це автоматизований механізм розгортання програмного забезпечення у виробничому (або будь-якому іншому) середовищі. Це може здатися складною річчю, але насправді все навпаки. Уявіть, що у вас є наступний процес доставки.

  1. Переконайтеся, що всі файли були збережені в Git.

  2. Створіть додаток з основної гілки.

  3. Запустіть усі модульні тести та переконайтеся, що вони проходять.

  4. Запустіть всі тести додатків і переконайтеся, що вони проходять.

  5. Запустіть усі інтеграційні тести та переконайтеся, що вони пройшли.

  6. Запустіть всі системні тести і переконайтеся, що вони проходять.

  7. Запустіть усі тести на доступність і переконайтеся, що вони пройшли.

  8. Розгортання у виробничому середовищі (якщо всі попередні кроки успішно виконані).

Перші два кроки – включити всі файли в збірку і переконатися, що код компілюється. Етапи з 3 по 7 перевіряють різні аспекти якості, а останній етап розгортається у виробничому середовищі (якщо всі попередні успішно завершені). Незалежно від того, як виконується цей процес, вручну або автоматично, його основне завдання – не допустити потрапляння програмних дефектів в розгорнуту систему.

Якщо ви вирішите використовувати сервер збірки, у вас буде автоматизована версія цього процесу, конвеєр доставки коду (див. рисунок 8.1).

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

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

Безпечний дизайн за допомогою модульних тестів

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

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

Для перевірки об’єктів рекомендуємо використовувати чотири різних типи тестів (табл. 8.1). Так ви можете бути впевнені, що код дійсно робить те, що вам потрібно, і небажана поведінка виключено.

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

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

Це природна стратегія налаштувати сервери електронної пошти так, щоб вони приймали лише адреси, які знаходяться в правильному домені. Але що робити, якщо ця конфігурація змінюється або втрачається при переході на нову версію? В цьому випадку ви несвідомо почнете приймати електронні листи з недійсними адресами – така діра в безпеці може мати плачевні наслідки. Розумнішим рішенням було б об’єднати конфігурацію сервера електронної пошти з механізм відхилення неправильних адрес на системному рівні. Це забезпечить глибоку безпеку, що зробить систему більш стійкою до атак, так як зловмиснику доведеться обходити відразу кілька захисних механізмів. Але для цього потрібно розуміти правила роботи з адресами електронної пошти всередині лікарні.

Розуміння правил домену

У розділі 1 ви дізналися, що спілкування з експертами допомагає вам глибше зрозуміти предметну область. Те ж саме стосується і лікарні. Виявляється, правила поводження з адресами електронної пошти в даному контексті зовсім не такі, як можна було б очікувати.

Специфікація адреси електронної пошти, RFC 5322, дозволяє досить широкий діапазон символів, які можуть міститися в дійсній адресі. На жаль, це визначення не може бути використано в лікарняних умовах, оскільки кілька застарілих систем обмежують набір доступних символів, і це потрібно враховувати. В результаті профільні експерти вирішили, що дійсна поштова адреса може містити лише літери, цифри та крапки. Загальна довжина обмежена 77 символами, а доменне ім’я має бути hospital.com.

Крім цього, є ще кілька вимог.

  • Формат поштової адреси повинен мати вигляд локальна частина@домен.

  • Довжина локальної частини не повинна перевищувати 64 символи.

  • Піддомени не допускаються.

  • Мінімальна довжина поштової адреси становить 15 символів.

  • Максимальна довжина поштової адреси – 77 символів.

  • Локальна частина може містити лише алфавітні символи (a — z), цифри (0–9) та одну точку.

  • На початку та в кінці локальної частини не може бути точки.

Спочатку, враховуючи вільне визначення в RFC 5322, у вас може виникнути спокуса думати про адресу електронної пошти як про  рядок. Але вимоги правил домену говорять про те, що краще створити примітивний домен EmailAddress. Щоб забезпечити дотримання правил домену, при проектуванні можна відштовхуватися від модульних тестів. Давайте перевіримо нормальну поведінку.

Перевірка нормальної поведінки

При тестуванні нормальної поведінки слід орієнтуватися на вхідні дані, які явно відповідають правилам домену. Для EmailAddress це означає, що вхідні дані повинні мати дійсну довжину (від 15 до 77 символів), hospital.com як домен і  локальну частину, що складається виключно з букв (від a до z), цифр і не більше однієї крапки. Таким чином, ми можемо бути впевнені, що  що коли ми отримаємо нормальний вхід, наша реалізація працюватиме так, як треба. Лістинг 8.1 показує приклад того, як перевірити нормальну поведінку.

EmailAddress. Тест виконується за допомогою JUnit 5, процес збірки досить цікавий в тому сенсі, що в ньому використовується потік вхідних значень (допустимих поштових адрес), прив’язаних до тесту відкладеного виконання, динамічного тесту. Живий тест відрізняється від звичайного тесту тим, що він визначається не під час компіляції, а під час виконання. Це дає можливість динамічно створювати тест-кейси в залежності від введених параметрів,  як у наступному списку. Крім того, параметризованому тестовому будівництву часто віддають перевагу в ситуаціях, коли потрібно підтвердити припущення, оскільки можна легко додавати і видаляти вхідні значення, не змінюючи логіку тесту.

Проведення цього тесту дозволяє розпочати проектування  об’єкта EmailAddress. За правилами домену, в локальній частині допускаються тільки літерні символи, цифри і крапка. Це трохи ускладнює ситуацію, але Лістинг 8.2 показує рішення, яке реалізує цю вимогу з використанням регулярного виразу (regexp). Доменне ім’я повинно бути hospital.com, що не дозволяє приймати будь-які інші домени.

Однак тестування нормальної поведінки – це лише один крок до створення безпечного  об’єкта EmailAddress. Вам також потрібно переконатися, що адреси, близькі до семантичних кордонів, обробляються так, як ми очікуємо. Наприклад, як дізнатися, що адреси довжиною понад 77 символів відхилено або адреса не може починатися з крапки? Це привід створити новий набір тестів, який буде тестувати поведінку меж.

Тестування граничної поведінки

У розділі 3 ми обговорили, наскільки важливо розуміти семантичні межі контексту і як дані можуть автоматично змінювати своє значення при перетині. Семантична границя об’єкта домену часто являє собою комбінацію простих структурних правил, таких як довжина, розмір або кількість, і складних правил домену. Візьмемо, наприклад, кошик для покупок на веб-сторінці, змодельований як сутність. До моменту оформлення замовлення дозволяється додавати певну кількість товарів і змінювати вміст кошика. Але після цього порядок стає незмінним і оновлення заборонені. Завдяки переходу між станами порядок перетинає смислову межу, так як відкритий і розміщений ордери мають різне значення. Це потрібно перевірити, оскільки навколо цих кордонів часто виникають проблеми з безпекою.

Повернімося  до EmailAddress і  лікарні. Потрібно переконатися, що архітектура дійсно відповідає граничним умовам, описаним в правилах домену. На щастя, тестування можна зробити трохи простішим, оскільки ці правила не вимагають складних переходів між станами, як у прикладі кошика для покупок. Всі вимоги структурні, включаючи обмеження довжини та дозволені символи. Це досить легко перевірити. У табл. 8.2 перераховані граничні умови, які необхідно перевірити.

Наявність цього списку дозволить вам розпочати розробку модульних тестів, які тестують поведінку країв у кожному конкретному випадку. Лістинг 8.3 показує приклад того, як це можна реалізувати за допомогою JUnit 5. Перший тест, should_be_accepted, перевіряє, чи приймається адреса, якщо вона є частиною домену hospital.com і  має довжину від 15 до 77 символів. Другий тест,  should_be_ відхилено, трохи довший і призначений для відхилення введених даних, які виходять за межі досяжності (наприклад, він може бути занадто коротким, занадто довгим, містити неприпустимі символи або мати неправильне доменне ім’я).

Запуск цього тесту показує, що реалізація EmailAddress занадто слабка. Регулярний вираз ^[a z0 9]\+\.? [a z0 9]\+@\bhospital.com$ трохи спрощений, без обмеження довжини локальної частини і загальної довжини адреси.

Лістинг 8.4 показує оновлену версію  EmailAddress, яка перевіряє довжину окремо перед застосуванням регулярного виразу. Як ви вже знаєте з глави 4, ви завжди повинні виконувати лексичний розбір перед обробкою введених даних. Це можна зробити безпосередньо в регулярному виразі за допомогою позитивної перевірки вперед, але ми навмисно пропустили цей крок, оскільки перевірка довжини гарантує, що аналіз безпечний незалежно від того, чи  які символи містяться у вході. Однак в більш складних ситуаціях аналізатор повинен бути захищений попереднім лексичним розбором.

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

Потрібно переконатися, що шкідливе введення не може порушити механізм перевірки. Наприклад, модель EmailAddress  сильно залежить від того, як інтерпретуються регулярні вирази. Це нормально, але що робити, якщо в модулі regexp є слабкість, яка призводить до збою певного входу, або обробка певного введення займає надзвичайно багато часу? Позбавлення від такого роду проблем є метою останніх двох типів тестів –  Перевірка на неправильне і екстремальне введення. Давайте подивимося, як їх виконати на об’єкті EmailAddress.

Тестування з некоректними вхідними даними

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

Лістинг 8.5 ілюструє, як перевірити EmailAddress,  використовуючи невірне введення. Вхідні дані – це серія адрес з незвичайними символами, нульовими значеннями  та даними, які на перший погляд здаються правильними. Цей вид тестування підвищує ймовірність того, що ваша модель насправді буде стійкою до простих ін’єкційних атак, які використовують слабкі місця в логіці перевірки.

Після виконання граничних тестів все виглядало так, ніби об’єкт EmailAddress спроектований досить добре. Проте тестування з використанням неприпустимого введення показало, що значення null призводить до збою реалізації в момент виклику value.length(). У лістингу 8.6 представлена оновлена версія EmailAddress, в якій null цілеспрямовано відхиляється контрактом неnull.

Екстремальне вхідне тестування

Екстремальне вхідне тестування складається з виявлення слабких місць в архітектурі, які викликають поломку програми або поведінку непередбачувано при обробці екстремальних значень. Наприклад, введення величезного введення може погіршити продуктивність, викликати витік пам’яті та інші небажані наслідки. Лістинг 8.7 показує,  як протестувати EmailAddress за допомогою лямбди постачальника  і введення від 10 000 до 40 000 000 мільйонів символів. Це явно порушує правила домену, але тут вони нас не цікавлять, ми швидше хочемо подивитися, як поводиться логіка перевірки при розбору таких вхідних даних. В ідеалі вхідні дані слід відхилити, але при використанні неповного алгоритму перевірки можуть виникати всілякі божевільні ситуації.

Як показує запуск тестів з екстремальним введенням, модель EmailAddress  працює належним чином. Вхідні дані фактично відхиляються, але все могло бути інакше. У розділі 4 ми говорили про те, як виконуються перевірки валідації та як потрібно перевіряти довжину вхідних даних перед аналізом вмісту. Лістинг 8.7 є прикладом того, наскільки це важливо.

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

На цьому ми завершуємо обговорення  прикладу EmailAddress  і того, як думати при розробці безпечних модульних тестів. Але це лише один з етапів забезпечення безпеки на рівні проектування. Інший підхід полягає в тому, щоб зберегти у виробничому середовищі тільки ті функції, які ви хочете бачити. І це підводить нас до наступної теми, яка полягає в тому, як протестувати функціональність перемикачів.

Перевірка функціональності перемикачів

Безперервна доставка та розгортання все частіше вважаються рекомендованими практиками розробки програмного забезпечення. В результаті перемикачі функціональності широко використовуються при розробці систем. Перемикання функцій – це техніка, яка дозволяє розробникам створювати та розгортати нові можливості у високому темпі, безпечно та контрольовано. Це корисний інструмент, але при надмірному використанні він може швидко ускладнитися. Залежно від того, які функції ви перемикаєте, помилки в механізмі перемикання можуть призвести не тільки до неправильної поведінки бізнесу, але і до серйозних проблем з безпекою (незабаром ви переконаєтеся в цьому самі).

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

Небезпека погано спроектованих вимикачів

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

Члени команди деякий час працювали разом, звикли один до одного належним чином і видавали робочий софт у високому темпі. Команда використовувала багато різних методів, запозичених з безперервної доставки, а код був написаний за допомогою TDD (test-driven development). На додаток до цього, вони створили великий трубопровід доставки, який гарантував, що лише належним чином працюючі функції досягають виробничого середовища.

Команда працювала над низкою нових функцій. На одному з перших кроків, як вони це зробили, фахівці додали перемикач функціоналу, який дозволяв вмикати і вимикати нові функції. Цей перемикач використовувався при запуску локальних тестів на комп’ютері розробника і сервері CI, а також при перевірці розгорнутого екземпляра системи в тестовому середовищі. Нова функціональність була доставлена через публічний API, і в остаточному вигляді вона повинна була мати відповідні механізми аутентифікації та авторизації, щоб лише певні користувачі мали доступ до нових кінцевих точок API. Авторизація повинна була базуватися на нових правилах доступу, які розроблялися іншою командою і ще не були розгорнуті. Але ці правила не були обов’язковими для перевірки інших аспектів ділової поведінки. Це дозволило команді продовжити роботу, поки інші розробники завершили свою частину системи. У виробничій конфігурації перемикач для функцій, які ще не готові, вимкнено, щоб вони не стали доступними в загальнодоступному API. Він повинен був залишатися вимкненим до тих пір, поки не буде завершена нова функціональність і не будуть пройдені всі приймально-здавальні випробування.

На якомусь етапі розробки перемикач в промисловій конфігурації був випадково включений. Сталося це через помилку, допущену одним з членів команди при об’єднанні деяких змін в конфігураційних файлах. Кількість використовуваних в додатку перемикачів поступово збільшувалася, а їх настройка стала досить складною. Знайти тонку помилку було непросто, і це одна з тих помилок, які міг допустити будь-який з розробників. В результаті новий функціонал став доступний у публічному API, але без будь-яких механізмів авторизації, оскільки вони ще не реалізовані. Це зробило нові кінцеві точки доступними майже для всіх. На щастя, команда швидко виявила та виправила помилку конфігурації раніше, ніж хтось встиг скористатися новими функціями у виробничому середовищі.

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

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

Перемикання функціоналу як інструмент розробки

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

За принципом роботи перемикачі за функціоналом схожі з електричним вимикачем. Вони дозволяють вмикати та вимикати певні функції вашого програмного забезпечення – це схоже на те, як ми вмикаємо або вимикаємо світло (рисунок 8.2). Окрім увімкнення та вимкнення функцій, перемикачі також можна використовувати для перемикання між різними функціями, що дає змогу вибирати різні варіанти поведінки.

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

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

Існує кілька видів функціональних перемикачів. Деякі з них використовуються для перемикання функцій, які все ще знаходяться в розробці, в той час як інші використовуються для включення і виключення можливостей в промислових умовах на основі налаштувань середовища виконання, таких як час, дата або певні властивості поточного користувача. Перемикачі також можуть бути реалізовані по-різному. Найпростіша процедура полягає в тому, щоб змінити код таким чином, щоб він містив або не містив певних частин кодової бази, як показано в Лістингу 8.8.

Як бачите, цей код перемикається між старим і новим функціоналом. Старі функції викликаються за допомогою  методу callOldFunctionality. Коли вони включені, потрібно відключити новий функціонал, закоментувавши  метод callNewFunctionality. Якщо ви хочете використовувати нові функції, слід повторити цю процедуру, але навпаки: закоментувати  метод callOldFunctional  і викликати callNewFunctionalяк у  списку usingNewImplementation.

Більш складним перемикачем можна керувати, наприклад, потужністю тієї конфігурації, яка надається при запуску програми. У списку 8.9 наведено приклад, де функціональність залежить від властивості системи feature.enabled. Якщо вам потрібні більш динамічні перемикачі, ви можете управляти ними під час виконання як якимось механізмом адміністрування.

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

Приручення вимикачів

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

Разом зі складністю збільшується і ймовірність помилки. Коли справа доходить до безпеки, навіть проста помилка може мати серйозні наслідки. Наприклад, відкриття доступу до незавершеного функціоналу в загальнодоступному API може викликати різні проблеми з безпекою, починаючи від прямих фінансових втрат і закінчуючи витоком конфіденційних даних.

Розробляючи автоматизовані тести, які перевіряють продуктивність кожного комутатора, і додаючи їх до свого конвеєра доставки, ви страхуєте себе від несподіваної поведінки. Оскільки тести виконуються автоматично і в кожній збірці, вони також дозволяють регресійне тестування для майбутніх змін, утримуючись від випадкових помилок. На початку цього розділу ми розглянули ситуацію, коли злиття дефектного коду призвело до того, що деякі кінцеві точки API стали публічними. Цьому можна було б запобігти, якби розробники забезпечили автоматизовані тести, які б запобігли включенню нової функціональності в промислові умови.

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

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

Лістинг 8.10 показує приклад трохи більш реалістичного  класу OrderService, який дозволяє розміщувати замовлення. Цей клас був розширений новою можливістю відправки даних про розміщене замовлення в систему бізнес-аналітики (BI). Ця функція вмикається та вимикається за допомогою ToggleService,  гіпотетичної бібліотеки для управління перемикачами функціональності. Кожен раз, коли  виконується метод placeOrder,  Об’єкт OrderService  перевіряє поточний режим замовлення (новий або старий) і поводиться відповідним чином.

Лістинг 8.11 показує приклад написання тестів для цього перемикача. Ми не тестуємо поведінку вихідного функціоналу, який полягає в оформленні замовлення і відправці даних в BI-систему. Наші тести перевіряють, що правильні аспекти поведінки спрацьовують залежно від стану перемикача. Якщо ордер має СТАРИЙ режим, його необхідно відправити на обробку, але нічого передавати в BI-систему не потрібно. Якщо ви використовуєте режим NEW order, крім обробки, дані про нього повинні відправлятися в BI-систему. Для перевірки взаємодії між допоміжними сервісами (код сервера BI-системи і оформлення замовлення) використовуються макети. Якщо ви вперше використовуєте макетні об’єкти в тестах, не хвилюйтеся. У цьому прикладі вони просто дозволяють перевірити, чи здійснюються дзвінки до служб підтримки.

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

Комбінаторна складність

Якщо ви використовуєте кілька перемикачів, потрібно спробувати перевірити всі їх комбінації, особливо якщо одні з них впливають на інші. Але навіть якщо прямого зв’язку між ними немає, все комбінації слід протестувати, так як вони можуть бути підключені опосередковано. Непряма прив’язка може відбуватися на будь-якій стадії розвитку. Як неважко здогадатися, необхідність перевірки великої кількості перемикачів може швидко перетворитися на справжній комбінаторний кошмар. Чим більше буде перемикачів, тим більша ймовірність того, що ви зробите щось не так, і тим важливіше їх перевірити. Це одна з причин, чому ви завжди повинні використовувати якомога менше перемикачів.

Існує думка: при аналізі ризиків (оцінці того, наскільки ризики знижені при перевірці всіх комбінацій в порівнянні з перевіркою лише декількох) не обов’язково тестувати всі комбінації. Такий підхід може здатися логічним, але він заснований на припущенні, що ви здатні оцінити дефекти безпеки, про які вам не відомо. Якщо ви про них знаєте, вони, швидше за все, вже виправлені. Рекомендуємо перевірити всі комбінації перемикачів. А щоб тестування не було занадто складним, кількість перемикачів в кодовій базі слід скоротити.

Вимикачі підлягають аудиту

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

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

Автоматизовані тести безпеки

Більшість розробників погодяться, що тестування безпеки є дуже значущим і має проводитися регулярно. Але насправді більшість програмних проектів ніколи не проводять аудит безпеки або тестування на проникнення. Іноді це пояснюється тим, що софт майже не схильний до ризику, а іноді розробники просто не приділяють належної уваги безпеці. Ще одна поширена причина відсутності таких тестів, з нашого досвіду, полягає в тому, що тестування на проникнення часто вважається занадто копітким і дорогим.

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

Робота, проведена в тестуванні на проникнення (і переваги, які вона приносить), певним чином не відрізняється від звичайного пошукового тестування. Люди можуть виконувати завдання і пропонувати логічні міркування на рівні, який все ще недоступний для комп’ютерів. Спроба замінити живі тестери автоматизованими тестами нереальна, і ми не говоримо, що ви повинні прагнути до цього. У цьому розділі ви дізнаєтеся, як писати тести, які дозволять вам проводити обмежене тестування на проникнення як частину вашого конвеєра доставки.

Тести безпеки – це лише тести

Слід розуміти, що тести безпеки нічим не відрізняються від будь-яких інших (рис. 8.3). Різниця лише в тому, що розробники чомусь вважають за краще використовувати в своїй назві слово «безпека». Якщо ви знаєте, як писати регулярні автоматизовані тести для перевірки поведінки та виявлення помилок програмного забезпечення, ви можете застосувати ті самі принципи до тестування безпеки.

  • Чи правильно ви обробляєте невдалі спроби входу? Напишіть для цього тест.

  • Чи має ваш інтернет-форум адекватний захист від XSS? Напишіть тест, який намагається ввести шкідливі дані.

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

Поговоримо докладніше про те, які перевірки виконує тестувальник безпеки. Деякі з них є більш-менш обов’язковими в тому сенсі, що проводяться завжди, незалежно від мети тестування. Багато з цих перевірок можна віднести до елементарної «гігієни», і ваша заявка завжди повинна їх проходити. Як виявилося, багато з них не так вже й складно реалізувати у вигляді автоматизованих тестів. Зазвичай легко перевірити автоматику, ручне впровадження якої приносить мало користі. Перетворення їх в автоматизовані тести не тільки дозволяє запускати їх так, як ви вважаєте за потрібне, але і дає можливість зосередитися на більш складному тестуванні. Надання шкідливих даних для пошуку дефектів в обробці введення, таких як SQL-ін’єкції або атаки переповнення буфера, є хорошим прикладом повсякденного завдання, яке можна автоматизувати.

Використання тестів безпеки

Щоб вам було простіше зрозуміти, які функції потрібно протестувати і як структурувати процес автоматизації тестування, давайте розділимо тести безпеки на дві основні категорії: додатки та інфраструктура (табл. 8.4). Крім цих двох категорій, спеціально призначених для безпеки, існують також тести, пов’язані з предметною областю. Їх ми вже розглянули в першій частині розділу, і, як ви вже знаєте, вони також допомагають зробити систему більш безпечною.

Далі ми поговоримо про згадані категорії тестів.

Для тестування додатків та інфраструктури існує ряд інструментів, на які слід звернути увагу. Наприклад, можна застосувати сканер портів до сервера, на якому розгорнуто програму. Аналогічно, інструмент веб-тестування може сканувати веб-додаток або виконувати підготовлені варіанти використання, допомагаючи виявити вразливості. Ви також можете використовувати інструменти для пошуку в коді вразливих сторонніх залежностей. Будь-які несподівані результати повинні призвести до невдачі тесту та зупинки конвеєра доставки.

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

Використання інфраструктури як коду

З популяризацією хмарних обчислень все більш поширеним стає поняття інфраструктури як код (IaC). Основною ідеєю IAC є можливість декларативно описувати інфраструктуру. Це може бути що завгодно, від серверів і мережевих топологій до брандмауерів, маршрутизації і так далі. Це має ряд переваг, однією з яких є детермінована конфігурація, яка дозволяє відтворювати всю інфраструктуру стільки разів, скільки потрібно. Це також значно полегшує використання системи контролю версій для ведення історії всіх змін у вашій інфраструктурі, незалежно від того, малі вони чи великі.

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

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

Застосування на практиці

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

На цьому етапі ви вивчили основи автоматизації цілеспрямованого тестування безпеки. У наступному розділі ми поговоримо про те, чому доступність відіграє важливу роль і як вона пов’язана з безпечною розробкою програмного забезпечення.

Тестування доступності

Існує думка, що класичні властивості – конфіденційність, цілісність і доступність – відносяться тільки до інформаційної безпеки, але вони також важливі при проектуванні захищеного програмного забезпечення. Наприклад, конфіденційність означає захист даних від прочитання неавторизованими  користувачами, а цілісність – це забезпечення того, щоб дані були  змінені санкціонованим чином. А як щодо доступності? Багато розробників легко схоплюють цю концепцію, але відчувають труднощі з її тестуванням, так як вона полягає в тому, що дані повинні бути доступні в той момент, коли вони потрібні авторизованим користувачам.

Уявіть, що у вас пожежа і ви набираєте номер телефону служби порятунку, але додзвонитися не можете. Номер набраний правильно, але диспетчерська перевантажена помилковими дзвінками. Це погано! Інший менш серйозний приклад – коли ви намагаєтеся купити квиток онлайн на концерт популярної групи, але сайт виходить з ладу або стає недоступним. Найчастіше причиною таких проблем стає не шкідлива активність, а те, що всі намагаються купити квитки одночасно: відвідувачі сайту не збиралися робити нічого поганого, але результат виявився нітрохи не кращим, ніж при цілеспрямованій атаці.

Таким чином, будь-яка програма повинна бути задіяна в тестуванні доступності. Але як реалізувати це на практиці? Для цього можна змоделювати відмову в обслуговуванні (DoS-атаку), яка дозволяє побачити поведінку системи до і після того, як дані стануть недоступними. В даному випадку все починається з оцінки операційної маржі.

Оцінка експлуатаційного фонду

Оцінка операційної маржі – це спроба зрозуміти, скільки навантаження може витримати додаток, поки не втратить здатність задовільно обслуговувати клієнтів. Для цього загальноприйнято аналізувати споживання пам’яті, використання ЦП, час відгуку і т. д. Це також може допомогти зрозуміти поведінку програми до її збою та виявити слабкі місця в її архітектурі.

На рис. На рисунку 8.4 наведено приклад DDoS-атаки (DDoS) (DDoS), при якій багато різних серверів одночасно надсилають величезну кількість запитів до програми. Незалежно від кількості запитів і навантаження, яке вони генерують, основною метою є обмеження доступності сервісів додатків. При обговоренні DDoS-атак часто використовується більш загальний термін DoS. Основна відмінність цих двох концепцій полягає в тому, що DoS-атака виконується не з декількох серверів, а з одного. Але завдання його та ж, і відтепер ми будемо вважати терміни DDoS і DoS взаємозамінними.

Імітуючи DoS-атаку, ви можете легко отримати уявлення про те, наскільки добре масштабується ваша програма і як вона поводиться, перш ніж вона перестане відповідати вимогам доступності. Слід зазначити, що як би добре не була влаштована система, вона рано чи пізно вийде з ладу, якщо атака виявиться досить масованою. З цього випливає, що практично неможливо спроектувати систему, яка була б на 100% стійка до DoS-атак. Але якщо ви намагаєтеся виявити слабкі місця у вашій архітектурі, оцінка її операційної маржі буде хорошою стратегією.

Для додатків для тестування навантаження існує кілька комерційних продуктів та альтернатив з відкритим кодом. Одним із прикладів є Bees with Machine Guns, утиліта для створення екземплярів сервера EC2 на Amazon Web Services, які атакують програму, надсилаючи їй тисячі одночасних запитів. Список 8.12 показує, як налаштувати вісім екземплярів EC2, які надсилатимуть на веб-сайт 100 000 запитів по 500 одночасно.

Який би продукт ви не вибрали, важкі системні випробування у вашому конвеєрі доставки є ефективним способом виявлення слабких місць, які можуть бути використані в промислових умовах.

Але успішна DoS-атака не вимагає тисяч одночасних запитів. Доступність також може бути підірвана більш складними способами, наприклад, за допомогою правил домену, які не відразу помітні.

Правила експлуатації домену

Коли ви запускаєте правила домену, ви фактично проводите DoS-атаку, в якій ці правила виконуються відповідно до вимог бізнесу, але зі злим наміром. Щоб проілюструвати це, розглянемо приклад готелю з щедрими правилами скасування.

Для того, щоб обслуговування клієнтів було на високому рівні, менеджер готелю прийняв рішення повністю повернути вартість бронювання, якщо ануляція бронювання здійснюється в день заїзду до 16:00. Це забезпечує велику гнучкість, але що робити, якщо хтось спеціально бронює номер і не з’являється? Чи призведе це до того, що номер виявиться недоступним, в результаті чого готель втратить потенційних клієнтів? Безсумнівно, саме так працює доменна DoS-атака. Дія політики ануляції дозволяє забронювати всі номери і скасувати бронювання в самий останній момент без будь-якої оплати. Таким способом зловмисник може заблокувати номери певного типу або направити клієнтів в конкуруючий готель.

Такі атаки можуть здатися вигаданими і малоймовірними, але щось подібне вже кілька разів траплялося в реальності. Наприклад, одного разу компанія-агрегатор таксі Lyft звинуватила свого конкурента Uber в спробі завдати фінансової шкоди, замовивши і скасувавши понад 5000 поїздок в Сан-Франциско. Інший випадок стався в Індії, де Uber подав до суду на свого конкурента Ola за замовлення 400 000 помилкових поїздок.

Моделювання такої поведінки в тестах може здатися безглуздим, але використання правил домену зі злим наміром допомагає краще зрозуміти слабкі сторони моделі домену. Знання, отримані таким чином, можуть бути безцінними при розробці сповіщень, які спрацьовують у відповідь на порушення обмежень або якусь дію користувача, або при використанні машинного навчання для виявлення шкідливої активності. Але тестування доступності – це лише одна з речей, які потрібно враховувати при впровадженні механізмів безпеки у вашому конвеєрі доставки. Інший – зрозуміти, як конфігурація програми впливає на її поведінку, особливо з точки зору безпеки. І це підводить нас до наступної теми – перевірити правильність настройки.

Перевірка правильності конфігурації

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

Якщо ви створюєте веб-додаток, ви, ймовірно, не хочете витрачати час на написання власної реалізації HTTPS для обслуговування веб-запитів або розробку доморощеного фреймворку ORM для роботи з базою даних, в обох з яких легко помилитися. Замість того, щоб розробляти загальні функції самостійно, ви можете використовувати існуючі бібліотеки або фреймворки. Для більшості розробників це буде розумним рішенням, так як підключення типового функціоналу за допомогою зовнішніх інструментів дозволяє зосередитися на унікальних аспектах сфери вашого бізнесу.

Але навіть якщо ви вирішите створити власну реалізацію спільної функціональності, ви, швидше за все, захочете розробити її як бібліотеку, щоб інші команди могли використовувати її у своїх додатках. Незалежно від того, який підхід ви виберете, деякі важливі функції вашого додатка будуть забезпечені зовнішнім кодом і управлятися за допомогою конфігурації. І хоча це типові функції, вони можуть відігравати провідну роль у безпеці вашого додатка. Звідси випливає, що помилки конфігурації можуть бути безпосередньою причиною проблем з безпекою. Ефективно боротися з ними можна за допомогою автоматизованих тестів.

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

Ненавмисні зміни

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

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

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

Навмисні зміни

Небажані побічні ефекти, які роблять додаток небезпечним, можуть виникнути не лише через випадкові зміни. Іноді навмисна зміна також може викликати подібні проблеми.

Уявіть, що ви впроваджуєте нову функцію, і в рамках цього процесу вам потрібно внести зміни в конфігурацію. Ви перевіряєте, як працює додаток (в ідеалі, додаючи нові автоматизовані тести, як ми щойно обговорювали) і продовжуєте працювати над новою функцією. Однак під час тесту на зміну поведінки ви не помітили, що інша частина програми почала вести себе інакше. Можливо, конфігурація, яку ви редагували, була результатом попереднього аудиту безпеки або тесту на проникнення, або була призначена для захисту деяких слабких місць. У процесі його модифікації ви відключили ці механізми безпеки, залишивши додаток вразливим.

Мимовільні зміни в поведінці однієї частини системи при редагуванні іншої – не рідкість. Це схоже на внесення ненавмисних змін, але варто зазначити, що в цьому випадку ви, як розробник, не робите нічого поганого.

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

Нерозуміння конфігурації

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

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

Автоматизовані випробування захисної сітки

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

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

Наш досвід показує, що часто досить легко створити автоматизовані тести для функціональності, яка управляється за допомогою конфігурації і представляє інтерес з точки зору безпеки. Наприклад, у веб-додатку легко написати тест, який перевіряє правильність заголовків HTTP або переконується, що форма використовує токени CSRF. Такі тести найкраще створювати в процесі розробки додатків, але завдяки простоті написання їх досить легко додати до існуючої кодової бази.

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

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

Значення за замовчуванням та перевірка

Крім спеціально налаштованих аспектів поведінки, важливо перевірити наявність неявних аспектів, які з’являються при використанні бібліотеки або фреймворка. Неявна поведінка – це  поведінка, яка відбувається без додавання будь-якої конфігурації. Іноді це називають  поведінкою за промовчанням. Оскільки конфігурації немає, ми можемо навіть не знати, що у нас є важлива функціональність для тестування. Щоб цього не сталося, ви повинні переміщатися по налаштуваннях, які використовуються у вашому інструменті за замовчуванням.

Наприклад, більшість сучасних веб-фреймворків і бібліотек дозволяють легко створювати API на основі HTTP або веб-сервіси на основі REST. Існує незліченна кількість проектів, за допомогою яких розробники можуть декларативно описувати кінцеві точки HTTP. Такого роду інструменти можуть підвищити вашу продуктивність, адже завдяки їм ви можете зосередитися на бізнес-логіці, ігноруючи шаблон і стандартний код. Можливість писати чистий і стислий код зазвичай обумовлена розумною поведінкою вашого фреймворка за замовчуванням. Дотримуючись стандартної конфігурації, ви напишете мінімальну кількість коду. Це чудово підходить для навчальних прикладів і невеликих прототипів, але для реальних проектів, які дуже значущі для вашої організації, вам потрібно чітко розуміти налаштування за замовчуванням. У багатьох випадках ці налаштування підвищують безпеку програми, але іноді можуть являти собою компроміс між безпекою та простотою використання. Якщо ви не знаєте про ці компроміси, ви можете мимоволі зробити свій код вразливим.

Уявіть, що ви розробляєте HTTP-сервіс. Це може бути свого роду REST API або якийсь інший API на основі HTTP. Щоб зменшити кількість векторів атаки, хорошим рішенням безпеки є включення тільки тих методів HTTP, які потрібні API. Якщо ваша кінцева точка повинна обслуговувати дані клієнтам у відповідь на запити HTTP GET, вам слід подбати про це  щоб він не повертав нормальні відповіді при зверненні за допомогою будь-яких інших методів HTTP. Замість цього він може повернути  код стану методу 405 Заборонено або 501 Не реалізовано, щоб повідомити клієнтів про те, що запитуваний метод HTTP не підтримується. Чим більше методів HTTP відповідає кінцева точка, тим більша ймовірність виникнення вразливостей. Наприклад, TRACE – це метод HTTP, який, як відомо, використовується в атаках міжсайтового трасування (XST), тому його слід вмикати лише за необхідності.

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

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

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

Резюме

  • Розділяючи тести на звичайні, перевірку граничних значень, некоректного та екстремального введення, ви можете інтегрувати механізми безпеки у свої набори модульних випробувань.

  • Модуль регулярних виразів може виконувати неефективне зворотне відстеження, тому ви повинні перевіряти довжину введення, яке йому передається.

  • Перемикачі функціональності можуть стати причиною уразливостей безпеки. Боротися з цим можна шляхом перевірки механізмів перемикання за допомогою автоматизованих тестів.

  • Хороший і практичний підхід полягає у створенні тесту для кожного перемикача, який ви додаєте. Ви також повинні тестувати всі можливі їх поєднання.

  • Остерігайтеся комбінаторної складності, що виникає, коли перемикачів стає надто багато. Щоб цього уникнути, найкраще звести кількість перемикачів до мінімуму.

  • Механізм перемикання сам собою предмет аудиту і обліку.

  • Впровадження автоматизованих тестів безпеки в конвеєр складання може дозволити вам виконувати обмежені тести на проникнення так часто, як хочеться.

  • Доступність – це важливий аспект безпеки, який слід враховувати у будь-якій системі.

  • Імітація DoS-атак допомагає краще зрозуміти слабкі сторони загальної архітектури програми.

  • Захиститися від DoS-атаки на доменні правила надзвичайно складно, оскільки звичайного використання системи її відрізняє лише намір.

  • Багато проблем безпеки викликані неправильною конфігурацією, за чиною якої може бути внесення змін (навмисне або ненавмисне) або нерозуміння конфігураційних параметрів.

  • Гарячі точки конфігурації є хорошими індикаторами тих ділянок, тестувати які потрібно в першу чергу.

  • Ви повинні знати, як використовувані вами інструменти поводяться за умовчанням, і перевіряти цю поведінку за допомогою тестів.

Ми використовували матеріали з книги “Безпека by design”, які  написали Дэн Берг Джонсон, Дэниел Деоган, Дэниел Савано.

Інші статті по темі
ОсвітаСамонавчання
Читати далі
Безпека by design. Частина 1. (Роль проектування у безпеці)
Проектування відіграє надзвичайно важливу роль у гарантуванні безпеки в різних сферах, від технологій до інфраструктури та бізнесу. Цей процес є фундаментальною складовою для створення рішень, які ефективно запобігають загрозам, забезпечують конфіденційність та зберігають цінності. Розглянемо ключові аспекти ролі проектування у забезпеченні безпеки.
689
ОсвітаСамонавчання
Читати далі
Безпека by design. Частина 2. (Антракт: анти-“Гамлет”)
У цій статті описується роль глибокого моделювання у забезпеченні безпеки інформаційних систем та бізнес-цілісності. Розглядаються ризики поверхового моделювання, які можуть призвести до недостатнього рівня захисту та дефективної безпеки.
482
ОсвітаСамонавчання
Читати далі
Безпека by design. Частина 3. (Основні концепції предметно-орієнтованого проектування)
Основні Концепції Предметно-Орієнтованого Проектування полягає в поясненні важливих концепцій цього підходу, включаючи абстракцію, глибокий аналіз предметної області та покращення процесів розробки. Виокремлені аспекти покращення якості та продуктивності роботи, що досягаються завдяки використанню предметно-орієнтованого проектування.
602
ОсвітаСамонавчання
Читати далі
Безпека by design. Частина 4. (Концепції програмування, що сприяють безпеки)
Застосування цих концепцій у програмуванні сприяє підвищенню рівня інформаційної безпеки. Вони допомагають уникнути загроз та вразливостей, забезпечуючи захист даних та надійність програмних продуктів.
495
ОсвітаСамонавчання
Читати далі
Безпека by design. Частина 5. (Доменні притиви)
У цій частині. Як доменні примітиви допомагають писати безпечний код. Боротьба з витоком даних за допомогою об'єктів одноразового читання. Поліпшення сутностей з допомогою доменних примітивів. Ідеї, запозичені із аналізу помічених даних.
485
Знайшли помилку?
Якщо ви знайшли помилку, зробіть скріншот і надішліть його боту.