• The Art of Defensive Programming
  • Diego Mariani
  • The Nuggets translation Project
  • Translator: GiggleAll
  • Proofread by: tanglie1993, FGHPDF

Why don’t developers write secure code? We’re not talking about “clean code” here. Let’s talk more about software security from a pure point of view. Yes, because an insecure software is almost useless. Let’s take a look at what insecure software means.

  • The European Space Agency’s Ariane 5 Flight 501 was destroyed 40 seconds after takeoff (4 June 1996). The $1 billion prototype rocket self-destructed due to an error in the onboard navigation software.

  • In the 1980s, an error in the code used to control therAC-25 radiation in a treatment machine led to the overdose of X-rays that killed at least five patients.

  • A software error in the Mim-104 Patriot caused its system clock to shift by a third of a second over a 100-hour period, making it impossible to locate and intercept incoming missiles. Iraqi missiles attacked a Saudi Military compound in Dahlan (25 February 1991), killing 28 Americans.

These examples are enough to remind us how important it is to write secure software, especially under certain circumstances. In other use cases, we should also know what our software bugs can do to us.

Defensive programming Angle one

Why do I think defensive programming is a good way to find these issues on some projects?

Defense is impossible, because the impossible will be possible.

There are many definitions for defensive programming, and it also depends on the level of security and the level of resources required by your software project.

Defensive programming is a defensive design designed to ensure the continuity of software functionality in unexpected situations. Defensive programming practices are often used in highly available, secure places – Wikipedia

I personally think this approach works well when you’re dealing with a large, long-term project with many people involved. For example, open source projects that require a lot of maintenance.

In order to implement a defensive approach to programming, let me share my humble opinion.

Using data abstraction

The first of OWASP’s top 10 security vulnerabilities is injection. This means that there are people (many people) who haven’t used security tools to query their databases. Use database abstraction packages and libraries. In PHP you can use PDO to ensure basic injection protection.

Don’t trust developers

Defensive programming can be associated with something called defensive driving. In defensive driving, we assume that everyone around us is likely to make a mistake. So we have to be careful what other people do. The same applies to our defensive programming, and as developers, we shouldn’t trust other developers. We shouldn’t trust our code either.

In large projects with many people involved, there are many different ways to write and organize code. This can also lead to confusion and even more mistakes. That’s why our uniform coding style and use of code detectors makes our lives easier.

Do not: uninitialized attributes

<? 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);Copy the code

In this case, we must remember that in order to send the payment, we need to call the setCurrency first. This is a very bad thing, and a state change operation like this (issuing a payment) should not use two (or more) public methods in two steps. We can still have many ways to pay, but we must have only one simple public method to change the state (objects should never be in an inconsistent state).

In this case, we could do a better job of encapsulating the uninitialized properties into the Money object.

<? php class BankAccount { public function payTo(Account$to,Money$money){ ... } } $bankAccount->payTo($joe,newMoney(100,newCurrency('GBP')));Copy the code

Make it foolproof. Do not use uninitialized object attributes.

Do not: Exposed state outside of 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();Copy the code

In this case, the message is passed by reference, and the result will be “Joe Message” in both cases. The solution is to clone the message object in the Mailer constructor. But we should always try to use an (immutable) value object instead of a simple Message mutable 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();Copy the code

conclusion

Hope you enjoyed this article. Remember that these are just suggestions, and it’s up to you when and where to take them.