设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

文艺青年如何管理密码

2014-11-24 13:35| 发布者: joejoe0332| 查看: 1576| 评论: 0|原作者: linuxtoy|来自: linuxtoy

摘要: 这个问题的根源是,你不能所有的账户都用一样的密码,如果你胆敢那么干,并且你曾经注册过 CSDN 之类的网站,那么黑客拿到你的用户名和密码后,会去所有可能的网站,碰碰运气。 ...

  这个问题的根源是,你不能所有的账户都用一样的密码,如果你胆敢那么干,并且你曾经注册过 CSDN 之类的网站,那么黑客拿到你的用户名和密码后,会去所有可能的网站,碰碰运气。



  负责任的说,中奖的机率要比彩票高。


  你也不用担心黑客劳累过度、积劳成疾,这些操作都可以自动化,保质保量,准时准点。


(如果实在想不出激情四射的密码,起码换个理性低调的马甲)

 

  所以我最近读了多篇文章,大概是普通青年想摆脱二逼的困境,向着文艺升华的节奏,讲怎么置办一系列比较安全的密码。


  首先,所有密码都高度相似,这肯定是非常二逼的;如果所有密码完全不同,怎么记住又是个问题。


  普通的解决方案,密码分为不同的安全级别,不同的安全级别采取不同的策略。不重要的密码,可以随便用身份证号,你朋友的母亲的隔壁邻居的小孩的那一条狗的生日什么的糊弄过去;重要的密码,要用 1Password、LastPass之类的工具生成,然后保存在云端(所以这个其实并不适合存储{敏感词}密码)。


  这里的问题是,密码都存储在云端,可能需要联网才能使用;并且还要依赖于服务商的安全管理水平和职业操守,LastPass 经常爆安全漏洞,并且大面积丢失过密码(如果密码是随机生成的,丢失了后,恐怕也只能一边哼着“密码哪去了”,一边设法重置了)。


  所以……我的意思不是说文艺青年应当用 1Password,它们本质上是一样的。


我们的目标是什么呢?

  • 复杂
  • 不重复
  • 己知部分密码,不能轻易的推出其它密码
  • 容易记住

 

  其实 hash 加密很容易达成这样的目标,以 SHA1 为例:

doyouknowmypasswd?1 -> 740845e8ddf457a518a4c0ce3d44a95924e80497

doyouknowmypasswd?2 -> b28ab233ba23dc844bf1095606f80962b244e019


  它的特点有:

  • 很长,上面结果是 16 进制数,转换成 10 进制还要更长一点
  • 不可逆,知道的密码,不可能推断出原文——这点是算法本身保证的
  • 对扰动敏感,上面两个例子只有一个字符不同,密码完全不一样。可以用主密码加描述的方式,比如 passwd#Kardinal@linuxtoy.org, 来生成密码
  • 固定的输入对应固定的输出,这样只需要记主密码,而密码描述,完全可以打印出来随意张贴——只要主密码不同,生成的密码是完全不同的

 


  相信很多文艺青年也想到了这种方式,但是为什么没有人采用呢?

  废话,我也不这用这种方式的,因为:

  • 长度固定,可能有的情况下密码只允许最多 n 位,当然你可以只取 n 位,这个问题到是不大
  • 单调,没有文采,有的情况要求“大小写+数字+特殊字符”

 


  现在我们已经从本质上解决了密码的问题,只需要一个这样的函数: genpwd primarypwd desc rev :: str -> str -> int -> int


  (三个参数为 主密码,描述,修订——有的时候你只是想单纯的更新一下密码,但是规则不变,怎么办呢?在待生成的字符串里加个修订版本就可以了。)


  但是对于这个密码,还需要润色一下,让它能根据我们的要求变形,所以需要这样一个函数: fmtpwd genpwd rule size retry :: int -> str -> int -> int -> str

(把刚才生成的密码,和规则、长度作为输入,输出美观的新密码。还有一个参数 retry 稍后再说)


  先生成一个密码

  def genseed desc, rev    (Digest::SHA1.hexdigest @primary + desc + rev.tos).to_i(16)  end

 

  现在有了一个类似 b28ab233ba23dc844bf1095606f80962b244e019 这样的密码,假设需要 10 位,应该怎么办呢?


  想到取前 10 位数的,拉出去毙了……


  平均分成 10 等分,然后每4个数对应一位?


  现在我需要一个 1000 位的密码,怎么分


  假使真的把这个密码切成一段一段的,我可以保证,非常麻烦,是个体力活。


  我觉得最方便的办法,是线性同余,这是一个生成伪随机数列的方法。感受下,伪~随机,“伪”表示它的输出是固定的, 如果每次是真的随机的话,你只能唱“密码去哪儿了……”,根本停不下来的节奏;“随机”意味着你生成的密码看起来就像掷骰子掷出来的一样。

  def lcg seed    seed * 630360016 % 2147483647  end

用这个方法生成指定长度的密码:

  def gen_pwd seed, size, try    [*1..size].inject([seed + try]){|n, x| n << (lcg n[-1])}[1..size]  end

假设需要 10 位密码, gen_pwd b28ab233ba23dc844bf1095606f80962b244e019 , 10 , 0 后,结果是这样的

[339466767, 236817397, 1269734706, 159396745, 121834885, 865587496, 446364382, 144844773, 1659694774, 1631169047]


  参数里为什么要有个 try 呢? 线性同余虽然有优良的周期分布,但是不能保证任何情况下都符合要求—— 生成一个只有特殊字符,而没有大小写和数字的密码,尽管概率极小,但不是完全没有可能。 这就需要对生成的密码进行校验,不符合要求的话,把 try 这个值递增上去重新生成,直到符合要求为止。

生成最终的密码:

  def fmt_pwd pwd, rule, custom    rl = rule.flatten + custom.split('')    pwd.map{|x| rl[x % rl.size] }  end

 

校验是否符合要求:

  def verify pwd, rule    return false unless pwd    rst = rule.map do |r|      lambda do        for x in pwd          return true if r.include? x        end        return false      end[]    end    rst.inject{|n, x| n and x }  end

 

最终的接口:

  def show desc, rule=false, rev:0, size:10, try:0, custom:@custom    rl = unless rule then @@rule else rule.split('').map {|x| @@rules[x].toa } end    seed = genseed desc, rev    result = nil    until verify result, rl      pwd = genpwd seed, size, try      result = fmtpwd pwd, rl, custom      try += 1    end    PassWord.new result.join, try  end

 

最终生成的密码差不多是这个样子的 : "k1irM9wt0V", "l69WHePAj4", "lwVsF2TyLG"

最后,如果想用的舒服一点,可以自己设计一个用户级别的接口;另外可以把每条密码除主密码外的所有信息保存起来,JSON 或者数据库什么的;还可以在浏览器里把每条密码对应一个网址,实现自动填写……总之,你可以好好享受 DIY 的乐趣了

上面代码的

https://github.com/ran9er/diy_pwd


酷毙

雷人

鲜花

鸡蛋

漂亮
上一篇:成为软件工匠下一篇:当AMD遇上FIS
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部