分布式计算是关于时间和状态的。也就是说,为了让多个组件相互通信,必须共享状态,所有这些都需要时间。
大多数程序员都会将其工作拟人化。他们会让一个控制线程以同样的方式(他们必须自己完成工作时采取的方式)执行整个程序。然而,现代计算机不同任务之间切换得非常快,在多核、多 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()
对象。有关更多详细信息,请参见 The "Double-Checked Locking is Broken" Declaration [1]。pthread_mutex_unlock()
来解除锁定 mutex,然后其他线程才能开始运行。如果发出信号的线程无法解除锁定 mutex,则在第二个线程中调用 pthread_cond_wait()
函数时不会返回任何值,该线程也将无法执行。 pthread_cond_signal()
向其他等待 mutex 的线程发出信号,但无法解除锁定 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
属性来打开文件,这样,如果文件已经存在,该操作就会失败,因此可以有效地防止上述攻击类型。然而,如果攻击者可以准确预测一系列临时文件名,那么就可以阻止应用程序打开必要的临时存储空间,从而导致拒绝服务 (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
...
os.O_CREAT
和 os.O_EXCL
标记调用 open()
来打开文件,如果该文件已存在,则操作将失败,从而可防止上述攻击类型。然而,如果攻击者可以准确预测一系列临时文件名,那么就可以阻止应用程序打开必要的临时存储空间,从而导致拒绝服务 (DoS) 攻击。如果仅从一小部分随机数中选择由这些函数生成的文件名,那么会很容易发动这种类型的攻击。tmpfile()
等函数。tmpfile()
样式的函数可以构造唯一的文件名,并在传递了 "wb+"
标志的情况下能够按照与 open()
函数相同的方式(即,作为在读/写模式下的二进制文件)打开文件。如果文件已存在,tmpfile()
将把文件的大小缩小为 0,也许能缓解前面提到的安全问题(唯一文件名的选择与随后打开所选文件之间的 race condition)。然而,该操作显然不能解决函数的安全性问题。首先,攻击者可以预先创建一个能轻松获取访问权限的文件,该文件可能会被用 tmpfile()
函数打开的文件保留。其次,在基于 Unix 的系统上,如果攻击者预先创建了一个文件作为另一重要文件的链接,应用程序可能会使用提高了的权限去截短该文件,这样就能按照攻击者的意愿执行破坏。最后,如果 tmpfile()
创建了一个新文件,那么应用在该文件上的访问权限在不同的操作系统间是不同的,因此,应用程序的数据极易受到攻击,即便是攻击者无法预测要使用的文件名。
...
HttpSession sesssion = request.getSession(true);
sesssion.setMaxInactiveInterval(-1);
...