JSP安全編程執行個體淺析(中級)2
最後更新:2017-02-28
來源:互聯網
上載者:User
js|安全|編程
四、時刻牢記SQL注入
一般的編程書籍在教初學者的時候都不注意讓他們從入門時就培養安全編程的習慣。著名的《JSP編程思想與實踐》就是這樣向初學者示範編寫帶資料庫的登入系統的(資料庫為MySQL):
Statement stmt = conn.createStatement();
String checkUser = "select * from login where username = '
"+ userName +"'and userpassword = '"+ userPassword +"'";
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
response.sendRedirect("SuccessLogin.jsp");
else
response.sendRedirect("FailureLogin.jsp");
這樣使得盡信書的人長期使用這樣先天“帶洞”的登入代碼。如果資料庫裡存在一個名叫“jack”的使用者,那麼在不知道密碼的情況下至少有下面幾種方法可以登入:
使用者名稱:jack
密碼:' or 'a'='a
使用者名稱:jack
密碼:' or 1=1/*
使用者名稱:jack' or 1=1/*
密碼:(任意)
lybbs(淩雲論壇)ver 2.9.Server在LogInOut.java中是這樣對登入提交的資料進行檢查的:
if(s.equals("") ││ s1.equals(""))
throw new UserException("使用者名稱或密碼不能空。");
if(s.indexOf("'") != -1 ││ s.indexOf("\"") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf("\\") != -1)
throw new UserException("使用者名稱不能包括 ' \" \\ , 等非法字元。");
if(s1.indexOf("'") != -1 ││ s1.indexOf("\"") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf("\\") != -1)
throw new UserException("密碼不能包括 ' \" \\ * 等非法字元。");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("使用者名稱或密碼中不能用空格。");
但是我不清楚為什麼他只對密碼而不對使用者名稱過濾星號。另外,正斜杠似乎也應該被列到“黑名單”中。我還是認為用Regex只允許輸入指定範圍內的字元來得乾脆。
這裡要提醒一句:不要以為可以憑藉某些資料庫系統天生的“安全性”就可以有效地抵禦所有的攻擊。pinkeyes的那篇《PHP注入執行個體》就給那些依賴PHP的設定檔中的“magic_quotes_gpc = On”的人上了一課。
五、String對象帶來的隱患
Java平台的確使安全編程更加方便了。Java中無指標,這意味著 Java 程式不再像C那樣能對地址空間中的任意記憶體位置定址了。在JSP檔案被編譯成 .class 檔案時會被檢查安全性問題,例如當訪問超出數組大小的數組元素的嘗試將被拒絕,這在很大程度上避免了緩衝區溢位攻擊。但是,String對象卻會給我們帶來一些安全上的隱患。如果密碼是儲存在 Java String 對象中的,則直到對它進行垃圾收集或進程終止之前,密碼會一直駐留在記憶體中。即使進行了垃圾收集,它仍會存在於空閑記憶體堆中,直到重用該記憶體空間為止。密碼 String 在記憶體中駐留得越久,遭到竊聽的危險性就越大。更糟的是,如果實際記憶體減少,則作業系統會將這個密碼 String 換頁調度到磁碟的交換空間,因此容易遭受磁碟塊竊聽攻擊。為了將這種泄密的可能性降至最低(但不是消除),您應該將密碼儲存在 char 數組中,並在使用後對其置零(String 是不可變的,無法對其置零)。
六、安全執行緒初探
“JAVA能做的,JSP就能做”。與ASP、PHP等指令碼語言不一樣,JSP預設是以多線程方式執行的。以多線程方式執行可大大降低對系統的資源需求,提高系統的並發量及回應時間。線程在程式中是獨立的、並發的執行路徑,每個線程有它自己的堆棧、自己的程式計數器和自己的局部變數。雖然多線程應用程式中的大多數操作都可以並行進行,但也有某些操作(如更新全域標誌或處理共用檔案)不能並行進行。如果沒做好線程的同步,在大並發量訪問時,不需要惡意使用者的“熱心參與”,問題也會出現。最簡單的解決方案就是在相關的JSP檔案中加上: 指令,使它以單線程方式執行,這時,所有用戶端的請求以串列方式執行。這樣會嚴重降低系統的效能。我們可以仍讓JSP檔案以多線程方式執行,通過對函數上鎖來對線程進行同步。一個函數加上synchronized 關鍵字就獲得了一個鎖。看下面的樣本:
public class MyClass{
int a;
public Init() {//此方法可以多個線程同時調用
a = 0;
}
public synchronized void Set() {//兩個線程不能同時調用此方法
if(a>5) {
a= a-5;
}
}
}
但是這樣仍然會對系統的效能有一定影響。一個更好的方案是採用局部變數代替執行個體變數。因為執行個體變數是在堆中分配的,被屬於該執行個體的所有線程共用,不是安全執行緒的,而局部變數在堆棧中分配,因為每個線程都有它自己的堆棧空間,所以這樣線程就是安全的了。比如淩雲論壇中添加好友的代碼:
public void addFriend(int i, String s, String s1)
throws DBConnectException
{
try
{
if……
else
{
DBConnect dbconnect = new DBConnect("insert into friend (authorid,friendname) values (?,?)");
dbconnect.setInt(1, i);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect.close();
dbconnect = null;
}
}
catch(Exception exception)
{
throw new DBConnectException(exception.getMessage());
}
}
下面是調用:
friendName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
如果採用的是執行個體變數,那麼該執行個體變數屬於該執行個體的所有線程共用,就有可能出現使用者A傳遞了某個參數後他的線程轉為睡眠狀態,而參數被使用者B無意間修改,造成好友錯配的現象。