在很多企业应用中,用户登录及验证普遍存在。从用户数据存储的方式来看,一般有数据库存储、LDAP/AD、文件存储等。
很显然,在关系数据库发达的年代,单就其保密性和安全性方面来说,文件存储已经基本上可以不用考虑。
数据库存储用户资料,是目前比较流行的存储方式,软件开发人员可以直接将用户名称及对应的密码存储到数据库中的两个字段中。然后,我们可以通过类似下面的SQL语言来实现用户验证:
select * from user where username='name' and password='password';
在UI设计上,我们一般提供一个文本框用来输入用户ID,用一个密码框来输入密码。然后通过变量传递的方式,传送至server端进行验证,服务端类似的验证SQL脚本可能是:
String sql = "select * from user where username='" + userName + "' and password='" + password + "'";
通过返回的数据集的size可以判断当前用户存在与否,以此来检查和验证用户。
咋一看,一切都很自然,也比较合理。殊不知,安全隐患由此产生:
1. 用户密码原文存储
虽然数据库一直是一个比较隐秘的部分,但是如果把密码老老实实的填写在password这个字段上,用户的密码就赤裸裸的暴露给数据库管理员或第三方知道数据库登陆密码的用户。现实中,不缺少那种诚恳的用户,使用自己的银行密码、保险箱密码等作为自己登录密码。而这一切,可能就成为了另外一个对你有企图的人的重要资料。
所以,无论如何,请不要将用户的密码直接存储在数据库中,你完全可以使用MD5, SHA等密码加密方式加密后,再存储到password字段。
那有人问了,如果用户密码丢了,要找回密码怎么办?很简单,直接创建一个新的密码,加密存储,然后提供给终端用户。
2. SQL语句注入威胁
那上面的那个例子来说,如果我在UI上的用户输入框中输入"name' and '1'='1",这会出现什么后果?你可以去尝试,这样会取出你数据库中所有的用户数据,如若你的程序处理不当,可能就给了人家绕过密码验证的捷径。
这还算比较轻的了,如果输入的是"name'; delete from user; select * from user where '1'='1",这样会出现什么后果?你的用户资料全部被恶意的删除了。那你的数据库还安全吗?
如何解决这种SQL注入的问题?可以从两方面入手:
1). 字符客户端或服务端验证,确保输入的用户名的长度,以及里面是否含有特殊字符。
2). 修改你的SQL语句的构造方法,不要采用直接字符串连接的方式来构造你的SQL语句,你可以采用其他的方式。如Hibernate,你不要使用hsql的查询方式来直接查询,你可以通过主键定位的方式先获取到这个User对象,然后在判断密码是否正确;另外,可以采用建立criteria, query对象的方式来直接查询验证。如:
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
detachedCriteria.add(
Restrictions.eq("username", userName)).add(
Restrictions.eq("password", password));
List userList = this.hibernateTemplate.findByCriteria(detachedCriteria);
if (userList.size() == 1) {
return (User) userList.get(0);
} else {
return null;
}
此处我们不考虑连接传输的安全问题,如使用密码客户端加密,https的安全连接等。
至于LDAP和AD,这也是我们经常用到的,应该说是一种非常安全、高效、灵活的用户验证手段。相关的连接LDAP或AD的程序,我们可以在网络上普遍搜集到相关的代码。
1111

被折叠的 条评论
为什么被折叠?



