分布式计算是关于时间和状态的。也就是说,为了让多个组件相互通信,必须共享状态,所有这些都需要时间。
大多数程序员都会将其工作拟人化。他们会让一个控制线程以同样的方式(他们必须自己完成工作时采取的方式)执行整个程序。然而,现代计算机不同任务之间切换得非常快,在多核、多 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);
...
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-statement,确认用户实际上是否已经过身份验证。
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);
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
权限的程序,该程序可代表非特权用户执行某些文件操作,并使用访问检查来确保它不会使用其 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");