分散式運算與時間和狀態相關。也就是說,為了使多個元件進行通訊,必須共用狀態,並且這一切都需要時間。
大多數的程式設計師將他們的工作擬人化。他們想採用一種控制執行緒來執行整個程式,就像他們必須自己完成這項工作一樣。但是,現代的電腦可以非常快速地切換工作,並且在多核心多 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);
...