我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
归档 2014 年 2 月

这个假期我都干了什么

时间开销

  • 我有 36.9% 的机率正在睡觉

  • 我花了 13.6% 的时间玩游戏,其中:

    • 玩 SC2 花了 51.1% 的时间
    • 玩 Diablo3 花了 25.3% 的时间
    • 玩 LOL 花了 17.2% 的时间
    • 玩 SimCity5 花了 5% 的时间
    • 玩 Minecraft 花了 1.4% 的时间
  • 我花了 12.5% 的时间在写代码,其中:

    • 70.9% 的时间我在写兼职的项目,其中:

      • 83.3% 的时间真正在写代码
      • 16.7% 的时间用于沟通
    • 写我自己的项目花了 29.1% 的时间,其中:

      • 写 RootPanel 花了 47.5% 的时间
      • 写 LightPHP 花了 20% 的时间
      • 写我的博客花了 20% 的时间
      • 制作 SC2 地图花了 6.8% 的时间
      • 写 books 花了 3.4% 的时间
      • 写 NodeTalk 花了 1.7% 的时间
  • 我花了 7.9% 的时间看视频,其中:

    • 54.5% 的时间我在看爱情公寓
    • 25.4% 的时间我在看流言终结者
    • 12.7% 的时间我在看哆啦A梦
    • 7.5% 的时间我在看 SC2 的比赛视频
  • 有 2% 的时间我出门在外,其中:

    • 54.5% 的时间和小璐在一起
    • 21.2% 的时间在学校
  • 除此之外,还有 26.7% 的时间没有被追踪到

停留时间最长的网站 TOP 10

  • bilibili.kankanews.com
  • github.com
  • google.com
  • zhihu.com
  • tieba.baidu.com
  • mail.google.com
  • xuxian.jimu.com
  • v2ex.com
  • jysperm.me
  • fe.pomotodo.com

访问次数最多的网站 TOP 10

  • google.com
  • github.com
  • v2ex.com
  • zhihu.com
  • taobao.com
  • jimu.in
  • qq.com
  • jysperm.me
  • baidu.com
  • segmentfault.com

这个世界缺少童话

本文也算是对我不上大学的一个比较正式的解释吧。

我小学六年都是同一个英语老师,我对她印象深刻。每当我犯错误,或者有些奇怪的念头的时候,她会问我,你愿意用你的一生来做这么一个实验么?看看是你说得对,还是我说得对。那时候我的回答都是,好吧,我错了。但是也有的时候,比如现在,我会说我愿意,因为这是我的人生。

我们总在『中国』这个国情下考虑问题,事实上中国在各方面,并不输给一些发达国家。但是至少在计算机和互联网方面,中国人似乎很少有能够惊艳世界的创造。很多伟大的创造都源于 just for fun (只是因为有趣), 起初他们只是为了实现那么一个目标,为了从此世界上多那么一种可能性,谁想到后来他们竟能影响世界呢?

我觉得我们缺少的就是这种童话般的精神,just for fun, 可以不计回报地投入一件事情。而不是在做任何事情之前,都考虑这对我们有什么好处,并事先想好退路。

我也见过很多大学生,计算机相关专业的。大学的课程以计算机科学(理论知识)为主,如果没有兴趣,不自己思考,和日后的实际工作是根本联系不到一起去的,事实上很多人在毕业甚至考完试的时候就已经把学过的东西忘得差不多了。

如果有兴趣的话,大学确实能提供非常不错的资源,比如图书馆,免费的软件授权,有共同兴趣的老师或同学。但是,现在是互联网时代了,如果我有兴趣的话,我一样可以在大学之外得到这些资源,而且更加自由,比如可以更灵活地安排时间,比如我不必在四年的时间里都待在一个城市。至于气氛,说实话我不觉得大学能提供很好的氛围,同学们各自抱着不同的目的来到大学,这种情况下很难谈气氛。

有人会说,大学是人生所必须的经历,但是我不能同意。谁知道我不上大学的四年里,不会是我人生中最精彩的一段时光呢。任何经历都是宝贵的(也有人说蹲监狱是宝贵的经历呢),也正因为不同的经历,每个人才会有不一样的价值观。

大家有没有想过,为什么学历如此重要。除去一些大规模企业的硬性规定,肯定所有老板都希望得到有能力的员工。但是简历谁都会写,上面描述的能力,经验,都可能是胡编乱造的,学历相比之下是其中最不容易造假的。所以老板们为了少花一点时间,选择先用学历过滤掉大部分不合格的人,大部分的情况下,这种过滤是非常有效的。

所以,如果没有学历的话,我需要通过其他手段来证明我的能力。事实上我很早就在做准备,比如你所见到的这个博客,我相信每个人都能从我的博客看到我积极的价值观,和我所付出的努力。另一方面是 Github, 它记录了我在技术方面的足迹,写过的项目和代码,和协作者的讨论。

很多长辈倾向于求稳,希望找一个铁饭碗,就是那种可以没有波澜地,按月领工钱的工作。但是,那是你们的想法,你们有家庭有责任,没有精力去拼搏。但不代表我们年轻人没有,我们还有时间可以用来为错误买单,创造未来的也正是我们这一代人。

长辈们总强调人脉的重要性,首先在不违法的前提下,托人也不是什么不光彩的事情,但是总会让你办事有所顾虑。不能随时想做就做,想走就走,总要顾及人情。所以在我的眼里,铁饭碗应当是走到任何地方,都可以轻易地找到一份合适的工作,有资本和老板讨价还价么,不必求人,不必奔波。

所以,我的目的并不是说服别人和我一样(围观的小朋友们请勿在家尝试), 高考确实是大多数人的最佳选择,但是我觉得对我来说并非如此。比如,通过这几年的时间,我证明了我有自学的能力,可以不靠任何监督,甚至顶着阻力凭兴趣学习。比如,我现在的能力足够我找到一个和大学毕业生薪资相当的工作,不用担心经济问题。然后,我很清楚我在做什么,我对不上大学这个决定有深入的考虑,我也确信我正在实现一个伟大的创造。

最后,你想继续和我讨论这个话题的话,你需要清楚两点。一是现在是互联网时代,年龄不代表掌握的信息的多少;二是隔行如隔山,请不要对你不了解的行业轻易做论断。

笔记:散列算法的使用场景

大家都知道散列算法,如 MD5, SHA, 但是对其具体特性恐怕都很模糊。说不准哪些用法是可靠的,哪些用法是不可靠的,只是通过加 salt, 或者反复散列的方式提高可靠性。本文将精确地讨论它们在各种使用方法下的可靠性,本文不讨论原理,只讨论使用,以下部分资料来自网络,感谢 wxy 帮忙检验文中的论断。

MD5 和 SHA 系列算法都属于同一类——我还没给这类算法找到一个足够贴切的名字。首先在大的分类上,它们都是散列算法。
散列是怎么个定义呢?典型的散列算法可以是任何一个:具有无限的定义域,且具有有限的值域的函数。甚至,宽松的广义散列算法可以是任何一个(数学意义上的)函数,因为函数本身的概念就是将一个或多个值映射到一个唯一的值。

从具体特征上来说,它们(的目标)都是足够安全的信息摘要算法。究竟何谓安全?
MD5 和 SHA 系列函数所追求的安全,在于下面两个方面。

  • 单向性
  • 抗冲突性

所谓单向性,就是指明文 M, 经过散列后的 hash(M). 从 hash(M) 无法推算出 M. 这是大部分摘要算法都具有的特征,因为大多数情况下,被摘要的信息长度要大于散列值,因为缺少信息,且值域明显小于定义域,即一个散列值会对应多个(无限个)原文,所以天然地就无法从散列值反向计算出原文。MD5 和 SHA 系列函数在单向性方面目前都还没有被发现明显的漏洞。

但是以上讨论的只是理论上的单向性,实际使用中,攻击者可以通过穷举,即暴力破解的方式推算出可能的原文。这种情况适用于已知信息原文定义域且范围较小的情况。例如如果直接对用户的密码进行散列,因为密码大多在十位字母左右,攻击者可以以很低的成本计算所有密码组合的散列值,以得到可能的信息原文。

从散列算法的角度,应对暴力破解攻击没有明显有效的手段,但也可以通过『提高计算成本』和『减少散列值长度』的方式提升单向性。

提高计算成本即通过将算法设计得更为复杂,增加更多次计算,来提高对单个信息进行散列的时间。对于正常单次散列来讲,即使增加十倍的计算时间,仍然是可以接受的。但当攻击者进行暴力破解时,同样需要原来十倍的计算时间,这个成本对攻击者来说可能无法接受。除此之外,还可以改造算法,使其只能运行于 CPU 上,无法使用显卡或专用芯片进行计算,以提高暴力破解的计算成本。
但似乎这种方式并没有被业界接受,SHA 系列函数中的新成员均减少而不是提高了计算成本。

减少散列值长度,即通过减少散列值的长度来使得多个(大量)信息原文映射到同一个散列值,使攻击者无法分辨究竟哪个才是真正的原文。
例如将散列值减少到 8 bit, 这样密码组合中 1/256 的密码都会映射到同一个散列值,根本无法分别哪个才是真正的密码。
这种方式显然很扯淡,因为这种方式会明显降低下面要提到的抗冲突性。

实际使用中,我们可以通过提高定义域范围的方式,来增强单向性。同样是散列用户密码的例子,我们可以将原来的 hash(M) 改为 hash(hash(M)). 因为 hash 的值域范围通常要比密码(约 10 位字母,即约 60 bit)大, 如 MD5 的值域是 128 bit, SHA-1 的值域是 160 bit. 因此我们通过第一次散列将第二次散列的定义域提高到了 2-4 倍,一定程度上提高了攻击者暴力破解的成本。

但是,攻击者会将 hash(hash(x)) 视为一个独立的散列函数 hash2(x), 除了正常地增加了一倍的计算时间,并没有受到我们增加作用域的影响。因此,这时我们需要引入 salt(佐料,干扰), 将散列算法改为 hash(hash(x + salt)), salt 需要与散列值一同储存。

在已知 salt 的情况下,对攻击者其实没有任何影响,因为这个函数依旧可以转换为 hash2(x + salt), 当然,有些攻击者会通过 Rainbow Table(彩虹表) 的方式进行协作和缓存,加 salt 会使他们无法使用 Rainbow Table.

Rainbow Table, 即事先对预定范围的数据进行散列,储存散列结果。然后通过数据库或者分布式技术(小型 Rainbow Table 就不需要了), 来优化从散列值到原文的『反向』查询。相似的技术还有『字典』,即收集大家常用的原文(密码,或其他短语), 代替穷举,以提高暴力破解的效率。

而在这个场景中,将 hash(hash(x + salt)) 更换为 hash(hash(x) + salt) 会获得更好的效果。因为前一种情况,攻击者可以将密码和 salt 视为一个整体来使用 Rainbow Table (虽然在 Rainbow Table 中很可能查不到这些信息); 后者则不能。因为后者是一个散列值加一个 salt, 长度已经远远超出了 Rainbow Table 的范围。当然,单纯加长 salt 也可以实现 hash(hash(x) + salt) 的效果。

在攻击者不知道 salt 的情况下,有两种选择。
一是将 hash2(x) 改为 hash2(x, salt), 这种情况适用攻击者知道 salt 的长度或范围的情况,因为这个函数有两个参数,因此破解成本与 x 或 salt 的长度呈指数函数,只要 salt 足够长,那么可以认为攻击者即使暴力破解也无能为力。
二是将第二个 hash 的输入视为一个散列值,换言之就是进行两次爆破,这种方式的计算成本相比于前一种会更高,因为暴力破解的成本同原文长度也是呈指数关系的。

所以,在未知 salt 的情况下,可以防御所有暴力破解,除非攻击者拥有极其惊人的计算力。
举个例子,如果密码为 10 位的字母,salt 为 3 位的字母,暴力破解需要相当于 Bitcoin 全网计算力的约 60 天时间。

虽然我们还拥有更强大的变种,例如:

hash(x, salt[]) = hash(hash(x + salt[1]) + hash(x + salt[2]) + ...)

但是我们认为,使用 hash(hash(x) + salt) 和足够长的 salt(大致相当于散列值的长度), 已经足够安全。

接下来,那么如何生成 salt 呢?是否有必要为每条数据(还是前面的例子,每个用户)使用单独的 salt 呢。通过前面的讨论我们看到,要保证单向性,对 salt 进行保密十分必要。如果你能够保证 salt 的绝对安全的话,那么是否使用单独的 salt 其实无关紧要。不要担心使用相同的 salt 会让攻击者找到某种规律而计算出 salt(当然前提是 salt 足够长), 因为 MD5, SHA 系列函数保证了单向性,攻击者无法从散列值推测出原文的任何信息。

至于 salt 的取值,没有什么特别的要求,基于保密的考虑,通常要使用随机生成的字符串,然后就是足够长(大约等于散列值的长度).

但我们认为仍有必要为每条数据使用单独的 salt, 这是基于下面两点考虑。
一是虽然暴力破解需要惊人的计算力,但是如果只需破解一个散列值,就可以获知所有数据的 salt, 这个破解也是值得的,也许攻击者就能够提供这么多的计算力也说不定,虽然如果你使用足够长的 salt 的话,这种可能性极小。
二是也许你不能保证 salt 的绝对安全,当你的 salt 泄漏后,每条数据使用不同的 salt 将会为攻击者制造最后一道障碍。

如果你所有的数据都使用同样的 salt, 那么攻击者在拿到数据后,只需进行一次暴力破解,即可破解出所有的数据(如果都命中了的话), 但如果你为每条数据使用不同的 salt, 那么攻击者将不得不为每条数据单独运行一次暴力破解,因为每条数据相当于在使用不同的散列函数,因为 salt 是不同的。

综上,我们认为网站的用户系统使用下面的散列即可保证安全性:

hash(hash(passwd) + salt)

其中 salt 是随机生成的,长度等于散列值长度的字符串。hash 可以是 MD5 或 SHA 系列函数。


我们接着来谈抗冲突性,所谓抗冲突性即从计算上不可能找到两个有同样散列值的原文。抗冲突性分为抗强冲突性,和抗弱冲突性。
抗强冲突性,即给定一个散列值,无法找到另一个具有同样散列值的信息原文。
抗弱冲突性,即无法找到两个具有同样散列值的信息原文。

加上单向性,这三条特性是呈阶梯状的,满足『抗弱冲突性』就一定满足『抗强冲突性』也就一定满足『单向性』同时也就满足『散列映射具有随机性和均匀性』。MD5 和 SHA 系列函数在设计上都满足这三条特征,但目前已经被发现了存在某些漏洞。

抗冲突性的主要应用场景就是为信息原文进行摘要,鉴别两段信息原文是否相同。
而攻击者的目标就是,对信息原文进行修改,然后在信息原文后(或者修改信息原文)添加给定的数据段,使其能够生成与之前相同的散列值,实现伪造数据原文。

在这种场景下攻击者是否也能够进行暴力破解呢?很难,因为在这种情况下,攻击者需要不断更换附加在信息原文后的数据,以获得和之前相同的散列值,在不理想的情况下,需要尝试所有的散列值,这个计算力被认为是无法接受的。但通过算法的一些漏洞,可以降低这个暴力破解的成本。

抗冲突性是检验散列算法设计是否优秀的重要指标,很多散列算法被淘汰都是因为抗冲突性存在漏洞。

目前 MD5 已在 2005 年被中国数学家王小云发现有抗强冲突性的漏洞,给定一个散列值,可以在几分钟内用普通计算机找到一个碰撞,即具有相同散列值的信息原文。

SHA-0 被发现漏洞可以将寻找碰撞的难度从 2^80 次暴力破解降低到 2^39 次,SHA-1 被发现漏洞可以将寻找碰撞的难度从 2^80 次降低到 2^63 次,SHA-2 系列函数还未发现漏洞。

因此目前使用 MD5 进行信息摘要并不安全,攻击者可以轻易地伪造一段散列值相同,但内容不同的原文,虽然攻击者还不能够自由地修改原文。
而 SHA-1 和 SHA-2 系列函数目前来讲是足够安全的。

1

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2023-12-20.