文章大綱 一、加密與驗簽介紹二、介面驗簽實操三、項目源碼下載 一、加密與驗簽介紹 大多數公共網路是不安全的,一切基於HTTP協議的請求/響應(Request or Response)都是可以被截獲的、篡改、重放(重發)的。因此我們需要考慮以下幾點內容: 防偽裝攻擊(案例:在公共網路環境中,第三方 有 ...
文章大綱
一、加密與驗簽介紹
二、介面驗簽實操
三、項目源碼下載
一、加密與驗簽介紹
大多數公共網路是不安全的,一切基於HTTP協議的請求/響應(Request or Response)都是可以被截獲的、篡改、重放(重發)的。因此我們需要考慮以下幾點內容:
- 防偽裝攻擊(案例:在公共網路環境中,第三方 有意或惡意 的調用我們的介面)
- 防篡改攻擊(案例:在公共網路環境中,請求頭/查詢字元串/內容 在傳輸過程被修改)
- 防重放攻擊(案例:在公共網路環境中,請求被截獲,稍後被重放或多次重放)
- 防數據信息泄漏(案例:截獲用戶登錄請求,截獲到賬號、密碼等)
二、介面驗簽實操
1. 實操說明
介面加密與驗簽的方法有非常多,比如RSA(後期進行講解),基於token等方式,而對於普通項目,我認為最重要的是防偽裝攻擊、防篡改攻擊、防重放攻擊。因為接下來的實操,主要圍繞以下幾點進行。
2. 邏輯講解
客戶端操作
(1)用戶登錄成功後,會接收到對應的key值和key過期時間,該key是經過32位小寫加密,且編碼格式為UTF-8
(2)介面請求時,將請求的參數,通過key-value方式進行字典升序,且編碼成UTF-8形式
(3)將key值拼接在升序且編碼後的字元串前面,進行MD32位小寫加密,其編碼成UTF-8形式形成簽名,連同請求參數一同發送至後臺
(4)退出登錄時,需要通知後臺失效該用戶的key
(5)補充說明1:對於登錄介面,如果檢測到用戶賬號密碼錯誤,則判斷錯誤次數後,在一定時間內進行登錄禁止,如果登錄禁止解除後,用戶再次出現錯誤,則延長限制時間
(6)補充說明2:對於無需登錄介面,需要限制客戶端請求次數,進行介面防刷保護
服務端操作
(1)當用戶登錄成功時,生成與該用戶對應的key值返回給用戶端,同時將id與key緩存在redis中
(2)當接收到請求時,根據請求id去redis查詢對應key是多少,查不到則代表沒有請求許可權,將該用戶系統信息,請求項目名、介面名,請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)存進redis(緩存進磁碟),當ip錯誤次數超過一定次數後,限制ip訪問項目
(3)將key和請求參數按客戶端同樣方式進行簽名,與請求的sign進行比較
(4)如果驗簽不一致,將該用戶系統信息,請求項目名、介面名,請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)存進redis,當ip錯誤次數超過一定次數時,限制ip訪問所有項目,若驗簽通過,則進行介面放行,且將用戶系統信息,請求項目名、介面名,請求時間緩存進日誌中(存進磁碟)
Redis參數需記錄信息
1.用戶信息:id,用戶key,客戶端請求系統信息
2.驗簽錯誤信息:用戶系統信息,請求項目名、介面名、請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)
日誌緩存信息
介面請求成功信息:用戶系統信息,請求項目名、介面名,請求時間
3.代碼講解
用戶登錄成功、生成key參數
//模擬用戶登錄成功
public String getMd5Key() {
return "de456878b58568e29773e6a53b39d6ef";
}
獲取客戶端信息
/**
* 獲取客戶端的信息
* @author 吳曉暢
*
*/
public final class SystemUtils {
/**
* 獲取訪問者IP
* 在一般情況下使用Request.getRemoteAddr()即可,但是經過nginx等反向代理軟體後,這個方法會失效。
*
* 本方法先從Header中獲取X-Real-IP,如果不存在再從X-Forwarded-For獲得第一個IP(用,分割),
* 如果還不存在則調用Request .getRemoteAddr()。
* @param request
* @return
*/
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (ip!= null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (ip!= null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理後會有多個IP值,第一個為真實IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
/**
* 獲取來訪者的瀏覽器版本
* @param request
* @return
*/
public String getRequestBrowserInfo(HttpServletRequest request){
String browserVersion = null;
String header = request.getHeader("user-agent");
if(header == null || header.equals("")){
return "";
}
if(header.indexOf("MSIE")>0){
browserVersion = "IE";
}else if(header.indexOf("Firefox")>0){
browserVersion = "Firefox";
}else if(header.indexOf("Chrome")>0){
browserVersion = "Chrome";
}else if(header.indexOf("Safari")>0){
browserVersion = "Safari";
}else if(header.indexOf("Camino")>0){
browserVersion = "Camino";
}else if(header.indexOf("Konqueror")>0){
browserVersion = "Konqueror";
}
return browserVersion;
}
/**
* 獲取系統版本信息
* @param request
* @return
*/
public String getRequestSystemInfo(HttpServletRequest request){
String systenInfo = null;
String header = request.getHeader("user-agent");
if(header == null || header.equals("")){
return "";
}
//得到用戶的操作系統
if (header.indexOf("NT 6.0") > 0){
systenInfo = "Windows Vista/Server 2008";
} else if (header.indexOf("NT 5.2") > 0){
systenInfo = "Windows Server 2003";
} else if (header.indexOf("NT 5.1") > 0){
systenInfo = "Windows XP";
} else if (header.indexOf("NT 6.0") > 0){
systenInfo = "Windows Vista";
} else if (header.indexOf("NT 6.1") > 0){
systenInfo = "Windows 7";
} else if (header.indexOf("NT 6.2") > 0){
systenInfo = "Windows Slate";
} else if (header.indexOf("NT 6.3") > 0){
systenInfo = "Windows 9";
} else if (header.indexOf("NT 5") > 0){
systenInfo = "Windows 2000";
} else if (header.indexOf("NT 4") > 0){
systenInfo = "Windows NT4";
} else if (header.indexOf("Me") > 0){
systenInfo = "Windows Me";
} else if (header.indexOf("98") > 0){
systenInfo = "Windows 98";
} else if (header.indexOf("95") > 0){
systenInfo = "Windows 95";
} else if (header.indexOf("Mac") > 0){
systenInfo = "Mac";
} else if (header.indexOf("Unix") > 0){
systenInfo = "UNIX";
} else if (header.indexOf("Linux") > 0){
systenInfo = "Linux";
} else if (header.indexOf("SunOS") > 0){
systenInfo = "SunOS";
}
return systenInfo;
}
/**
* 獲取來訪者的主機名稱
* @param ip
* @return
*/
public String getHostName(String ip){
InetAddress inet;
try {
inet = InetAddress.getByName(ip);
return inet.getHostName();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "";
}
/**
* 命令獲取mac地址
* @param cmd
* @return
*/
private String callCmd(String[] cmd) {
String result = "";
String line = "";
try {
Process proc = Runtime.getRuntime().exec(cmd);
InputStreamReader is = new InputStreamReader(proc.getInputStream());
BufferedReader br = new BufferedReader (is);
while ((line = br.readLine ()) != null) {
result += line;
}
}catch(Exception e) {
e.printStackTrace();
}
return result;
}
/**
*
*
*
* @param cmd
* 第一個命令
*
* @param another
* 第二個命令
*
* @return 第二個命令的執行結果
*
*/
private String callCmd(String[] cmd,String[] another) {
String result = "";
String line = "";
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
proc.waitFor(); // 已經執行完第一個命令,準備執行第二個命令
proc = rt.exec(another);
InputStreamReader is = new InputStreamReader(proc.getInputStream());
BufferedReader br = new BufferedReader (is);
while ((line = br.readLine ()) != null) {
result += line;
}
}catch(Exception e) {
e.printStackTrace();
}
return result;
}
/**
*
*
*
* @param ip
* 目標ip,一般在區域網內
*
* @param sourceString
* 命令處理的結果字元串
*
* @param macSeparator
* mac分隔符號
*
* @return mac地址,用上面的分隔符號表示
*
*/
private String filterMacAddress(final String ip, final String sourceString,final String macSeparator) {
String result = "";
String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
Pattern pattern = Pattern.compile(regExp);
Matcher matcher = pattern.matcher(sourceString);
while(matcher.find()){
result = matcher.group(1);
if(sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
break; // 如果有多個IP,只匹配本IP對應的Mac.
}
}
return result;
}
/**
* @param ip
* 目標ip
* @return Mac Address
*
*/
private String getMacInWindows(final String ip){
String result = "";
String[] cmd = {"cmd","/c","ping " + ip};
String[] another = {"cmd","/c","arp -a"};
String cmdResult = callCmd(cmd,another);
result = filterMacAddress(ip,cmdResult,"-");
return result;
}
/**
*
* @param ip
* 目標ip
* @return Mac Address
*
*/
private String getMacInLinux(final String ip){
String result = "";
String[] cmd = {"/bin/sh","-c","ping " + ip + " -c 2 && arp -a" };
String cmdResult = callCmd(cmd);
result = filterMacAddress(ip,cmdResult,