1. 程式人生 > >使用者登入模組進行必要的安全處理(MD5加密、加鹽和傳輸過程加密)

使用者登入模組進行必要的安全處理(MD5加密、加鹽和傳輸過程加密)

1、首先簡談一下常規Web登入模組的開發(只為了實現簡單的登入功能,未對資料庫欄位進行加密處理以及傳輸過程中進行加密處理)

非安全性登入模組開發

使用JSP+MYSQL

資料庫表如下所示:   在這裡插入圖片描述    先用jsp頁面建立login.jsp和index.jsp頁面(為了方便講解,直接使用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="index.jsp" method="post">
    賬&nbsp;號:<input type="text" name="username"/><br/>
    密&nbsp;碼:<input type="password" name="password"><br/><br/>
        <input type="submit" value="提交"><br/>
</form>
</body>
</html>    
<%@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"%>
<!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>
<%
    //獲取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+"不存在");
    }
%>
</body>
</html>

通過上面兩個jsp頁面進行實現登入頁面的,可以實現校驗功能。但存在的安全隱患問題太多

資料庫以明文的形式進行儲存 資料傳輸的過程中未對資料進行加密處理(可以使用WireShark等抓包工具獲取post傳遞的明文資訊 2、接下來針對以上兩個問題進行分析和解決:

安全加固1:對資料庫表的password欄位進行摘要處理(在MYSQL中對資料摘要處理,sql語句如下)

//使用SHA進行摘要處理
UPDATE loginset password = SHA(password)

//使用MD5進行摘要處理
UPDATE userinfo set password = MD5(password)

原來明文123456 經過是用MD5加密後是e10adc3949ba59abbe56e057f20f883e 在這裡插入圖片描述

但是這樣子還是不安全,進入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();
    }

}

改造後的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"%>
<!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>
<%
    //獲取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+"不存在");
        }
%>
</body>
</html>

通過以上步驟,我們只對資料庫的password明文欄位進行了簡單的MD5加密,有以下缺點:

容易根據密文位數推測演算法,從而使用工具破解 真實密碼相同,加密過的密碼也相同 接下來我們介紹一下對其進行加鹽處理:

3、加鹽處理,以此來增強系統的複雜度,再通過摘要處理,就能得到隱蔽性更強的摘要值

將表中的salt欄位隨意輸入abccba,然後和原來的明文密碼123456結合,再進行SHA1加密 多建一對資料,將表中的salt欄位輸入cbaabc,然後和原來的明文密碼123456結合,再進行SHA1加密 在這裡插入圖片描述 所謂的salt欄位就是一個隨機的欄位,具體隨機演算法就不討論了,每當使用者註冊賬戶時,後臺就給它隨機生成一個不同的欄位

然後根據password和salt欄位結合進行摘要處理,存在資料庫表中的password欄位,這樣一來,原來明文都是123456生成的密文就不一樣了

操作如下:  在這裡插入圖片描述  得到加密後密文是(原來密碼都是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"%>
<!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>
<%
    //獲取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();
    %>
</body>
</html>

以上的步驟我們只是對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"%>
<!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>
<script type="text/javascript" src="md5.js"></script>
</head>
<body>
<form action="index.jsp" method="post">
    賬&nbsp;號:<input type="text" name="username"/><br/>
    密&nbsp;碼:<input type="password" name="password" id="password"><br/><br/>
    
    <input type="submit" value="提交" onclick="toMd5()"><br/>
</form>
</body>
<script type="text/javascript">
    function toMd5(){
        var passwordNode=document.getElementById("password");
        //加密前
        alert(passwordNode.value);
        var hash=hex_md5(passwordNode.value);
        passwordNode.value=hash;
        //加密後
        alert(passwordNode.value);
    }
</script>
</html>