Мистецтво оборонного програмування

image
Чому розробники не можуть написати безпечний код? Ми не говоримо тут черговий раз про «чистий код». Ми говоримо про більшому з чисто практичної точки зору — щодо надійності і безпеки програмного забезпечення. Так, тому що небезпечне програмне забезпечення в значній мірі марно. Подивимося, що означає «небезпечне» програмне забезпечення:

  • Політ №501 ракети «Аріан-5» Європейського космічного агентства був припинений через 40 секунд після старту (4 червня 1996 р.). Експериментальна ракета-прототип вартістю 1 млрд. доларів США самоліквідувалася через помилки у бортовому З управління.
  • Помилка у програмі, яка керувала установкою променевої терапії Therac-25, стала прямою причиною смерті, як мінімум, п'яти пацієнтів у 80-х роках, коли вона ставила надмірні дози рентгенівського опромінення.
  • Програмна помилка в зенітному ракетному комплексі MIM-104 «Patriot», що викликала догляд його системних годин на одну третину секунди за сто годин, призвела до його нездатності виявити і перехопити ракету. Іракська ракета потрапила у військову частину в р. Дахран, Саудівська Аравія (25 лютого 1991 р.), загинуло 28 американців.
Цього повинно бути достатньо, щоб зрозуміти, наскільки важливо писати безпечне і працююче програмне забезпечення, особливо для визначених додатків. Але і в інших випадках використання ми повинні знати, до чого можуть привести наші програмні помилки.

Перше знайомство з захисним програмуванням (проектування самотестирующих і самокорректирующих програм)
Чому я думаю, що захисне (оборонна) програмування є хорошим підходом для рішення цих проблем у певному вигляді проектів?
Захищайтеся від неможливого, тому що станеться саме воно.
Є багато визначень для терміна «захисне (оборонна) програмування»; вони залежать від рівня «безпеки» і рівня ресурсів, потрібних для ваших програмних проектів.
Захисне програмування є деякою формою захисного (оборонного) проектування, призначеного для забезпечення безперервного функціонування деякої частини в непередбачених обставинах. Методи захисного програмування часто використовують там, де потрібні висока доступність, безпека та надійність (Вікіпедія).
Я особисто вважаю, що даний підхід доцільний, коли є великий і довговічний проект, в який залучено багато людей. Також це підходить, наприклад, для такого проекту з відкритим вихідним кодом, який вимагає значного і постійного обслуговування.

Давайте розглянемо деякі з моїх спрощених ключових моментів, спрямованих на досягнення підходу захисного програмування.

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

image
Кращий захист — це напад

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

Кращий захист — це напад. Будьте суворими.

Використовуйте абстрактне представлення баз даних
Першою з топ-10 ЗА вразливостей згідно OWASP є ін'єкції. Це означає, що хтось (чи багато людей) ще не використовує безпечні інструменти для звернення до своїх баз даних. Використовуйте пакети і бібліотеки абстрактного представлення баз даних. В мові PHP можна використовувати PDO (portable розподілений об'єкт), щоб забезпечити базову захист від ін'єкцій.

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

Саме це називають розумним багаторазовим використанням коду. Користуйтеся цією можливістю.

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

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

Пишіть НАДІЙНИЙ код
Це важка справа для (захисного) програміста — написати належний код. Про це багато знають і говорять, але насправді мало хто піклується або приділяє достатньо уваги та зусиль для того, щоб отримати НАДІЙНИЙ код.

Розглянемо кілька прикладів неналежного підходу.

Не робіть так: неініціалізовані властивості

<?php
class BankAccount
{
protected $currency = null;
public function setCurrency($currency) { ... }
public function payTo(Account $to, $amount)
{
// sorry for this silly example
$this->transaction->process($to, $amount, $this->currency);
}
}
// I forgot to call $bankAccount->setCurrency('GBP');
$bankAccount->payTo($joe, 100);

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

У цьому випадку ми зробили ще краще, інкапсулюючи неинициализированное властивість об'єкт Money:

<?php
class BankAccount
{
public function payTo(Account $to, Money $money) { ... }
}
$bankAccount->payTo($joe, new Money(100, new Currency('GBP')));

Захистіть програму від неправильного поводження. Не використовуйте неініціалізовані властивості об'єкта.

Не робіть так: що дає витік стан за межами області дії класу

<?php
class Message
{
protected $content;
public function setContent($content)
{
$this->content = $content;
}
}
class Mailer
{
protected $message;
public function __construct(Message $message)
{
$this->message = $message;
}
public function sendMessage(){
var_dump($this->message);
}
}
$message = new Message();
$message->setContent("bob message");
$joeMailer = new Mailer($message);
$message->setContent("joe message");
$bobMailer = new Mailer($message);
$joeMailer->sendMessage();
$bobMailer->sendMessage();

У цьому випадку Message (Повідомлення) передається за посиланням, і результат буде в обох випадках «joe message» («повідомлення Джо»). Рішенням могло б бути клонування об'єкта повідомлення у конструкторі Mailer. Але що ми завжди повинні намагатися зробити, так це використовувати (незмінний), об'єкт-значення замість простого змінюваного об'єкта Message. Використовуйте незмінні об'єкти, коли ви можете.

<?php
class Message
{
protected $content;
public function __construct($content)
{
$this->content = $content;
}
}
class Mailer
{
protected $message;
public function __construct(Message $message)
{
$this->message = $message;
}
public function sendMessage(
{
var_dump($this->message);
}
}
$joeMailer = new Mailer(new Message("bob message"));
$bobMailer = new Mailer(new Message("joe message"));
$joeMailer->sendMessage();
$bobMailer->sendMessage();

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

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

Спасибі за прочитання!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.