第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.构建应用

figure_0403_0141

图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所示。

figure_0406_0142

图 12-2 登录页面

输入用户名“Admin”和密码“Admin”,点击“登录”按钮,我们将成功登录该系统,如图12-3所示。

figure_0406_0143

图 12-3 登录成功页面

这是一个再简单不过的用户登录验证模块,相信很多读者对此都非常熟悉。当然,这样的系统并不安全。这里的“安全”针对拥有数据库访问权限的任何人,甚至是这套系统的开发者。用户敏感数据必须加以隐蔽,避免他人盗用。此处,我们需要隐蔽用户的密码信息,使用消息摘要算法隐蔽用户密码。