分散式運算與時間和狀態相關。也就是說,為了使多個元件進行通訊,必須共用狀態,並且這一切都需要時間。
大多數的程式設計師將他們的工作擬人化。他們想採用一種控制執行緒來執行整個程式,就像他們必須自己完成這項工作一樣。但是,現代的電腦可以非常快速地切換工作,並且在多核心多 CPU 或分散式系統中,兩個事件可能恰好同時發生。瑕疵急於填補程式設計師在程式執行模型與實際情況之間的差距。這些瑕疵與執行緒、處理序、時間和資訊之間的意外互動有關。這些互動透過共用狀態發生:信號、變數、檔案系統,以及基本上任何可以儲存資訊的項目。
HttpSessionState
屬性儲存,會破壞應用程式的可靠性。HttpSessionState
物件,以及在記憶體中它參照的屬性和任何物件。此模型會將作用中的階段作業狀態限制為,僅由單一機器的系統記憶體來提供。為了擴充這些限制的容量,要頻繁的配置伺服器以維持階段作業狀態資訊,如此不僅可擴充容量,還可在多台機器上進行複製以增進整體的效能。為了要維持其階段作業狀態,伺服器必須序列化 HttpSessionState
物件,此作業要求所有儲存在伺服器中的物件皆為可序列化的。[Serializable]
屬性。此外,如果物件要求自訂的序列化方法,它還必須執行 ISerializable
介面。
public class DataGlob {
String GlobName;
String GlobValue;
public void AddToSession(HttpSessionState session) {
session["glob"] = this;
}
}
sleep()
的同時還持續鎖定,會降低效能,而且可能造成鎖死。sleep()
的同時還持續鎖定會讓所有其他執行緒等待資源釋放,這會導致效能降低與鎖死。 sleep()
並同時持續鎖定。
ReentrantLock rl = new ReentrantLock();
...
rl.lock();
Thread.sleep(500);
...
rl.unlock();
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
Fitzer()
物件,但是又不希望每次呼叫此程式碼時都進行一次同步化。這就是所謂的 double-checked locking。Fitzer()
物件。請參閱 Double-Checked Locking is Broken 聲明以取得更多詳細資訊 [1]。pthread_mutex_unlock()
函數能開始執行之前,先呼叫此函數來解除 mutex 鎖定。如果發出訊號的執行緒沒能解除 mutex 鎖定,就不會回傳在第二個執行緒呼叫的 pthread_cond_wait()
,而且也不會執行該執行緒。 pthread_cond_signal()
,對一個在 mutex 上等待的執行緒發出訊號,但卻無法解除另一個執行緒在等待的 mutex 鎖定。
...
pthread_mutex_lock(&count_mutex);
// Signal waiting thread
pthread_cond_signal(&count_threshold_cv);
...
...
if (tmpnam_r(filename)){
FILE* tmp = fopen(filename,"wb+");
while((recv(sock,recvbuf,DATA_SIZE, 0) > 0)&&(amt!=0))
amt = fwrite(recvbuf,1,DATA_SIZE,tmp);
}
...
tmpnam()
、tempnam()
、mktemp()
這樣的 C 程式庫函數以及與其等價的以 _
(下劃線) 開頭的 C++ 文件和 Windows API 中的 GetTempFileName()
函數。這組函數在檔案名稱選擇方面很可能會有 Race Condition 的麻煩。雖然函數保證在選取檔案時,其檔案名稱是唯一的,但是沒有機制能防止其他處理或攻擊者在選取檔案後和應用程式試圖開啟檔案之前的這段時間內,建立一個相同檔案名稱。相比一個由其他程式呼叫相同函數所引發的合法衝突,攻擊者非常有可能建立一個惡意的衝突,因為由這些函數建立的檔案名稱沒有充分的隨機化來使猜測更加困難。open()
函數並使用 O_CREAT
和 O_EXCL
旗標,或者呼叫 CreateFile()
函數並使用 CREATE_NEW
屬性來開啟檔案。如此一來,如果檔案已經存在,那麼檔案建立就會失敗,也可以防範以上所描述的攻擊類型。然而,如果攻擊者可以準確預測一個檔案序列的檔案名稱,那麼應用程式將被強制禁止開啟所需的臨時儲存空間,進而引發 Denial of Service (DoS) 攻擊。如果使用由這些函數產生的一小部分亂數來選擇檔案名稱,那麼這種類型的攻擊將不難實現。tmpfile()
這樣的 C 函數和與之對應的以 _
(下劃線) 開頭的 C++ 函數,以及表現更為出色的 C 程式庫函數 mkstemp()
。tmpfile()
的函數構成唯一的檔案名稱,並藉由與傳遞 "wb+"
標記時 fopen()
使用的相同方法開啟檔案,就像讀/寫模式下的二位元檔一樣。如果檔案已存在,tmpfile()
將把檔案大小縮小為 0,藉此緩和前面提到的有關存在於唯一檔案名稱選擇與隨之打開選取檔案之間的 Race Condition 的安全問題。然而,這個運作方式顯然不能解決函數的安全性問題。首先,攻擊者可能使用可能由 tmpfile()
.開啟的檔案所保存的寬鬆存取權限來預先建立檔案。其次,在 Unix 系統上,如果攻擊者預先建立了一個檔案作為其他重要檔案的一個連接,應用程式可能會使用其被提升的權限去縮小該檔案,這樣就能按照攻擊的意願執行破壞。最後,如果 tmpfile()
建立了一個新檔案,那麼套用在此檔案上的存取權限在不同的作業系統間是不同的;如此一來,應用程式就極易受到攻擊,而攻擊者甚至不需要預先猜測到將被使用的檔案名稱也可進行攻擊。mkstemp()
函數是建立暫存檔案的安全方式。它將以一個使用者結合一組隨機生成的字元提供的檔案名稱範本的方式,來建立並開啟唯一的檔案。如果它不能建立此檔案,它將回傳 -1
表示失敗。在先進的系統上,是使用模式0600
來開啟檔案,這就意味著這個檔案將免受干擾,除非使用者改變其存取權限。然而,mkstemp()
仍然會受到使用能夠預先推測的檔案名稱的威脅,而且如果攻擊是藉由猜測或預先建立將要使用的檔案名稱的檔案導致 mkstemp()
函數失效的話,將使應用程式極易受到 Denial of Service 攻擊。
...
try:
tmp_filename = os.tempnam()
tmp_file = open(tmp_filename, 'w')
data = s.recv(4096)
while True:
more = s.recv(4096)
tmp_file.write(more)
if not more:
break
except socket.timeout:
errMsg = "Connection timed-out while connecting"
self.logger.exception(errMsg)
raise Exception
...
open()
函數並使用 os.O_CREAT
和 os.O_EXCL
旗標來開啟檔案。如此一來,如果檔案已經存在,那麼檔案建立就會失敗,也可以防範以上所描述的攻擊類型。然而,如果攻擊者可以準確預測一個檔案序列的檔案名稱,那麼應用程式將被強制禁止開啟所需的臨時儲存空間,進而引發 Denial of Service (DoS) 攻擊。如果使用由這些函數產生的一小部分亂數來選擇檔案名稱,那麼這種類型的攻擊將不難實現。tmpfile()
的函數。tmpfile()
的函數構成唯一的檔案名稱,並藉由與傳遞 "wb+"
標記時 open()
使用的相同方法開啟檔案,就像讀/寫模式下的二位元檔一樣。如果檔案已存在,tmpfile()
將把檔案大小縮小為 0,藉此緩和前面提到的有關存在於唯一檔案名稱選擇與隨之打開選取檔案之間的 Race Condition 的安全問題。然而,這個運作方式顯然不能解決函數的安全性問題。首先,攻擊者可能使用可能由 tmpfile()
.開啟的檔案所保存的寬鬆存取權限來預先建立檔案。其次,在 Unix 系統上,如果攻擊者預先建立了一個檔案作為其他重要檔案的一個連接,應用程式可能會使用其被提升的權限去縮小該檔案,這樣就能按照攻擊的意願執行破壞。最後,如果 tmpfile()
建立了一個新檔案,那麼套用在此檔案上的存取權限在不同的作業系統間是不同的;如此一來,應用程式就極易受到攻擊,而攻擊者甚至不需要預先猜測到將被使用的檔案名稱也可進行攻擊。
...
HttpSession sesssion = request.getSession(true);
sesssion.setMaxInactiveInterval(-1);
...
HttpSession
屬性儲存,會破壞應用程式的可靠性。HttpSession
物件,所以當一個 JVM 不可用時,另一個 JVM 可以在不中斷應用程式流程的情況下接替它。Serializable
介面。
public class DataGlob {
String globName;
String globValue;
public void addToSession(HttpSession session) {
session.setAttribute("glob", this);
}
}
...
var authenticated = true;
...
database_connect.query('SELECT * FROM users WHERE name == ? AND password = ? LIMIT 1', userNameFromUser, passwordFromUser, function(err, results){
if (!err && results.length > 0){
authenticated = true;
}else{
authenticated = false;
}
});
if (authenticated){
//do something privileged stuff
authenticatedActions();
}else{
sendUnathenticatedMessage();
}
true
,否則會設為 false
。很遺憾,由於回撥遭到 IO 封鎖,因此將會異步執行, 並可能在對 if (authenticated)
進行檢查後執行,而由於預設值為 true,因此無論使用者是否確實得到驗證,回撥都將進入 if 陳述式。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "app.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
setuid root
的程式。程式代表無權限使用者執行了特定的檔案操作,並使用存取檢查來確保它不使用其根權限執行目前使用者不應該執行的操作。程式使用 access()
系統呼叫來檢查在程式開啟檔案和執行必要操作之前,執行程式的使用者是否具有權限去存取這些指定的檔案。
if (!access(file,W_OK)) {
f = fopen(file,"w+");
operate(f);
...
}
else {
fprintf(stderr,"Unable to open file %s.\n",file);
}
access()
呼叫的運作方式在意料之中,而且,如果執行程式的使用者具有必要的權限來編輯檔案,那麼就會回傳 0
,其他情況則會回傳-1。無論怎樣,因為 access()
和 fopen()
都是對檔案名稱進行操作,而不是對檔案控制碼進行操作,所以當 file
變數傳送到 fopen()
的時候,就不能保證這個變數仍然能夠像傳送到 access()
的時候那樣參照磁碟上相同的檔案。如果攻擊者在 access()
呼叫之後,用指向不同檔案的一個象徵連結來取代 file
,程式就會使用它的根權限對檔進行操作,即使這個檔案攻擊者在其他情況下是無法篡改的。藉由欺騙程式去執行其他情況下不被允許的操作,攻擊者就能取得權限的提高。root
權限,因而沒有受到程式的限制。如果應用程式有能力執行攻擊者在其他情況下不被允許的任何操作,那麼這個程式就是一個可能的攻擊目標。
fd = creat(FILE, 0644); /* Create file */
if (fd == -1)
return;
if (chown(FILE, UID, -1) < 0) { /* Change file owner */
...
}
chown()
呼叫所操作的檔案與對 creat()
的呼叫所建立的檔案相同,但實際上未必如此。由於 chown()
是針對檔案名稱 (而非檔案控制碼) 進行操作,因此攻擊者可能會使用並非由攻擊者所擁有的檔案連結來取代檔案。隨後,對 chown()
的呼叫會為攻擊者提供所連結檔案的擁有權。CBL_CHECK_FILE_EXIST
常式,以在建立檔案之前先檢查檔案是否存在,並執行必要的操作。
CALL "CBL_CHECK_FILE_EXIST" USING
filename
file-details
RETURNING status-code
END-CALL
IF status-code NOT = 0
MOVE 3 to access-mode
MOVE 0 to deny-mode
MOVE 0 to device
CALL "CBL_CREATE_FILE" USING
filename
access-mode
deny-mode
device
file-handle
RETURNING status-code
END-CALL
END-IF
CBL_CHECK_FILE_EXIST
呼叫的運作方式在意料之中,並傳回一個非零值,表示該檔案不存在。不過,因為 CBL_CHECK_FILE_EXIST
和 CBL_CREATE_FILE
都是對檔案名稱進行操作,而不是對檔案控制碼進行操作,所以當 filename
變數傳遞到 CBL_CREATE_FILE
的時候,就不能保證這個變數仍然能夠像傳遞到 CBL_CHECK_FILE_EXIST
的時候那樣參照磁碟上相同的檔案。如果攻擊者在 CBL_CHECK_FILE_EXIST
呼叫後建立 filename
,CBL_CREATE_FILE
的呼叫將會失敗,進而導致程式認為該檔案是空的,但實際上它包含由攻擊者控制的資料。root
權限的程式,以代表無權限使用者執行特定檔案操作,並使用存取測試來確保它沒有使用其根權限來執行操作,這種權限對目前使用者來說在其他情況下是無法取得的。藉由欺騙程式去執行其他情況下不被允許的操作,攻擊者就可能取得提升的權限。java.text.Format
中的 parse()
和 format()
方法有設計上的缺陷,可能導致使用者看見其他使用者的資料。java.text.Format
中的 parse()
和 format()
方法有 race condition,可能導致使用者看見其他使用者的資料。
public class Common {
private static SimpleDateFormat dateFormat;
...
public String format(Date date) {
return dateFormat.format(date);
}
...
final OtherClass dateFormatAccess=new OtherClass();
...
public void function_running_in_thread1(){
System.out.println("Time in thread 1 should be 12/31/69 4:00 PM, found: "+ dateFormatAccess.format(new Date(0)));
}
public void function_running_in_thread2(){
System.out.println("Time in thread 2 should be around 12/29/09 6:26 AM, found: "+ dateFormatAccess.format(new Date(System.currentTimeMillis())));
}
}
format()
時發生 race condition,第一個執行緒的日期會在第二個執行緒的輸出中顯示。Windows.Storage.ApplicationData
類別的 RoamingFolder
或 RoamingSettings
屬性。RoamingFolder
和 RoamingSettings
屬性會取得漫遊應用程式資料存放區中的容器,該容器可用於在兩部或多部裝置之間共用資料。透過寫入及讀取儲存在漫遊應用程式資料存放區中的物件,開發人員會增加遭受危害的風險。這包括透過漫遊應用程式資料存放區來共用這些物件的資料、應用程式和系統的保密性、完整性與可用性。free()
兩次,會導致 Buffer overflow。當程式使用相同引數呼叫 free()
兩次,就會毀損程式中的記憶體管理資料結構。這樣的毀損會使程式當機,或者在某些情況下,還會導致之後兩次的 malloc()
呼叫回傳相同的指標。如果 malloc()
兩次回傳值都相同,且程式之後讓攻擊者控制整個已經寫入雙倍分配記憶體的資料,此程式就變得容易受到 Buffer overflow 攻擊。
void sh(int dummy) {
...
free(global2);
free(global1);
...
}
int main(int argc,char* argv[]) {
...
signal(SIGHUP,sh);
signal(SIGTERM,sh);
...
}
public class GuestBook extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req, HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
Dick
」指派至 name
Jane
」指派至 name
Jane, thanks for visiting!
」Jane, thanks for visiting!
」
public class ConnectionManager {
private static Connection conn = initDbConn();
...
}
<http auto-config="true">
...
<session-management session-fixation-protection="none"/>
</http>
Example 1
中,攻擊者使用直接明顯的方法並不高明,對於攻擊知名度較低的網站並不適用。但是,千萬不要過於大意,攻擊者有許多手段來突破上述攻擊的限制。攻擊者最常採用的技術包括:利用目標網站中的 Cross-Site Scripting 或 HTTP Response Splitting 弱點 [1]。透過誘使受害者向一個易受攻擊的應用程式提交惡意要求,讓應用程式將 JavaScript 或者其他程式碼反映到受害者的瀏覽器,這樣一來,攻擊者就可以建立 Cookie,使受害者重新使用受攻擊者控制的階段作業識別碼。bank.example.com
和 recipes.example.com
),其中一個應用程式存在弱點,那麼攻擊者便可以透過此弱點設定一個 Cookie,並在其中包含已修改的階段作業識別碼,且該階段作業識別碼可以在 example.com
[2] 網域上的所有應用程式中交互使用。use_strict_mode
屬性。
ini_set("session.use_strict_mode", "0");