您的位置:首页 > 其它

关于登录加密问题的一些讨论

2015-04-08 00:50 477 查看
        本文收集了一些关于登录加密问题的一些讨论。

******************************************************************

一. QQ网站登录的RSA加密传输缺陷分析

[b]From: http://www.cnbeta.com/articles/43687.htm
[/b]

感谢匿名人士的投递

QQ网站登录处没有使用https进行加密,而是采用了RSA非对称加密来保护传输过程中的密码以及敏感信息的安全性。 QQ是在javascript中实现整个过程的。这个想法非常新颖,但是也是存在严重缺陷的。如果被黑客利用,则可能被捕获明文密码。
分析报告如下:

Author: axis

Date: 2007-11-23

Team: http://www.ph4nt0m.org  (http://pstgroup.blogspot.com)

Corp: Alibaba B2B Corp / Infomation Security

这个想法非常新颖,详细可以参考云舒写过的 《RSA非对称加密的一些非常规应用》,地址为http://www.icylife.net/yunshu/show.php?id=471

这个原理简单描述为下:

1. 在server端生成一对RSA密钥,包括public key 和 private key

2. public key传输给客户端浏览器, 客户端浏览器用public key加密敏感数据,比如密码;加密后的密文传回给server,然后server用 private key解密。

3. 注意private key只保存在server端,而public key则分发给所有人。 由于 private key只有server知道,所以密文即使被截获了,也无法解开。

这个解决方案其实还是非常好的,至少他防住了大部分的攻击,但是为什么说它是无法替代https,是有缺陷的呢?

因为这个方案无法防止中间人攻击 (man-in-the-middle)。

攻击过程如下:

1. 攻击者通过MIM(比如arp欺骗等)劫持server与客户端浏览器之间的http包

2. 攻击者生成一对伪造的RSA密钥: fake public key/fake private key

3. 攻击者将js文件中的public key替换为fake public key,并传输给客户端浏览器

4. 客户端浏览器用 fake public key加密敏感数据,比如密码,并将加密后的数据传输给攻击者

5. 攻击者用fake private key解密,获得明文密码等

6. 攻击者用server的public key加密明文数据,并传送给server

整个过程中不会出现任何提示,而用户的明文数据则被窃取了!

而luoluo则提出来一个更邪恶的想法(顺便在这里祝luoluo今天生日快乐!),他提出可以直接将加密的介质修改。

比如,如果是用js在做加密,则修改js,如果是用flash或java applert做加密,则替换flash或applet,直接去掉这种加密机制,捕获明文密码。

那么为什么说https是不可替代的呢? 因为当实施中间人攻击的时候,浏览器会提示证书已改变(具体参考云舒的关于https安全性的文章),这种机制是内建在浏览器里的,攻击者无力改变它。所以这种报警是非常有意义的。

而如果像QQ一样使用js进行RSA加密传输,实施中间人攻击的时候,是不会有任何提示的,一切都会在用户不知情的情况下发生。

这种情况和以前windows的RDP中间人攻击情况一样: 当使用3389端口的rdp协议登录时候,证书改变的时候没有任何提示。

而相对设计比较安全的ssh协议,ssl协议等,则都会针对证书改变做出提示,防止中间人攻击。

所 以,QQ的这个方案只能保护传输过程中一般的sniffer攻击,但是考虑到当今网络环境下,大部分的sniffer都是基于arp欺骗的,所以这种保护 机制其实是非常脆弱的。它只能对抗目前已知的arp sniffer软件,而对专门开发的替换关键字的软件,则无法有效防御。一旦这种专门针对QQ网站登录的sniffer软件被开发出来并且提供下载,灾难 就不远了。

不过这个方案还是有积极意义的,除去不能抵抗中间人攻击的缺陷外,其他方面都比较完美,特别是成本低廉。如果与https结合使用来防止中间人攻击的话,整个方案就更完美了。

******************************************************************


二. 如何保证用户登录时提交密码已经加密?(知乎)

From: http://www.zhihu.com/question/20060155

如何保证用户登陆时提交密码已经加密?密码是否已加密,需要客户端和服务端建立约定,双方按约定办事就行了。

这里提到的另一个问题是,如何保证传输安全?
最理想的方案当然是走 HTTPS 协议. HTTPS 在理论上是可靠的,但在国内会打一些折扣:你可以随便找一台电脑看看有没有安装商业公司或机构的根证书,这些根证书为线路某节点成为中间人提供了可能性;同时,在木马横行的年代,密码在加密提交前可能就被拿到了,此时
HTTPS 成了摆设,这是为什么国内流行密码控件的一个重要原因。

从成本和需求上考虑,对于众多对安全性要求不高的个人网站,仍然可以考虑采用 HTTP 传输,密码提交前通过 JavaScript 加密。由于 JavaScript 代码暴露在客户端,因此一般通过不可逆的加密方法加密密码,而对于任何摘要式的加密算法,都可以通过类似
md5 字典的方式直接查表获知弱密码,所以要混入 salt 以增加制作字典的成本。可想而知,解密只是时间成本的问题。因此这里的重要前提是“对安全性要求不高”。

如何验证密码呢?一个可行的方法是,客户端提交 md5(password) 密码(如上所述,此方法只是简单保护了密码,是可能被查表获取密码的)。服务端数据库通过 md5(salt+md5(password)) 的规则存储密码,该 salt
仅存储在服务端,且在每次存储密码时都随机生成。这样即使被拖库,制作字典的成本也非常高。
密码被 md5() 提交到服务端之后,可通过 md5(salt + form['password']) 与数据库密码比对。此方法可以在避免明文存储密码的前提下,实现密码加密提交与验证。
这里还有防止 replay 攻击(请求被重新发出一次即可能通过验证)的问题,由服务端颁发并验证一个带有时间戳的可信 token (或一次性的)即可。
当然,传输过程再有 HTTPS 加持那就更好了。

最后,为什么要密码控件?原因之一是上面说的,要防止密码在提交前被截获。当然,还有一些其他原因,工作所限,这里就不说了。


-------------------------------------------------------------------------------

使用js和后端的代码做非对称加解密,这里是个参考 http://www-cs-students.stanford.edu/~tjw/jsbn ,其实原理和https一样。不是所有服务器环境都支持https,所以可酌情考虑使用。


-------------------------------------------------------------------------------

既然前提是不用https,那么有几种方式可以来实现:
1、用本地控件,如ActiveX
2、用屏幕键盘,用户的输入值,每个键盘都是重新初始化过的,每个值输入,只有当前用户的Session知道,用完就删掉,下次重新随机生成。
3、另外类似于方案1就是常见的硬件加密,如银行的密保


[b]-------------------------------------------------------------------------------
[/b]

[b]加密的本质是对原有内容的混淆,目的是提高从表面结果反推达到目的的成本。
在网页提交(其实所有的通信都有这个问题)密码这个问题上,需要有几个维度来保证安全,首先是切面的安全性,对于一次提交首先保护的是密码本身,从最早的Base64到MD5或是SHA都可以做到这一点,但是Base64是可逆的,对于密码本身的保护是很弱的,哈希算法解决了这个问题,将不同长度的数据转换成统一长度的大数字,而理论上这个数字对应无穷多解,但限于密码的输入有限制,其实是可逆的,所以从1次混淆的MD5变成了3次混淆的SHA,但是随着现代计算机技术的进步,逆运算的成本不断降低,人们不得已要使用更大的数字来提高这个难度,但是为了向成本和发展妥协,人们不得已使用统一的算法,在CS时代由于很难侵入到CS两端,所以相对的双方使用的算法是保密的,破解难度相对比较大,BS由于为了保证开放性,特别是js本身就是明文算法,所以其实只能说防君子不妨小人,所以就有了安全控件,控件的唯一目的是用2进制代码来隐藏加密的算法,不知道算法,也就很难破解原文。
第二维度就是时间,如果密码一样加密结果也会一样,那么在不使用原文的情况下,可以使用加密过后的数据来模拟用户登录的动作也是可以的,所以纯粹对密码的加密其实不能解决这个问题,所以有了盐值,让同一个数据在不同情况下结果依旧不一致,但是盐值需要约定,总会被人找出规律,只是成本又高了点,所以还是不安全,这就引发了通讯安全的问题。
所以出现了https使用非对称加密来保护数据通路上的安全,让通讯变得不可窥视。
但是还有个东西叫木马,所以人们在输入上继续做文章,包括混淆输入,就是软键盘,好的控件不会把明文存在内存里,这很重要,以前的VB、Dephi密码控件都仅仅看不到,实际内存里面都是明文,很容易被病毒和木马利用,所以一般来说现在最安全的bs系统使用控件保护输入,隐藏自己的HASH算法,用HTTPS保护通讯。
但是ssl都有漏洞被爆出,所以好的系统加上用户自己勤换密码才是保护自己的不二法则,不过,太累了,所以使用硬件ukey吧,成本稍高
[/b]

[b]-------------------------------------------------------------------------------
[/b]

客户端用验证码作为盐值作可逆加密,传到服务端后先解密然后再用不可逆加密与数据库对比。详见:


http://www.2cto.com/Article/201304/202288.html#12732-tsina-1-75385-4cfdb5a4fb74592eb67847a918275b04


[b]******************************************************************
[/b]

[b]三.
Stackoverflow上的讨论

[/b]

[b]How
to send password securely via HTTP using Javascript in absence of HTTPS?
[/b]

[b]From: http://stackoverflow.com/questions/2003262/how-to-send-password-securely-via-http-using-javascript-in-absence-of-https[/b]

[b]---------------------------------------------[/b]



There is no way to send a password securely that the user can verify without SSL.

Sure, you can write some JavaScript that will make a password secure for over-the-wire transmission through hashing or public-key-encryption. But how can the user be sure that the JavaScript itself has not been tampered with by a man-in-the-middle before it
reached them, to send the password to an attacker instead of the site, or even just compromise the security of the algorithm? The only way would be for them to be expert programmers and have them inspect every line of your page and script to ensure it was
kosher before typing the password. That is not a realistic scenario.

If you want passwords to be safe from man-in-the-middle attacks, you must buy an SSL cert. There is no other way. Get used to it.

If I use MD5, one can reverse that password string.

No... not trivially at least. Whilst MD5 has attacks against it, it's a hashing algorithm and thus unreversable. You would have to brute-force it.

But again, a man-in-the-middle attacker doesn't need to look at your MD5s. He can simply sabotage the JavaScript you send the user to make the MD5s.

-------------------------------------------------------------------------------

The solution here is to not send the password at all. Use challenge/response.

In the original form include a large block of random text along with a key. Store the original random text in the session based on key on the server. When the client submits the form, use JS to hash the random text and password together. Then send the username,
key, and hashed random text to the server. DO NOT send the password. On the server, use the key to lookup the original random text, perform the same hashing operation with the stored password. If the server-hashed value matches the client hashed value, then
you know the client entered the right password without ever sending the password to the server.

[b]******************************************************************
[/b]




四. http下是否有加密登陆密码的必要(segmentfault)

From: http://segmentfault.com/q/1010000000926027



-------------------------------------------------------------------------------

QQ 网页上的登陆模块(全程HTTP/GET请求).

QQ 在登陆时,对用户输入的密码加密的JS代码为:
function getEncryption(password, uin, vcode, isMd5) {
var str1 = hexchar2bin(isMd5 ? password : md5(password));
var str2 = md5(str1 + uin);
var str3 = md5(str2 + vcode.toUpperCase());
return str3
}


白话就是: md5(md5(md5(密码) + 用户的QQ号) + 验证码)

验证码是一次性的, 所以,在你在网络层拿到本次的请求之后,无法做 重放攻击, 因为验证码是不正确的.

而当你获取新的验证码, 但你并不知道 组合之前的内容[md5(md5(密码) + 用户的QQ号)] 是什么 , 所以你无法重新发送本次请求实现登陆的目的.

32位MD5 + 4位验证码 总计 36位的字符串, 你去破解吧. 估计等你挂了你也破解不出来.

至于 服务端的校验, 只要将记录下来的MD5值(而不是记录的明文), 进行同样的运算, 得到的结果与提交上来的一样, 即密码正确.

验证码的内容是服务器下发的,而且是一次性的,所以 客户端无法伪造, 也无法重用.
-------------------------------------------------------------------------------
[b]使用自己的登录控件,比如支付宝,财付通,各银行等。
还有个某公司的键盘芭蕾的也是要客户安装控件,据说还会记录用户按键的时长和间隔,通过这两项值来验证密码是不是用户输入的,也就是有了密码也不一定能登录。当然这需要事先采集多次用户输入。
说的挺牛逼的,具体就不得而知了。
[/b]

[b][b]-------------------------------------------------------------------------------
[/b][/b]



我看到过一种处理方法,大致如下:

通过session计算出一个常数,储存在服务器。

在提交password的时候,在前端通过js将这个常数与用户输入的密码进行md5运算,运算结果POST给服务器。

服务器这边从数据库取出密码,同样的步骤在服务器端运行一次。

两次结果做比较。

有一个缺点就是服务器需要明文储存密码
-------------------------------------------------------------------------------


加密盐,防止md5值被逆向。

salt = "网站自定义的一个加密盐",服务端和客户端都有该值。

注册的时候对密码加盐做散列vp = md5(密码明文+salt),保存到数据库。

登陆:

服务器端产生一个随机值rnd保存在服务器的session里面,并传给浏览器;

浏览器提交加密过的密文:md5(md5(密码明文+salt)+rnd)

服务器验证的时候从数据库读出注册时候保存的散列值vp,做运算md5(vp+rnd),和浏览器提交的密文做比较。

这样至少可以让服务器不保存密码明文也能防止暴库之后被逆向,同时登陆过程中提交的密文也基本没法还原。因此中间人只能抓取会话,重放会话,会话过期之后也没法重放了。

最后,为了安全还是上https。
[b][b]-------------------------------------------------------------------------------
[/b][/b]

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  加密 解密 破解 密码 md5