Archive for 2012年2月

用户是最大的漏洞

2012/02/15

从安全方面来说,用户是系统中最薄弱的环节。不过「聪明的」用户总是会找出各种理由把责任推卸给「技术」。甚至研究者有时也会落入这种陷阱。

不久前在知乎上有一个关于 MD5 的讨论。不得不说 MD5 是一种非常弱的 hash 算法。考虑到使用 SHA-1 等更强算法的额外负担完全可以忽略,我建议无条件的避免使用 MD5。但是从另一个方面来说,到底多少安全问题真正的责任在于 hash 算法的脆弱性?我在知乎上的总结是这样的(下面有些描述是符号化的,但在知乎上用的是非符号化语言。如果二者造成理解上的差异我为知乎上的不准确抱歉):

给定一个 hash 算法 f(x),对 f(x) 的「破解」有下面几种方法(注意「破解」不能等同于攻击。破解是非蛮力攻击。所以以下方式均有「比穷举算法效率更高」这个限制):

  1. 使用比穷举算法效率更高的方法得到 x1 和 x2,满足 f(x1) = f(x2);(可称为随机盲目碰撞)
  2. 给定 x1,使用比穷举算法效率更高的方法得到 x2,满足 f(x1) = f(x2);
  3. 给定 x1,使用比穷举算法效率更高的方法得到满足约束 A 的 x2,满足 f(x1) = f(x2)。约束 A 越严格,达成实质攻击的可能越大。比如,若 A 是描述自然的英语或者汉语,则任意的签名欺诈可行矣。

目前在理论界只有破解方式 1 得到了一些针对 MD5 级别的弱 hash 算法的突破。方式 2 和 3 在理论上的可能性也还未证实,甚至于对最弱的 hash 算法即使用蛮力也无法得到方式 3 中真正可以施行欺诈的 x2。我认为方式 1 很难达成实质的攻击。但是有研究者不同意这个观点本地存档)。这个链接给出了一个方法,通过方式 1 的随机盲目碰撞也能达成实质的数字签名欺诈。果真如此吗?

错!

(以下描述需要理解刚才给出的链接内容)

Caesar 的错误不在于使用了弱 hash 算法,而是他轻易相信了 Alice 给他的 PostScript 格式的文件。他没有审查这个文件的实质代码(尽管 PostScript 文件的源代码实际上是可读的),而是轻信了一个 PostScript viewer 一时 render 出来的外观就匆匆地数字签名了。如果把 PostScript 文件变成可执行文件,把 PostScript viewer 变成操作系统,Caesar 变成某家水果公司。那么以上关于 Caesar 和 Alice 的情景无异于:一个开发者给水果公司一个 app,水果公司运行了一下发现没问题,然后水果公司就用公司证书给这个 app 签名并将签名后的 app 发回给开发者了。那么水果公司因此背上任何黑锅都是咎由自取。事实是:

  • 水果公司不会用自己的证书,而是用自己的证书认证开发者证书,然后要求开发者用自己的开发者证书签名;
  • 水果公司要保留追溯开发者的机制,在后期发现 app 违规时可以追加惩罚。

所以,数字签名技术提供的信任是有约束的。对简单的可审查文件(如纯文本)才能任意签署。对复杂的不可审查文件(如可执行文件,复杂结构化文档,特别是带有动态内容的文档 —— 其实质等同于可执行文件,有些甚至达到了 Tuning-complete 级别)必须坚持「谁产生谁签署」的原则,而且必须有后续的法律追溯机制。更近一步说,对复杂的不可审查文件的轻信是任何技术都无法挽救的安全溃败。

其实上文提到的这个研究者在最后一页中提到了采用其它文件格式作为解决方法。但是他明显认为和采用更强的 hash 算法相比这是一个次要因素,而且他提出的居然是 Word 文档,一种内在格式和外观差异与 PostScript 不相上下的格式,而非「所见即所存」的简单文本格式。我曾经参与过几次安全培训,那些很有经验的讲师一般也会在几天的培训里不经意举出一两个他们认为的技术上的安全漏洞,而实为用户「盲目轻信」导致的问题。从信噪比的角度看,这些疏忽对那些安全专家的知识价值可说瑕不掩瑜,但也说明在人类心理和行为模式方面的安全才是最脆弱的。

新增功课

2012/02/10

在 Amazon.com 上买的《Mac OS X Internals: A System Approach》经过十天到货了。虽然有电子版,但还是买个纸质的收藏一下。

技术与文化

2012/02/07

我写了两篇《为什么 Mac OS X 先进》(  ) 。主要讨论什么样的文化经历了什么样的历史如何沉淀到技术中去。不过,文化有时候就保持为文化,不是所有的文化都有机会或者有必要沉淀为某种具体的技术。

前不久有一阵关于 Android 和 iOS 用户体验差异的讨论,始于《Why is Adnroid laggy, while iOS, Windows Phone 7, QNX, and WebOS are fluid》。这篇文章的作者认为原因在于两个操作系统调度线程 (thread scheduling) 的方式不同,在 iOS 上 UI 由一个有 realtime 优先级的专用线程处理,而在 Android 上只由普通线程处理。但是马上有人指出其实 iOS 和 Android 线程调度方式的差异并无文中猜测的那么巨大。

文章的作者承认自己的结论属于「猜测」。他说明自己并无特权接触iOS 底层(如果真有「特权」接触,也就不太可能被允许讨论),只是根据观察两个操作系统的外部行为大胆推测。他的观察是准确的,iOS 上大多数 app 界面渲染的优先级高于其它任务的优先级的程度,要比 Android 上类似的差异大。不过我推测这种最终处理效果的优先级差异并非源自操作系统的线程调度机制。

第一,虽然大多数 app 需要对用户的操作作出及时反应,但并不要求这种反应很快地提供最终计算结果。所以很多计算并不要求在后台和 UI 渲染同步进行(或者说,在单核系统上,和 UI 渲染以 preemptive 的方式分时进行),相反,只是要求 UI 渲染能比较快地打断计算即可。这种「打断」一般是利用 timer 等协作式多任务机制来完成。因为相对操作系统本身的 preemptive 多任务,协作式多任务对数据结构一致性的处理更为简单。此类多任务并不受操作系统的调度,而是 timer 本身的实现和 timer 的调度库(比如 Cocoa,Cocoa Touch)负责的。其中又以前者的影响为大。

第二,现代操作系统的线程调度一般基于动态反馈优先级 (dynamic feedback priority) 。在这种策略中,如果一个线程总是用尽分配给自己的时间 (time slice) 而必须由操作系统强制收回 CPU,它的优先级就会降低。而一个线程如果总是由于等待鼠标、键盘、或者磁盘主动让出 CPU,它的优先级就会提升。这种策略只考虑收回 CPU 的方式,而不考虑线程本身运行和休眠 (sleep) 的历史情况。在此基础上,有些操作系统,如 FreeBSD 和 Linux,会统计线程运行和休眠状况的历史,并且根据二者的关系推测该线程是否为 interactive 类型,即上文的 UI 线程。不过这种基于统计的推测做不到 100% 准确。Interactive 线程从休眠状态恢复到可运行状态时,会放入运行队列直接等待 CPU 调度。而 non-interactive 线程从休眠状态恢复到可运行状态时,会放到比运行队列低一级的「next 队列」。CPU 调度线程时不会考虑 next 队列,只有当运行队列清空之后(其中线程都已经调度过,并且其间没有 interactive 线程被重新唤醒),next 队列和运行队列的角色才会对调。值得注意的是 OS X 的 XNU kernel 中的 Mach scheduler 并没有采用这种「运行/next」队列的概念,也没有 interactive/non-interactive 类型,它只为每个 CPU core 创建和维护唯一的基于动态反馈优先级的运行队列。我猜测同样基于 Mach 的 iOS 和基于 Linux kernel 的 Android,其 scheduler 也与各自的源头基本相同。所以,现代操作系统中为提高用户体验的最重大的努力之一并没有应用到 OS X / iOS 中,而结果是似乎后者的用户体验并未受到什么伤害。

在 time-sharing 系统中,虽然 scheduler 是一个关键角色,但是因为它没有上层应用的 knowledge,scheduler 根据统计 (statistic) 和启发式 (heuristic) 算法所作的优化并不如上层应用根据自身逻辑进行的优化效果更明显。OS X 和 iOS 上 app 顺畅的操作感来自于 app 开发者本身对高质量界面文化的认同,而不是操作系统提供的「免费午餐」。OS X 没有采纳「运行/next」队列是不完美的,我希望这点最终能改变,不过目前看来其它操作系统和 OS X / iOS 在质量文化方面的差距差距抵消了后者技术上的不完美,亦或是,把有限的资源用在提高 app 质量本身而非效果甚微的底层方案才是正确的。