📜 ⬆️ ⬇️

The art of defensive programming

image

Why can't developers write safe code? We do not speak here again about the "clean code" . We are talking about more from a purely practical point of view - about the reliability and security of software. Yes, because unsafe software is pretty much useless. Let's see what “unsafe” software means:


This should be enough to understand how important it is to write safe and properly working software, especially for certain applications. But in other cases of use, we need to know what our program errors can lead to.

First acquaintance with defensive programming (designing self-testing and self-correcting programs)


Why do I think defensive (defensive) programming is a good approach to solving these problems in a certain kind of projects?
Protect yourself from the impossible, because that is what will happen.
There are many definitions for the term "defensive (defensive) programming"; they depend on the level of “security” and the level of resources required for your software projects.
Defensive programming is a form of defensive (defensive) design designed to ensure the continuous operation of a certain part of software in unforeseen circumstances. Security programming techniques are often used where high availability, security, and reliability are required ( Wikipedia ).
I personally think that this approach is expedient when there is a large and long-lived project in which many people are involved. It is also suitable, for example, for such an open source project, which requires substantial and continuous maintenance.

Let's look at some of my simplified key points to achieve a defensive programming approach.
')

Never trust user input


Assume always that you can get what you do not expect. This should be your approach as a programmer involved in defensive programming in relation to user input or, in general, to everything that goes into your system. Because, as mentioned above, the impossible is possible. Try to be as strict as possible. Prove that your input values ​​are what you expect.

image

Best defense is attack

Do white, not black lists, for example, when checking the image extension, do not check for invalid types, but check for valid types, excluding everything else. For example, in the PHP language, you also have a lot of open source verification libraries that simplify your work.

Best defense is attack. Be strict.

Use an abstract database view


The first of the top 10 software vulnerabilities according to OWASP are injections. This means that someone (or many people) does not yet use secure tools to access their databases. Use packages and libraries of abstract database representation. In PHP, you can use PDO (Portable Distributed Object) to provide basic protection against injections .

Do not reinvent the wheel


Are you not using a framework (or microformrvk)? Well, it means you like to do extra work without any need, congratulations! This applies not only to frameworks, but also to many other new features, where you can easily use what has already been done, well tested, what thousands of developers trust and that works steadily ; It is not worthwhile to engage in a demonstration of your skill only for the love of one. The only reason to do something yourself is the need to get something that does not exist or that exists, but does not meet your requirements (poor operating parameters, missing characteristics, etc.).

This is what is called intelligent code reuse . Take advantage of this opportunity.

Do not trust the developers


Protective programming is akin to what motorists call “cautious” driving . When “careful” driving, we assume that everyone around us may act improperly. Thus, we must be careful, carefully tracking the behavior of others. The same applies to defensive programming when we, as developers, must not trust the code of other developers . Nor should we trust our own code.

In large projects involving many people, there can be many different ways of writing and organizing programs. This can create confusion and generate errors. That is why we must apply development styles and a code quality controller that simplify our lives.

Write a reliable code


This is a difficult thing for a (defensive) programmer to write the proper code . Many people know and talk about this, but in reality few people care or pay enough attention and effort to get a RELIABLE code .

Consider a few examples of inappropriate approach.

Do not do this: uninitialized properties

<?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); 

In this case, you need to remember that in order to make a payment, you must first call setCurrency . This is indeed a bad approach — a state transition operation, such as indicated (making a payment), should not be carried out in two stages using two (or more) public methods. You can have many payment methods, but we are obliged to have only one simple public method for changing status (it is absolutely unacceptable to have objects in an uncertain state) .

In this case, we did even better by encapsulating an uninitialized property into a Money object:

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

Protect the program from abuse. Do not use uninitialized object properties.

Do not do this: a leaky state outside the class scope

 <?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(); 

In this case, the Message is passed by reference, and the result will be in both cases the “joe message” (“Joe message”). The solution would be to clone the message object in the Mailer constructor. But what we should always try to do is use a ( immutable ) value object instead of a simple variable Message object. Use immutable objects when you can.

 <?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(); 

Write tests


Do you still need to talk about this? Writing unit tests will help you withstand general principles, such as strong connectivity, personal responsibility, loose coupling, and proper composition of objects . This will help test not only a working small module, but also a way of structuring your objects. Indeed, you will see clearly by testing your small functions, how many modules you need to test and how many objects you need to simulate in order to get 100% coverage of code tests.

findings


Hope you enjoyed the article. Remember that here are just suggestions. It is up to you when and where to apply them and whether to apply them at all.

Thanks for reading!

Source: https://habr.com/ru/post/320088/


All Articles