1、首先简谈一下常规Web登录模块的开发(只为了实现简单的登录功能,未对数据库字段进行加密处理以及传输过程中进行加密处理)
非安全性登录模块开发
使用JSP+MYSQL
数据库表如下所示:
先用jsp页面创建login.jsp和index.jsp页面(为了方便讲解,直接使用jsp页面传值及校验)具体代码如下所示:
账 号:<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>
登录界面
<%@page import=”java.sql.ResultSet”%>
<%@page import=”java.sql.PreparedStatement”%>
<%@page import=”java.sql.DriverManager”%>
<%@page import=”java.sql.Connection”%>
<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>
首页
<%
//获取mysql连接对象
String driverClass=”com.mysql.jdbc.Driver”;
String user=”root”;
String psw=”zwkkwz”;
String url=”jdbc:mysql://localhost:3306/mytest”;
Class.forName(driverClass);
Connection conn=DriverManager.getConnection(url,user,psw);
//获取login.jsp传过来的username和password
String username=request.getParameter(“username”);
String password=request.getParameter(“password”);
String sql=”select * from login where username=?”;
PreparedStatement stmt=conn.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs=stmt.executeQuery();
%>
<%
if(rs.next()){
String p=rs.getString(“password”);
if(password.equals(p)){
out.println(“用户”+username+”登录成功”);
}else{
out.println(“用户”+username+”登录失败”);
}
}else{
out.println(“用户”+username+”不存在”);
}
%>
通过上面两个jsp页面进行实现登录页面的,可以实现校验功能。但存在的安全隐患问题太多
数据库以明文的形式进行存储
数据传输的过程中未对数据进行加密处理(可以使用WireShark等抓包工具获取post传递的明文信息
2、接下来针对以上两个问题进行分析和解决:
安全加固1:对数据库表的password字段进行摘要处理(在MYSQL中对数据摘要处理,sql语句如下)
//使用SHA进行摘要处理
UPDATE loginset password = SHA(password)
//使用MD5进行摘要处理
UPDATE userinfo set password = MD5(password)
但是这样子还是不安全,进入http://www.cmd5.com/ 输入加密后的密文进行解密后可以得到明文密码
比如数据库有多个密码的明文是123456,通过MD5加密生成的密文是一模一样的,这样别人解密后就可以获取到其他一样的密码
针对上述问题,我们会进行加盐处理。即在用户注册时随机生成一个规定长度的字段,然后和用户密码相结合,在进行MD5加密,等会再讨论这个问题。
当对数据库的明文密码进行MD5加密后,我们再来改造一下jsp页面的处理逻辑,对用户输入的密码也进行MD5处理后再校验
写一个工具类DigestUtil,由这个工具类来帮助我们生成用户输入的password对应的MD5
写一个工具类BytesToString,将字节数组转化为字符串
具体代码分别如下,具体过程请读者自己分析代码
package util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class DigestUtil {
public static String getMD5(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data);
byte[] resultBytes = md.digest();
String resultString = BytesToString.fromBytesToString(resultBytes);
return resultString;
}
public static String getSHA1(byte[] data) throws NoSuchAlgorithmException{
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(data);
byte[] resultBytes = md.digest();
String resultString = BytesToString.fromBytesToString(resultBytes);
return resultString;
}
}
package util;
public class BytesToString {
public static String fromBytesToString(byte[] resultBytes) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < resultBytes.length; i++) {
if (Integer.toHexString(0xFF & resultBytes[i]).length() == 1) {
builder.append("0").append(
Integer.toHexString(0xFF & resultBytes[i]));
} else {
builder.append(Integer.toHexString(0xFF & resultBytes[i]));
}
}
return builder.toString();
}
}
<%@page import=”util.DigestUtil”%>
<%@page import=”java.sql.ResultSet”%>
<%@page import=”java.sql.PreparedStatement”%>
<%@page import=”java.sql.DriverManager”%>
<%@page import=”java.sql.Connection”%>
<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>
首页
<%
//获取mysql连接对象
String driverClass=”com.mysql.jdbc.Driver”;
String user=”root”;
String psw=”zwkkwz”;
String url=”jdbc:mysql://localhost:3306/mytest”;
Class.forName(driverClass);
Connection conn=DriverManager.getConnection(url,user,psw);
//获取login.jsp传过来的username和password
String username=request.getParameter(“username”);
String password=request.getParameter(“password”);
String sql=”select * from login where username=?”;
PreparedStatement stmt=conn.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs=stmt.executeQuery();
%>
<%
if(rs.next()){
String p=rs.getString(“password”);
//简单的明文校验代码
/* if(password.equals(p)){
out.println(“用户”+username+”登录成功”);
}else{
out.println(“用户”+username+”登录失败”);
}
}else{
out.println(“用户”+username+”不存在”);
} */
//sql使用MD5加密后的密文和用户输入的password校验代码
if(p.equals(DigestUtil.getMD5(password.getBytes())))
//getMD5(byte[] data)方法是将原始的数据转换成加密后的密文
{
out.println(“数据库password字段”+p);
out.println(“用户输入的password”+password);
out.println(“经过处理后的password”+DigestUtil.getMD5(password.getBytes()));
out.println(“用户”+username+”登录成功”);
}else{
out.println("用户"+username+"登录失败");
}
}else{
out.println("用户"+username+"不存在");
}
%>
通过以上步骤,我们只对数据库的password明文字段进行了简单的MD5加密,有以下缺点:
容易根据密文位数推测算法,从而使用工具破解
真实密码相同,加密过的密码也相同
接下来我们介绍一下对其进行加盐处理:
3、加盐处理,以此来增强系统的复杂度,再通过摘要处理,就能得到隐蔽性更强的摘要值
将表中的salt字段随意输入abccba,然后和原来的明文密码123456结合,再进行SHA1加密
多建一对数据,将表中的salt字段输入cbaabc,然后和原来的明文密码123456结合,再进行SHA1加密
所谓的salt字段就是一个随机的字段,具体随机算法就不讨论了,每当用户注册账户时,后台就给它随机生成一个不同的字段
然后根据password和salt字段结合进行摘要处理,存在数据库表中的password字段,这样一来,原来明文都是123456生成的密文就不一样了
操作如下:
接下来改造index.jsp的处理逻辑,对于用户输入的密码进行SHA1处理后的工具类在上面的DigestUtil方法中有列举出
index.jsp代码如下:
<%@page import=”util.DigestUtil”%>
<%@page import=”java.sql.ResultSet”%>
<%@page import=”java.sql.PreparedStatement”%>
<%@page import=”java.sql.DriverManager”%>
<%@page import=”java.sql.Connection”%>
<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>
首页
<%
//获取mysql连接对象
String driverClass=”com.mysql.jdbc.Driver”;
String user=”root”;
String psw=”zwkkwz”;
String url=”jdbc:mysql://localhost:3306/mytest”;
Class.forName(driverClass);
Connection conn=DriverManager.getConnection(url,user,psw);
//获取login.jsp传过来的username和password
String username=request.getParameter(“username”);
String password=request.getParameter(“password”);
String sql=”select * from login where username=?”;
PreparedStatement stmt=conn.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs=stmt.executeQuery();
%>
<%
if(rs.next()){
String p=rs.getString(“password”);
//sql使用SHA1进行加盐加密后的密文和用户输入的password校验代码
//我们需要获取到用户输入的密码和对应的salt
String salt=rs.getString(“salt”);
if(p.equals(DigestUtil.getSHA1((password+salt).getBytes()))){
out.println(“数据库password字段”+p);
out.println(“用户输入的password”+password);
out.println(“经过处理后的password”+DigestUtil.getSHA1((password+salt).getBytes()));
out.println(“用户”+username+”登录成功”);
}else{
out.println("用户"+username+"登录失败");
}
}else{
out.println("用户"+username+"不存在");
}
%>
<%
rs.close();
stmt.close();
conn.close();
%>
以上的步骤我们只是对sql进行了加密操作。
为了防止用户输入密码在传输的过程中被抓包工具获取,我们还要在密码传输的过程中进行加密,这样可以使得获取到的也是密文。
使用MD5.js对表单加密(可以百度搜索MD5.js下载)
传输加密:在浏览器端发送出数据之前就要对数据进行加密处理。
在jsp引入MD5.js;给input标签指定一个id,然后在提交的时候给定一个方法onclick,来对用户输入的密码进行加密,具体的login.jsp如下:
账 号:<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>
登录界面
欢迎工作一到五年的Java工程师朋友们加入Java架构开发:855801563
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导
同时大家可以多多关注一下小编 纯干货 大家一起学习进步