第12章 量体裁衣—为应用选择合适的装备
信息是这个时代最核心的内容,网络是这个时代最主要的载体,网络信息安全终于成为这一时代的主题。我们该如何确保论坛、博客、社区等开放性网络应用的用户隐私不被泄露?我们如何避免QQ、MSN、RTX、Skype、GTalk等聊天工具在传递敏感数据时不被窃听?我们该如何保证与合作伙伴交换的商业数据不被窃取?
本章将通过3套较为常见的实例,介绍如何为应用选择合适的加密算法,确保数据安全。
12.1 实例:常规Web应用开发安全
BBS、BLOG以及越来越火爆的SNS正一次又一次地“绑架”了我们的生活,我们正不遗余力地在这些“场所”投入我们的时间、精力甚至是汗水。我们关注每一条行业消息、用心发表每一篇帖子、细心维护自己的日志,哪怕半夜都要爬起来去“偷菜”。倘若有一天别人盗用了我们的账号,发表了不该有的言论、清空了我们的日志,或是变卖了我们的“家产”,我们长久以来积累起来的威信、声誉、财富等都荡然无存。如果言论涉及伦理道德或者法律等问题,这对于用户群体来说就是天大的麻烦。某些系统中保留了我们的私人信息,包括所在公司、住址、私人相册、电子邮箱、手机号码甚至是身份证号,一旦私密信息泄露,将给我们的生活带来很多不必要的麻烦。造成这一局面的主要根源不外乎账号管理问题!
很多系统在设计时都将密码以明文的方式存储在数据库中,并以此为基础提供取回密码的服务,这使得用户私密数据泄露成为可能。如果仅仅将密码以明文的方式存储在数据库中,任何拥有数据库访问权限的人都可以获得某个用户的访问权限。倘若开放取回密码的服务,任何具有该权限的人同样可以获得该用户的访问权限。通过使用该用户的账号,可以假借用户之名进行任何操作,谋取私利而不留痕迹。
本文以常规Web应用登录模块为例,介绍如何通过消息摘要算法隐蔽用户敏感信息。
12.1.1 常规Web应用基本实现
这里,我们将构建一个名为“SNS”的Web应用,并使用MySQL为SNS提供数据库服务。
1.准备工作
本书将使用MySQL作为SNS应用数据库,并使用Commons Codec提供消息摘要算法实现。有关MySQL数据库下载及安装操作,请参考相关资料。同时,请读者注意下载MySQL JDBC包。有关Commons Codec使用,请参考第4章和第6章的内容。
2.搭建数据库
首先,我们需要为SNS构建数据库。建库SQL如代码清单12-1所示。
代码清单12-1 SNS库SQL
CREATE DATABASEsns
DEFAULT CHARACTER SET utf8
接下来,我们需要构建用户表Account,主要包含主键(account_id字段)、用户名(name字段)、用户密码(password字段)和电子邮箱(email字段)共4项基本信息。建表SQL如代码清单12-2所示。
代码清单12-2 Account表SQL
CREATE TABLEsns.account(
account_idint(10)unsigned NOT NULL AUTO_INCREMENT,
namevarchar(45)NOT NULL,
passwordvarchar(45)NOT NULL,
emailvarchar(45)NOT NULL,
PRIMARY KEY(account_id)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
为方便演示,我们构建3条用户数据。插入用户信息SQL如代码清单12-3所示。
代码清单12-3 插入用户信息SQL
INSERT INTOsns.account(name, password, email)values('Admin','Admin','admin@zlex.org')
INSERT INTOsns.account(name, password, email)values('zlex','zlex','zlex@zlex.org')
INSERT INTOsns.account(name, password, email)values('snowolf','snowolf','snowolf@zlex.org')
上述SQL中,用户密码即用户名,并以明文存储,如图12-1所示。
任何拥有数据库访问权限的人都可以获得某个用户的访问权限,这是很不安全的。
3.构建应用
图12-1检索用户数据1
完成数据库搭建操作后,我们将构建应用。这里我们构建一个简单的用户登录模块。
首先,我们需要构建web.xml文件,如代码清单12-4所示。
代码清单12-4 web.xml文件
<?xml version="1.0"encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID"
version="2.5">
<display-name>web</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
接下来,我们将构建两个页面:index.jsp和login.jsp。index.jsp页面给出用户登录入口,login.jsp页面操作数据库验证用户身份。
index. jsp页面代码如代码清单12-5所示。
代码清单12-5 index.jsp页面
<%@page language="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"content="text/html;charset=UTF-8">
<title>欢迎</title>
</head>
<body>
<form action="login.jsp"method="post">
<table>
<tr>
<td>username:</td>
<td><input name="username"/></td>
</tr>
<tr>
<td>password:</td>
<td><input name="password"type="password"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit"value="登录"/>
<input type="reset"value="重置"/></td>
</tr>
</table>
</form>
</body>
</html>
index. jsp页面与一般的HTML页面几乎没有任何差别,仅仅用于展示用户登录入口。
login. jsp页面用于处理用户登录验证逻辑,如代码清单12-6所示。
代码清单12-6 login.jsp页面1
<%@page language="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.sql.*"%>
<!DOCTYPE html PUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"content="text/html;charset=UTF-8">
<title>登录结果</title>
</head>
<body>
<%
//构建数据库连接
//JDBC驱动类
String driverClass="com.mysql.jdbc.Driver";
//用户名
String name="root";
//密码
String pwd="admin";
//数据库连接
String url="jdbc:mysql://localhost:3306/sns";
Class.forName(driverClass);
Connection conn=DriverManager.getConnection(url, name, pwd);
//取得表单信息
String username=request.getParameter("username");
String password=request.getParameter("password");
//检索用户
PreparedStatement statement=conn.prepareStatement("select*from account
where name=?");
statement.setString(1,username);
ResultSet rs=statement.executeQuery();
%>
<pre>
<%
//如果记录存在验证用户身份
if(rs.next()){
String p=rs.getString("password");
if(p.equals(password)){
out.println("用户"+username);
out.println("登录成功!");
}else{
out.println("用户"+username);
out.println("密码错误!");
}
}else{
out.println("用户"+username+"不存在");
}
%>
</pre>
<%
//关闭结果集
rs.close();
//关闭语句集
statement.close();
//关闭连接
conn.close();
%>
</body>
</html>
用户验证的逻辑很简单,按用户名检索用户信息。如果用户记录存在,则校验用户密码。如果密码校验通过,则用户登录成功。当然,我们也可以按用户名和密码检索用户信息。
4.验证服务
启动Tomcat,访问地址http://localhost:8080/sns/,登录页面如图12-2所示。
图 12-2 登录页面
输入用户名“Admin”和密码“Admin”,点击“登录”按钮,我们将成功登录该系统,如图12-3所示。
图 12-3 登录成功页面
这是一个再简单不过的用户登录验证模块,相信很多读者对此都非常熟悉。当然,这样的系统并不安全。这里的“安全”针对拥有数据库访问权限的任何人,甚至是这套系统的开发者。用户敏感数据必须加以隐蔽,避免他人盗用。此处,我们需要隐蔽用户的密码信息,使用消息摘要算法隐蔽用户密码。