对于一个WEB项目来说,首先设计用户的注册和登录,是最正常不过的事了,但要设计一个安全的登录程序,看起来简单,其实并不容易;
一个简单的登录页面是这样的;
<h3>login</h3>
<form action="/some/login.do" method="POST">
<p>
UserName: <input id="username" name="username" type="text" />
</p>
<p>
Password: <input id="password" name="password" type="password" />
</p>
<button type="submit">login</button>
</form>
表单提交时,会获取input中的name属性,做为HTTP请求body中的参数传送给服务器,进行登录校验;
假定我的帐号为admin,密码为123456,那么在登录时发送给后台的HTTP请求如下(用Opera调试);
可以发现即使我输入的密码值为黑点,但在本机上仍以明文的形式被截获请求;
在网络传输过程中,被嗅探到的话,会直接危害用户的信息安全,通过调试,发现HTTP协议传输直接暴露用户密码字段;
为了用户信息的安全,需要在WEB的前后端进行加密处理;
加密的种类;
在WEB前端可以通过某种算法,对密码字段进行加密后,再将密码作为Http请求的内容进行提交,常见的包括对称和非对称加密;
1、对称加密;内容的加密和解密使用同样的密钥;
2、 非对称加密;需要两个密钥,公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密;
使用对称加密;
加密解密在前后台协商后,似乎是个不错的办法,比如,前台使用一个字符串位移+字符串反转的简单方法(举个例子,当然不能这么简单);
那么,如果原密码123456先移位:
123456 --> 456123
再进行反转:
456123–>321654;
那么这样简单的方法似乎可以混淆原密码,并且轻松由后台进行相反操作复原。但是这有两个缺点:
l 前后端加密解密需要同时修改代码;
l 前端加密无非是写在JS里,但是JS有风险被直接破解从而识别加密方法。
使用非对称加密;
非对称加密有着公钥私钥的存在,公钥可以随意获取,私钥是用来对公钥解密的本地存储,通过公私钥的机制似乎可以保证传输加密并且乃至现在还在使用的HTTPS就是基于这个原理。
但是HTTPS就一定安全吗?HTTP存在两种可能的风险:
HTTPS可以保证传输过程中的信息不被别人截获,但是细细思考下,HTTPS是应用层协议,下层采用SSL保证信息安全,但是在客户端和服务端,密文同样是可以被截获的;
这样就可以得出一个结论:不论是HTTP还是https,内容必须密文传输;
应该考虑在应用层之上再继续对密码进行保护,也就是编写代码来进行控制,而不依赖特定协议,易想到的就是利用不可逆加密散列函数MD5(string),用户在注册输入密码的时候,就存储MD5(password)值,并且在WEB端先进行MD5(password),然后将密码传输至后台,与数据库中的密文进行比较(PS:MD5函数在指定位数的情况下,对相同字符串运算值相同)。
这样一来,优点比较明显;
l 保证了用户数据库内部的密码信息安全;
l 传输过程中无论如何都不会使得用户的密文被破解出原密码;
l 简单高效,执行以及编码难度都不大,各种语言都提供MD5支持,开发快。
回到开始,用户名为admin,密码为123456;无论使用哪种协议,发送的报头内容在经过MD5处理后是这样的:
没错,加密登录成功了。但是,当我们庆祝密码安全的时候,可能账户的钱却突然不翼而飞。
这是为什么呢?
因为黑客不一定要获取到你的密码明文,如果直接截获你的密码密文,然后发送给服务器不是一样可以登录吗?因为数据库里的不也是MD5(password)的一样的密文吗?HTTP请求被伪造,一样可以登录成功,从而攫取其他的数据或者转走余额;
怎么办?解决办法:
就是服务器缓存生成随机的验证字段,并发送给客户端,当客户端登录时,把这个字段一并传给服务器,用于校验。
具体做法:控制器将把数据的Model封装到View中,这种存在Session的连接方式,允许了在Session中存取信息。那么我们可以利用一些开源的验证码生成工具,例如JAVA中的Kaptcha,在服务端存放生成一个验证码值以及一个验证码的生成图片,将图片以Base64编码,并返回给View,在View中解码Base64并加载图片,并于用户下次登录时再进行比对。
在现在的JDK中,对md5加密已经提供了支持,附一个获取用md5加密后的密文方法;