防守式编程的艺术

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

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

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

  • 在 20 世纪 80 年代,一个治疗机中控制 Therac-25 辐射的的代码错误,导致其施用过量的 X 射线致使至少五名患者死亡。

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

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

防守式编程角度一

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

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

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

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

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

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

从不相信用户输入

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

The best defense is a good offense

进攻就是最好的防守

(将输入)列入白名单而不是把它放到黑名单中,例如,当验证图像扩展名时,不检查无效的类型,而是检查有效的类型,排除所有其余的类型。 在 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日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
2月前
|
人工智能 搜索推荐 算法
编程之舞:从代码到艺术的蜕变
【10月更文挑战第30天】在数字世界的无限舞台上,编程不仅仅是冰冷的逻辑和枯燥的算法。它是创造者手中的画笔,是构建梦想的乐章。本文将带你领略编程背后蕴含的艺术之美,探索如何通过代码示例将技术与创造力结合,从而让程序设计成为一种独特的艺术表达。
40 2
|
2月前
|
存储 设计模式 算法
探索编程之美:从代码到艺术的旅程
【10月更文挑战第22天】在数字世界中,代码不仅仅是指令的集合,更是创造力和逻辑的结晶。本文将带领读者踏上一场思维之旅,探索编程背后的美学和哲学,揭示如何通过代码实现技术与艺术的完美融合。从基础的数据结构到复杂的算法设计,我们将一起见证编程如何从简单的指令序列转变为解决问题的艺术形式。
|
3月前
|
算法 搜索推荐 程序员
编程之舞:从代码到艺术的转变
【9月更文挑战第35天】本文旨在探索编程不仅仅是技术操作的集合,更是一种创造性的艺术表达。我们将通过具体的编程示例和技巧,展示如何将代码转化为富有美感和效率的作品。文章将引导读者理解编程背后的哲学和美学原则,从而提升他们的编码技能和审美意识。
55 3
|
4月前
|
机器学习/深度学习 敏捷开发 测试技术
软件测试的艺术:从代码到用户心灵的旅程
在阅读本文之前,让我们先共同思考一个问题:“为什么即使是最小的错误,也可能对用户体验和企业声誉造成巨大的影响?” 正如我们将要探讨的,软件测试不仅是技术活动的一种,更是确保产品质量、优化用户体验和维持品牌声誉的关键步骤。本文将引导您了解软件测试的基本概念,探索其背后的艺术性,以及如何高效地实施测试策略来达到最佳的质量保证结果。
36 0
|
5月前
|
算法 前端开发 JavaScript
编程之舞:从代码到艺术的演变
【8月更文挑战第27天】在数字世界的舞台上,代码不仅仅是冷冰冰的指令序列,它们像音符一样组合成旋律,编织出功能与美的交响曲。本文将带您领略编程的艺术性,探索如何通过代码实现技术与美学的和谐统一。
|
5月前
|
算法 程序员 开发工具
代码之舞:探索编程的艺术与实践
【8月更文挑战第7天】 在数字世界的舞台上,每一行代码都是舞者的步伐,每一个算法都是编舞的旋律。本文将带领读者穿梭于编程的世界,从初学者的踌躇满志到资深开发者的从容不迫,揭示技术成长之路上的心得体会。我们将探讨如何通过持续学习、实践和反思,不仅提升技术能力,更培养对编程艺术的感悟与欣赏。
57 3
|
5月前
|
算法 JavaScript 前端开发
编程之舞:从代码到艺术
【8月更文挑战第30天】在数字世界的舞台上,代码不仅仅是指令的堆砌,它更像是一种语言,一种能够创造无限可能的艺术。本文将带你走进编程的世界,探索如何将枯燥的代码转化为富有创造力的艺术作品,从而开启一段技术与艺术交织的旅程。
|
6月前
|
数据采集 算法 大数据
代码之舞:探索软件开发的艺术与科学
在软件工程的广阔天地里,编程不仅仅是一系列指令的堆砌,它更像是一场精心编排的舞蹈。本文将深入探讨软件开发中的艺术性和科学性如何交织在一起,通过实际案例分析,揭示高效编码背后的逻辑美学和创造性思维。我们将一同穿梭于代码行间,体验技术与创新的完美融合,感受那些让软件项目从平凡走向卓越的微妙之处。 【7月更文挑战第21天】
78 1
|
5月前
|
算法
编程之舞:技术感悟与生活哲学的交织
【8月更文挑战第16天】在数字世界的舞台上,编程不仅是技术的展示,更是思考的艺术。它如同一场精心编排的舞蹈,每一个动作、每一次旋转都蕴含着深远的意义。本文将带领读者走进编程的内在世界,探索那些看似晦涩难懂的代码背后所隐藏的生活哲学和情感表达。通过个人的技术感悟,我们将一同见证编程如何超越语言的界限,成为一种独特的沟通方式,连接着人与人、人与机器之间的思想与灵魂。
|
7月前
|
设计模式 算法 程序员
代码的诗意:技术与艺术的交织
【6月更文挑战第28天】在数字世界的构建中,编程往往被视为一项枯燥且逻辑性强的技术活动。然而,当我们深入探究时,会发现编程不仅涉及逻辑和算法,还蕴含着一种独特的艺术美。本文将探讨编程如何融合技术性和艺术性,揭示代码背后的诗意及其对创造性思维的促进作用。通过个人的技术感悟,我们将看到,编程不仅是科技的产物,也是人类创造力的展现。
50 1