[译] 防守式编程的艺术。防御性代码的艺术,对bug说再见。

  • 原文地址:The Art of Defensive
    Programming

为何开发人员不修安全代码?

  • 初稿作者:Diego
    Mariani
  • 译文出自:掘金翻译计划
  • 译者:GiggleAll
  • 校对者:tanglie1993
    , fghpdf

咱俩以这边不再谈论“干净的代码”。
我们在讨论更多之事物,从纯的履行角度看,软件的安全性以及长治久安。是的,因为一个未安全的软件几乎没因此。

怎开发人员不修安全代码? 我们不再在此间讨论 “绝望之代码
。我们打一个纯粹的角度,软件之安全性来谈谈还多的东西。是的,因为一个休安全之软件几乎是不曾因此之。让咱来探不安全之软件意味着什么。

被我们看不安全之软件带来的损失。

  • 欧洲航天局的 Ariane 5 Flight 501 在起飞后 40
    秒(1996年6月4日)被坏。10
    亿美元之
    原型火箭由机载导航软件面临之失实而自毁。

  • 在 20 世纪 80 年代,一个治疗机中决定 Therac-25
    辐射的之代码错误,导致该予以用过量的 X 射线致使至少五誉为患者身故。

  • MIM-104 爱国者的软件错误致该系时钟在 100
    小时上内偏移三分之一秒,以至于无法稳定及拦截来袭导弹。伊拉克导弹袭击了沙特阿拉伯在达哈兰底一个人马大院(
    1991 年 2 月 25 日 ),杀害了 28 称为美国丁。

欧洲航天局的Ariane 5 Flight 501在起飞后40秒(1996年6月4日)被磨损。
10亿美元的原型火箭由于机载指导软件面临之失实而自毁。

这些事例可以被我们认识及编辑安全之软件,特别是在某些情况下是多么重要。在其它使用情况下,我们呢理应亮我们软件错误会带来吃咱们啊。

决定Therac-25放射治疗机的代码中的不当直接促成了以80年代,当那给予用过量的X射线时,至少要五名为患儿去世。

防守式编程角度一

何以我以为防守式编程在少数种类中是一个发觉这些题材之好点子?

看守不容许,因为未可能将可能发。

对于防御性编程有成千上万概念,它还在安全性的级别及而的软件类所待的资源级别。

防守式编程是一种防守式设计,旨在确保在意外的情下软件的连绵功能,防守式编程实践时叫用当高可用性,需要安全的地方

维基百科

自我个人认为这种办法可当您处理一个雅的、长期的、有成千上万口踏足的花色。
例如,需要大量保障的开源项目。

以兑现防守式编程方法,让自身谈谈自己个人简陋的观。

MIM-104爱国者的软件错误致该系时钟在100时之内漂移三分之一,导致无法稳定和阻止入射导弹。
伊拉克导弹袭击了沙特阿拉伯达哈兰一个三军大院(1991年2月25日),造成28称作美国口去世。

从不相信用户输入

若是你总是会吸纳你意料之外的东西。这应当是您作为防守式程序员的艺术,针对用户输入,或者平常上而的网的各种东西。因为我们可以预料到意想不到的,尽量做到尽量严格。断言)你的输入值是公希望的。

图片 1

The best defense is a good offense

击就是极端好之守卫

(将输入)列入白名单而休是将她坐黑名单中,例如,当证图像扩展名时,不反省无效的路,而是检查中的型,排除拥有其他的档次。
在 PHP 中,也发多底开源验证库来要您的办事还便于。

击就是绝好之守,控制而从严。

立即应该可以理解安全软件多么重要,特别是在某些圈子被有差不多重要。
但在其余应用状况下,我们该掌握我们的软件错误将见面造成什么伤害。

使数据抽象

OWASP
十不行安全漏洞

中之首先单凡是流入。这代表有人(很多总人口)还从来不动用安全工具来查询他们的数据库。请以数据库抽象包以及货栈。在
PHP 中而可以
PDO
来保基本的流入保护。

图片 2

毫不还过去轮子

卿不用框架(或微框架)?
你便是爱无理由的做额外之干活。恭喜你!只要是经精美测试、广于信赖的安居乐业之代码,你尽管可尽管用于各种新特点(不仅是框架)的付出,而非是独自坐其是一度前往好之车轱辘的原由而重新造轮子。你协调过去轮子的绝无仅有因是若需要部分免存在或者有不过非符合您的求(性能不漂亮,缺少的作用等)。

万分(使用框架)我们遂其吗智能代码用,它值得拥有。

防御编程的首先征

永不相信开发人员

防守式编程可以跟名防御性驾驶的物有关。在防卫驾驶中,我们设我们周围的每个人还有或发错误。
所以我们要小心别人的表现。这些平适用于俺们的防守式编程,作为开发者,我们无应有相信任何开发者。我们呢一律未应当相信我们的代码。

每当众多人踏足的慌项目蒙,我们得以产生那么些见仁见智的法子来编排和团组织代码。
这吗恐怕致乱,甚至又多之缪。
这就是是为何我们联合编码风格和行使代码检测器会如我们的在越自在。

缘何我觉着防御性编程在少数种类的种类被钻这些题目之好法子?

写SOLID代码

即时是本着一个防守式程序员困难的地方,writing code that doesn’t
suck
。这是众总人口领略与议论的政工,但未曾人确实关注或投入是的注意力和奋力来贯彻
SOLID代码

给我们来拘禁一些坏的例子。

毫无:未初始化的性

<?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,newMoney(100,newCurrency('GBP')));

如若它们万无一失。 决不使不初始化的对象属性

Don’t: Leaking state outside 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();

每当这种状态下,消息通过引用传递,结果以于个别种情形下都是 “joe
message”
。 解决方案是以 Mailer 构造函数中克隆消息对象。
但是咱应该总是尝试利用一个(不可变的)值对象失去替代一个概括的
Message mutable对象。当您得的下以不可变对象

<?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%底代码覆盖率。

防御性编程有诸多概念,它还取决于“安全性”级别跟你的软件类所要的资源级别。

总结

希你欣赏这篇文章。
记住这些只是是建议,何时、何地采纳这些建议,这取决你。

防御性编程是一样栽防御性设计,旨在确保在不足预见的景下软件的继续效力。防御性规划做法常用于需要高可用性,安全性还是稳定的地方

自个人认为这种方式可当您处理一个百般,使用周期长的差不多丁涉足种。例如,对于欲大量维护的开源项目。

叫咱探讨有简约的主要点,以落实防御性编程方法。

甭相信用户输入

假使你连会吸纳你不期待之物。那么你的措施应该当一个防御性程序,针对用户输入,或貌似进入你系统用户。这虽是咱们可预期到意外的结果。尽量做到尽量严格。断言您的输入值是若要的。

尽好之防御是攻击

列入白名单而非是不法名单,例如,当证图像扩展名时,不反省无效的品种,但检查中之花色,排除有其他的色。在PHP中,你也时有发生诸多底开源验证库,使你的工作再次易。

极好的守卫是一个吓之抢攻。要谨慎。

采取抽象数据库

OWASP十要命安全漏洞中之率先个是流入。这意味有人(很多口于那边)还没有使安全工具来查询他们之数据库。请动数据库抽象包以及仓库。在PHP中,您可以应用PDO来确保基本的流保护。

决不再次发明轮子

君无应用框架(或微框架)?你欢喜开额外的行事,没有理由,恭喜您!它不只是框架,而且对新的成效,你可充分轻地使,经过测试,受到广大的开发人员和安静之信赖,而无是只有为团结打造的东西。你应有好创造一个事物的绝无仅有因是你需要部分不在或者在但非称您的需要(性能不尽如人意,缺少的作用等)。

毫不相信开发人员

防御性编程可以跟防御性驾驶的事物有关。在防守驾驶负,我们若我们周围的每个人犹发或发错误。所以我们必须小心别人的行为。同样的定义呢适用于防御性编程,开发人员不应相信外开发人员的代码。也无应有相信我们团结一心之代码。

当死色面临,许多丁涉足,我们好起许多例外之方式来修和团体代码。这吗可能引致乱,甚至更多的左。所以我们相应规范编码风格。

写入SOLID代码

顿时是一个(防御)程序员的窘迫有,编写代码不抽烟。这是无数人知和座谈的事务,但从不丁真正关注或投入对的注意力和努力来兑现SOLID代码。

为我们看有百般的例子

未初始化的习性

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。
这是一个颇糟糕之业务,像这么的状态更改操作(发出付款)不应用简单(n)个国有措施在有限单步骤。
我们还是可来为数不少办法来付款,但是我们必须就来一个简易的共用艺术,以转状态(对象不应处于无雷同的状态)。

于这种状态下,我们做得重复好,将不初始化的属性封装到Money对象吃。

class BankAccount

{

public function payTo(Account $to, Money $money) { … }

}

$bankAccount->payTo($joe, new Money(100, new Currency(‘GBP’)));

毫不采用无初始化的靶子属性

型范围外的泄漏状态。

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

每当这种情景下,消息经引用传递,结果将于少数栽情形下还是“joe message”。
解决方案是以Mailer构造函数中克隆音对象。
但是咱们应该总是尝试使用一个(不可变的)值对象,而不是一个略的Message
mutable对象。 尽可能使用不可变对象。

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

}

}

$message = new Message();

$message->setContent(“bob message”);

$joeMailer = new Mailer($message);

$message->setContent(“joe message”);

$bobMailer = new Mailer($message);

$joeMailer->sendMessage();

$bobMailer->sendMessage();

在这种气象下,消息经引用传递,结果以以片种植情形下还是“joe message”。
解决方案是于Mailer构造函数中克隆信息对象。
但是我们理应总是尝试采用一个(不可变的)值对象,而不是一个简练的Message
mutable对象。 尽可能使用不可变对象。

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%底代码覆盖率。

图片 3

愿意您喜爱这篇文章。
记住这些只是是建议,下次你就算清楚什么时,在哪里用他们。

转载请告知。

相关文章