自建加密解密傳輸&kaptcha驗證碼使用次數限制思路
序:
剛來第一天環境剛搞好,svn剛裝完,賬號都還沒有,員工手冊還沒有學習完,就被要求開始擼程式碼。還好同事之間溝通狠順暢,直接可以找到人問東西。
今天主要比較緊急的待修復項:
1.驗證碼只能使用一次的問題
2.登入/修改密碼過程中資料傳輸簡單的decode不滿足安全性要求
首先說下這邊的驗證碼是怎麼生成的。一般我們已說到驗證碼只能用一次想到的肯定是利用redis儲存生成的驗證碼,設定時效性。但是這裡生成驗證碼用的是一個元件kaptcha,這個元件的核心思路就是生成了驗證碼放session裡,沒有設定有效時長這個說法。此外非同步校驗也是每次從session取比對,程式碼如下:
String captchaCode = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
if(captchaCode.equalsIgnoreCase(verificationCode)){
return "true";
}
如果比對一致就表示校驗通過。這裡許可權是使用shiro,登陸也校驗驗證碼,這時候是從token裡取比對。重點來了,我處理這裡保證驗證碼只能使用一次也是在這裡做的邏輯,也就是在shiro的繼承AuthorizingRealm裡的doGetAuthenticationInfo方法裡做文章,思路:就是在校驗token裡的驗證碼比對時從session刪除驗證碼,這樣再次傳這個驗證碼就過不了了,具體邏輯:
String captcha = (String) SecurityUtils
.getSubject()
.getSession()
.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
//刪除session裡的驗證碼 SecurityUtils.getSubject().getSession().removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
if(StringUtils.isBlank(captcha)) {
throw new IncorrectCaptchaException("驗證碼失效");
}
//比對token裡與session裡的驗證碼
if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha()) ){
throw new IncorrectCaptchaException("驗證碼錯誤");
}
另外做以上操作,注意需要配合頁面的重新整理做,比如頁面提示驗證失效或錯誤了需要重新獲取ajax生成(保證新生成一個放session裡備用)
接下來該說第二個問題,那就是加密傳輸的問題。
其實這裡的思路受起來也狠簡單,就是傳統的加密配合自己寫的加密使用。自己寫的加密的思路:
每個輸入的字元生成一個的字串,而這個字串裡某位是角標,真正的charAt(角標)位置才是輸入內容,其他位則是隨機生成,這個角標也是隨機的,所以生成的串每次看起來不一樣,這樣抓到的包看到的就是每次傳送的都不一樣,讓黑客難以分析出規律。前臺自建加密js:
function randomsort(d, c) {
return Math.random() > 0.5 ? -1 : 1
}
function encryption(e) {
var g = e.split("");
for (var f = 0; f < g.length; f++) {
var h = Math.floor(Math.random() * 3) + 1;
var c = (f + 1) + "0" + (h + 1);
for (var d = 0; d < h; d++) {
c += Math.floor(Math.random() * 9)
}
g[f] = c + g[f];
var b = 9 - g[f].length;
if (b > 2) {
for (d = 0; d < b; d++) {
g[f] += Math.floor(Math.random() * 9)
}
}
}
g.sort(randomsort);
var a = "";
for (f = 0; f < g.length; f++) {
a += g[f] + "$"
}
return a
};
頁面使用也超級簡單:
var username = encode(encryption($("#username").val()));
$("#sendUsername").val(username);
var password = encode(encryption($("#password").val()));
$("#sendPassword").val(password);
其實就是獲取使用者輸入的明文進行自建加密,然後再呼叫一次其他常規加密,這裡的encode就是其他的常規加密。
這樣因為自建加密生成的串隨機,那麼常規加密生成的就每次不一樣,儘管真正的每次使用者名稱密碼都一樣。
但是說實話,別人要是花店心思先獲取這個自建加密js(瀏覽器開啟debug就可以看到,不過這個js要想讓使用者看到但是不知道什麼意思除了變數定義用abcd外還可以加混淆,這樣真的絕大部分黑客就望而止步了),說遠了。
再來說說後臺的解密,其實根據這個js已經可以知道java的解密怎麼寫了,這裡貼出的我故意留個bug吧,可以用,但是會有問題,不能告訴大家見諒,如果真需要知道,可以單獨找我。
public static String decryp(String str){
String[] strs = str.split("[$]");
Map<Integer, String> map = new TreeMap<Integer, String>(
new Comparator<Integer>() {
public int compare(Integer obj1, Integer obj2) {
return obj2.compareTo(obj1);
}
});
for(String temp : strs){
Integer number = Integer.parseInt(temp.substring(0, temp.indexOf("0")));
temp = temp.substring(temp.indexOf("0") + 1, temp.length());
Integer index = Integer.parseInt(temp.substring(0, 1));
String[] temps = temp.split("");
String value = temps[index];
map.put(number, value);
}
Set<Integer> keySet = map.keySet();
Iterator<Integer> iter = keySet.iterator();
String result = "";
while (iter.hasNext()) {
Integer key = iter.next();
result = map.get(key) + result;
}
return result;
}
這是自建加密的後臺解密,常規加密的解密就不說了。
到此私密資料的安全傳輸就提高一個檔次了,加個混淆安全性就更上一層樓了。