防守式编程的艺术

本文讲的是防守式编程的艺术,


为什么开发人员不编写安全代码? 我们不再在这里讨论 “干净的代码” 。我们从一个纯粹的角度,软件的安全性来讨论更多的东西。是的,因为一个不安全的软件几乎是没用的。让我们来看看不安全的软件意味着什么。

  • 欧洲航天局的 Ariane 5 Flight 501 在起飞后 40 秒(1996年6月4日)被毁。10 亿美元的原型火箭由于机载导航软件中的错误而自毁。
  • 在 20 世纪 80 年代,一个治疗机中控制 Therac-25 辐射的的代码错误,导致其施用过量的 X 射线致使至少五名患者死亡。
  • MIM-104 爱国者的软件错误导致其系统时钟在 100 小时时段内偏移三分之一秒,以至于无法定位和拦截来袭导弹。伊拉克导弹袭击了沙特阿拉伯在达哈兰的一个军事大院( 1991 年 2 月 25 日 ),杀害了 28 名美国人。

这些例子足以让我们认识到编写安全的软件,特别是在某些情况下是多么重要。在其他使用情况下,我们也应该知道我们软件错误会带给我们什么。

防守式编程角度一

为什么我认为防守式编程在某些项目中是一个发现这些问题的好方法?

防御不可能,因为不可能将可能发生。

对于防御性编程有很多定义,它还取决于安全性的级别和您的软件项目所需的资源级别。

防守式编程是一种防守式设计,旨在确保在意外的情况下软件的持续性功能,防守式编程实践常被用在高可用性,需要安全的地方 — 维基百科

我个人认为这种方法适合当你处理一个大的、长期的、有许多人参与的项目。 例如,需要大量维护的开源项目。

为了实现防守式编程方法,让我谈谈我个人简陋的观点。

从不相信用户输入

假设你总是会收到你意料之外的东西。这应该是你作为防守式程序员的方法,针对用户输入,或者平常进入你的系统的各种东西。因为我们可以预料到意想不到的,尽量做到尽可能严格。断言你的输入值是你期望的。

进攻就是最好的防守

(将输入)列入白名单而不是把它放到黑名单中,例如,当验证图像扩展名时,不检查无效的类型,而是检查有效的类型,排除所有其余的类型。 在 PHP 中,也有无数的开源验证库来使你的工作更容易。

进攻就是最好的防守,控制要严格。

使用数据抽象

OWASP 十大安全漏洞 中的第一个是注入。这意味着有人(很多人)还没有使用安全工具来查询他们的数据库。请使用数据库抽象包和库。在 PHP 中你可以使用 PDO 来确保基本的注入保护

不要重复造轮子

你不用框架(或微框架)? 你就是喜欢没有理由的做额外的工作。恭喜你!只要是经过良好测试、广受信任的稳定的代码,你就可以尽管用于各种新特性(不仅是框架)的开发,而不是只因为它是已经造好的轮子的缘故而重新造轮子。你自己造轮子的唯一原因是你需要一些不存在或存在但不适合你的需求(性能不佳,缺少的功能等)。

那个(使用框架)我们称它为智能代码重用,它值得拥有。

不要信任开发人员

防守式编程可以与称为**防御性驾驶的东西相关。在防御驾驶中,我们假设我们周围的每个人都有可能犯错误。 所以我们必须小心别人的行为。这些同样适用于我们的防守式编程,作为开发者,我们不应该相信其他开发者**。我们也同样不应该信任我们的代码。

在许多人参与的大项目中,我们可以有许多不同的方式来编写和组织代码。 这也可能导致混乱,甚至更多的错误。 这就是为什么我们统一编码风格和使用代码检测器会使我们的生活更加轻松。

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

总结

希望你喜欢这篇文章。 记住这些只是建议,何时、何地采纳这些建议,这取决于你。






原文发布时间为:2017年2月3日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-10-27 15:58:50

防守式编程的艺术的相关文章

契约式编程与防御式编程

背景 事情的来由还要从几十几亿年前的一次星球大爆炸说起,sorry,背错台词了,是从几天前讨论接口返回数据和几个月前讨论课件本地数据结构说起,简单的说,就是碰到约定好的内容出现异常,是我们在程序中内部作兼容处理,还是抛出去. 打个比方,我们要解析一段json,约定这个json的格式,只能是正常格式,或者是空,那么一旦返回json的方法返回了一个『既不是正常格式,又不是空的异常值』,程序该如何处理呢? 小花:一旦碰到约定异常,程序必须兼容处理,一定不能让程序Crash小Fa:一旦碰到约定异常,就必

华山论剑之契约式编程与防御式编程

背景 事情的来由还要从几十几亿年前的一次星球大爆炸说起,sorry,背错台词了,是从几天前讨论接口返回数据和几个月前讨论课件本地数据结构说起,简单的说,就是碰到约定好的内容出现异常,是我们在程序中内部作兼容处理,还是抛出去. 打个比方,我们要解析一段json,约定这个json的格式,只能是正常格式,或者是空,那么一旦返回json的方法返回了一个『既不是正常格式,又不是空的异常值』,程序该如何处理呢? 小花:一旦碰到约定异常,程序必须兼容处理,一定不能让程序Crash 小Fa:一旦碰到约定异常,就

.NET 4.0中的契约式编程

契约式编程不是一门崭新的编程方法论.C/C++ 时代早已有之.Microsoft 在 .NET 4.0 中正式引入 契约式编程库.博主以为契约式编程是一种相当不错的编程思想,每一个开发人员都应该掌握.它不但 可以使开发人员的思维更清晰,而且对于提高程序性能很有帮助.值得一提的是,它对于并行程序设计 也有莫大的益处. 我们先看一段很简单的,未使用契约式编程的代码示例. // .NET 代码示例public class RationalNumber{ private int numberator;

.net非控件式编程的优越性

上个月头因为幻想曲同志的的造访,使得VIEW进入了重构期.将原有的架构全部推翻,使用标准的工厂三层模式,代码全部重写,加入缓存依赖,简化存储过程,改进整个项目的容错机制,等等. 整整一个月的奋战总算是重构完成,在重构中仍然保持不使用框架,不使用可视化控件.其实在重构之前有些朋友强烈推荐我使用 EnterpriseLibrary, 以及 Nhibernate 作为数据访问层框架.不过抱着学习底层知识的态度,我没有使用,一切方法都是纯手工打造. 在重构中最头疼的就是写实体层(valueobject)

为什么响应式编程并非一时之势?

[编者按]本文作者为 David Buschman,文章从程序架构与系统的发展历程出发,逐步论证了为什么响应式编程并非一时之势,而是能带来更快处理速度,更高硬件利用率的未来选择.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 这些年来,程序架构和系统发生了不少变化.大部分情况下,这些变化都跟它们依托的硬件密切相关.软件架构到底是从何处起源,众说纷纭,而且对构架的实际构成部分也有各种定义.本文将从整体化应用的兴起来展开讨论. 摩尔定律 当你的所有资源都在单机上时,把所有的代码存在一个地方

link中如何实现链式编程,后面的拿到前面的方法并且提前结束?

问题描述 link中如何实现链式编程,后面的拿到前面的方法并且提前结束? link中如何实现链式编程,后面的拿到前面的方法并且提前结束? 解决方案 参考:http://tech.it168.com/d/2008-01-18/200801181226825.shtml 也可以自己实现IEnumerable<T>,用yield break

观察者模式 – 响应式编程 [Android RxJava2](这到底是什么):第一部分

本文讲的是观察者模式 – 响应式编程 [Android RxJava2](这到底是什么):第一部分, 哦,我们又过了一天,是时候来学习新东西让这一天变得很棒了. 大家好,希望你们做的更好.今天我打算开始一个关于 Rx Java2 的新系列,但是首先的 2-3 篇文章是关于响应式编程的.希望我们能够学到新的东西,然后一起消除所有的困惑. 动机: 说实话,我一开始学习 Rx 的时候遇到了许多问题.我尝试了许多教程.书籍,但是最后我都无法在我的应用里面使用 Rx.许多教程让我感到很困惑,就像有些部分说

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式 - 响应式编程 [Android RxJava2](这到底是什么)第三部分

本文讲的是函数式接口.默认方法.纯函数.函数的副作用.高阶函数.可变的和不可变的.函数式编程和 Lambda 表达式 - 响应式编程 [Android RxJava2](这到底是什么)第三部分, 太棒了,我们又来到新的一天.这一次,我们要学一些新的东西让今天变得有意思起来. 大家好,希望你们都过得不错.这是我们的 RxJava2 Android 系列的第三篇文章. 第一部分 第二部分 在这篇文章中,我们将讨论函数式的接口,函数式编程,Lambda 表达式以及与 Java 8 的相关的其它内容.这

[译] iOS 响应式编程:Swift 中的轻量级状态容器

本文讲的是[译] iOS 响应式编程:Swift 中的轻量级状态容器, 原文地址:Reactive iOS Programming: Lightweight State Containers in Swift 原文作者:Tyler Tillage 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:deepmissea 校对者:FlyOceanFish iOS 响应式编程:Swift 中的轻量级状态容器 事物的状态 在客户端架构如何工作上,每一个 iOS