读程序员的制胜技笔记13_安全审查(上)

1. 安全

1.1. 关乎人类心理学

1.1.1. 接受开发者有着人类的弱点,主要的弱点就是对概率的错误估计

1.2. 安全从来不只跟软件和信息有关,也跟人和环境有关

1.2.1. 有不计其数的公司让它们的数据库在互联网上没有密码就可以被访问

1.3. 安全漏洞本身总是被叫作事故(incident),绝不是不负责任的

1.4. 安全,就像测试一样,是你的服务、数据和业务的可靠性的一个子集

1.5. 应当将与安全有关的决定看作可靠性技术债,它能帮你优化整个人生

1.6. 安全问题的不可避免也强调了事无绝对,没有绝对安全的系统

1.7. 完美的安全是不可能实现的,你总会遇到用户体验和安全之间的权衡

2. 复盘报告

2.1. postmortem blog post

2.2. 通常是在一次非常让人尴尬的安全事件发生之后写的,目的是清晰地、极尽细节地向管理层描述事件始末

2.2.1. 实际上是为了掩盖他们搞砸了的事实

3. 负责任的披露

3.1. responsible disclosure

3.2. 指的是那些没有在快速识别问题方面投入较多资源的公司,在利用充足的时间来修复问题后,再向公众公布修复安全漏洞的做法

3.3. 负责任的披露从一开始就应该被称为限时披露

4. 黑客之外

4.1. 安全可能还与那些你认为不相关的因素有关

4.2. “不要在星期五进行部署”

4.2.1. 这句话的逻辑很简单,即如果你把事情搞砸了,周末不会有人来处理,所以你应该在一周的第一天来进行一些高危操作

4.2.2. 周末存在的本身不是一个安全漏洞,但它仍然可能导致灾难性的结果

4.3. Facebook给开发者提供了一个API,让他们可以浏览用户的好友列表

4.3.1. 2016年,一个公司通过这些信息生成用户的政治倾向图,然后通过精准投放广告影响大选

4.3.2. 这个功能是完全按照需求来设计的,没有错误,没有安全漏洞,没有后门,也没有黑客介入

4.3.3. 某些人创造了这个功能,另一些人使用它,但是获得的数据却能违背他人意愿而操控他们,并因此导致伤害

5. 威胁模型

5.1. 对脑海里或者纸面上的威胁模型确定需要设置的安全措施的优先次序,并找出其中的漏洞

5.2. 最常见的威胁模型之一可能是用“反正我也没什么值得看的东西!”的想法来应对黑客攻击、平台管控,或者心怀恶意的前合作伙伴

5.3. 我们并不真正关心数据是否被泄露和滥用,这种情况产生的原因主要是我们缺乏想象力来思考数据的用途

5.4. 隐私就像安全带:你在大多数时间里不需要它,但当你需要它的时候,它可以救你的命

5.5. 实际的威胁建模涉及分析行为者(analyzing actor)、数据流(data flow)和信任边界(trust boundary)

5.6. 袖珍威胁模型

5.6.1. 你的应用程序的资产

5.6.1.1. 任何你不想丢失或泄露的东西都是资产,包括你的源代码、设计文档、数据库、私钥、API令牌、服务器配置,还有Netflix观看清单

5.6.2. 资产所处的服务器

5.6.2.1. 每台服务器都会被一些人访问,而每台服务器都会访问其他一些服务器

5.6.3. 信息敏感性

5.6.4. 访问资源的路径

5.7. 你服务器上的所有第三方组件都经过了数百万次的测试、错误修复和安全审计

6. 编写安全的网络应用程序

6.1. 在设计时考虑到安全问题

6.1.1. 安全是很难后续改造的,主要是因为所有的设计导致你一开始就写了不安全的代码

6.1.1.1. 设计时首先要考虑到安全问题,因为在既有基础上去改造安全措施是很难的

6.1.2. 审查你纸面的或脑海里的威胁模型

6.1.2.1. 了解风险,以及现在使之安全的成本和以后使之安全的成本

6.1.3. 决定你将把应用程序的秘密(数据库密码、API密钥)存储在哪里

6.1.3.1. 让它成为一个铁打不动的原则

6.1.4. 设计最少的权限

6.1.4.1. 代码不应该得到除它必须用到的之外的权限

6.1.4.2. 如果只有少数任务需要更高的权限,可以考虑将它们分解成单独的、隔离的实体

6.1.4.3. 尽可能在最低权限的账户下运行网页应用程序

6.1.5. 将安全原则应用于你的整个组织

6.1.5.1. 员工不应该访问他们执行日常任务时不需要的资源

6.1.5.2. CEO根本就不该有访问数据库或服务器的权限

6.1.5.3. 没有人可以被信任,而是因为他们的访问权可能会被外部人员恶意取得

6.2. 隐蔽性安全的用处

6.2.1. 软件安全是一场与时间的竞赛

6.2.1.1. 所有安全措施的唯一目的是为你赢得时间,让攻击者做无用功

6.2.2. 信息安全专家厌恶隐蔽性安全

6.2.2.1. 它不能为你赢得时间,或者也许它可以,但只是杯水车薪

6.2.3. 隐蔽性并不会给你带来真正的安全,但它偶尔会给你争取补救时间,直到你把问题解决掉

6.2.4. 边际安全并不是真正的安全

6.2.5. 当你在安全这件事上努力的程度是大是小都没什么区别而且风险很大时,更推荐你采用真正的安全而不是隐蔽性安全

6.2.6. 隐蔽性安全并不是真正的安全,但它可能是真正的损害。你得权衡利弊

6.3. 不要光靠你自己去实现安全

6.3.1. 你不应该编写一种仅属于自己的安全机制,无论它是哈希、加密还是节流

6.3.2. 一个普通的开发者在实现自己软件的安全时可能会错过关键的细节,基本上造成的结果就是毫不安全(zero security)

6.4. SQL注入攻击

6.4.1. 解决SQL注入问题的最安全方法是使用参数化查询(parameterized query)

6.4.1.1. 参数化查询并不是灵丹妙药

6.4.1.2. 有些数据库的抽象似乎不支持常见的参数化查询,这些数据库的抽象有其他的方法来进行参数化查询

6.4.1.3. 不要力求全部查询都实现参数化

6.4.2. 使用参数化查询的另一个好处是可以减少查询计划缓存(query plan cache)污染

6.4.3. 因为查询计划缓存的容量是有限的,如果你用大量的不同用户名运行这个查询,其他有用的查询计划条目将从缓存中被挤出,而缓存将被这些可能无用的条目填满,这就叫作查询计划缓存污染

6.5. 备份

6.5.1. 回退是最糟糕的错误类型,会浪费我们的时间

6.5.2. "3-2-1备份规则”(3-2-1 backup rule)

6.5.2.1. 有三个独立的备份,两个在独立的媒介上,一个在独立的地点

6.6. 跨站脚本攻击

6.6.1. 跨站脚本(cross-site scripting)攻击应该被叫作“JavaScript注入攻击”

6.6.2. 第一阶段是将JavaScript的代码插入网页当中

6.6.3. 第二阶段就是通过网络传输更多的JavaScript代码,并在网页上执行

6.6.4. 通过从其他会话中窃取会话cookie来捕获这些会话,这个操作叫作会话劫持(session hijacking)

6.6.5. 导致XSS攻击出现的原因往往是存在问题的HTML代码

6.6.6. 抵御XSS攻击的最简单方法是对文本进行重编码,使特殊的HTML字符被转义

6.6.7. CSP是应对XSS攻击的另一个手段

6.6.7.1. CSP(content security policy,内容安全策略)

6.6.7.2. 它是一个HTTP头,限制了可以从第三方服务器请求的资源

6.6.7.3. 维护一个可信域列表并且使它保持最新状态是一件很费力、费时的事情

6.6.7.4. 无论你是否打算使用CSP,都应该注意正确编码HTML输出

6.6.8. 只要不忽略注入HTML代码和完全不进行编码等问题,那么避免XSS攻击还是很容易的

6.7. 跨站请求伪造

6.7.1. 在HTTP中,修改网络内容的操作是用POST,而不是用GET来实现的,这是有原因的

6.7.1.1. 你没办法生成指向POST地址的可单击链接

6.7.1.2. 它只能被POST一次,如果操作失败,浏览器会警告你是否需要再次提交

6.7.1.3. POST的这种性质使我们对它过于信任

6.7.1.4. POST的隐患是,原始表单不一定要和POST请求所在的域相同

6.7.1.4.1. 要避免这种问题的产生,可以对每个生成的form使用一个随机生成的数字,这个数字会被复制在form本身和网站响应标题上
6.7.1.4.2. 你需要确保它在服务器端也得到了验证

6.8. 永远不要相信用户的输入