분산 컴퓨팅에서 중요한 것은 시간과 상태입니다. 즉, 둘 이상의 구성 요소가 통신하려면 상태를 공유해야 하며 시간이 걸립니다.
대부분의 프로그래머들은 자신들의 작업을 의인화합니다. 하나의 컨트롤 스레드로 마치 자신이 직접 작업한 것처럼 전체 프로그램을 수행할 수 있을 것으로 생각합니다. 하지만 최신 컴퓨터는 작업 간에 매우 빠르게 전환되므로 멀티코어, 멀티 CPU 또는 분산 시스템에서 두 이벤트가 정확히 동시에 발생할 수 있습니다. 프로그래머가 만든 프로그램 실행 모델과 실제 현실 간에 빠르게 결함이 생기기 마련입니다. 이러한 결함은 스레드, 프로세스, 시간 및 정보 간의 예기치 않은 상호작용과 관련이 있습니다. 이러한 상호 작용은 공유 상태를 통해 발생합니다. 세마포, 변수, 파일 시스템 그리고 정보를 저장할 수 있는 모든 것이 여기에 포함됩니다.
HttpSessionState
속성으로 저장하면 응용 프로그램 안정성을 해칠 수 있습니다.HttpSessionState
개체, 해당 속성 및 메모리에서 참조하는 모든 개체를 저장합니다. 이 모델은 단일 시스템의 시스템 메모리가 수용할 수 있는 상태로 활성 세션 상태를 제한합니다. 이 제약의 범위를 넘어 용량을 확장하려면 전체적인 성능을 개선하기 위해 용량을 확장하고 여러 시스템 간의 복제를 허용하는 세션 상태 정보를 지속하도록 서버를 자주 구성해야 합니다. 해당 세션 상태를 유지하려면 저장된 모든 개체가 serializable 상태인 HttpSessionState
를 서버가 직렬화해야 합니다.[Serializable]
속성을 선언해야 합니다. 또한, 개체가 사용자 지정 serialization 메서드를 사용해야 하는 경우 ISerializable
인터페이스도 구현해야 합니다.
public class DataGlob {
String GlobName;
String GlobValue;
public void AddToSession(HttpSessionState session) {
session["glob"] = this;
}
}
sleep()
을 호출하면 성능이 떨어질 수 있고 교착 상태(deadlock)가 발생할 수도 있습니다.sleep()
을 호출하면 다른 모든 스레드는 리소스가 해제되기를 기다리게 될 수 있어 성능이 저하되거나 교착 상태(deadlock)가 될 수 있습니다. 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" Declaration을 참조하십시오[1].pthread_mutex_unlock()
을 호출하여 뮤텍스를 잠금 해제해야 합니다. 스레드의 신호 처리가 뮤텍스의 잠금 해제를 실패하는 경우, 두 번째 스레드의 pthread_cond_wait()
호출은 반환되지 않고 이 스레드는 실행되지 않습니다. pthread_cond_signal()
를 호출하여 뮤텍스에 대기 중인 또 다른 스레드를 신호 처리하지만, 대기 중인 나머지 스레드를 뮤텍스의 잠금 해제하는 것은 실패합니다.
...
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);
}
...
GetTempFileName()
함수뿐 아니라 tmpnam()
, tempnam()
, mktemp()
및 이에 상응하는, 앞에 _
(밑줄)이 붙은 C++ 함수 등의 C 라이브러리가 있습니다 이 그룹의 함수는 속성 상 선택한 파일 이름에 대한 경쟁 조건(race condition)이 발생합니다. 함수가 파일 이름 선택 시점에 이름이 고유하다는 것은 보장하지만 이름을 선택한 후 응용 프로그램이 파일을 열기 전에 다른 프로세서나 공격자가 같은 이름의 파일을 생성하는 것을 방지하는 메커니즘이 없습니다. 같은 함수의 다른 호출로 인해 발생하는 자연스러운 충돌의 위험 외에도 이들 함수가 생성한 파일 이름이 추측하기 어려울 만큼 무작위성이 보장되지 않기 때문에 공격자가 악의적으로 충돌을 일으킬 수 있습니다.O_CREAT
및 O_EXCL
플래그를 사용한 open()
호출 또는 CREATE_NEW
속성을 사용한 CreateFile()
호출로 파일을 여는 것입니다. 이 방법은 파일이 이미 있을 때는 파일을 열지 않기 때문에 앞서 설명한 종류의 공격을 예방할 수 있습니다. 하지만 공격자가 임시 파일 이름의 순서를 정확하게 예측할 수 있는 경우 응용 프로그램이 필요한 임시 저장소를 여는 것을 막아 Denial Of Service(DoS) 공격을 일으킬 수 있습니다. 이런 종류의 공격은 이들 함수가 생성하는 파일의 이름 선택에 사용된 무작위성의 수준이 낮은 것을 감안하면 어렵지 않게 실행할 수 있습니다.tmpfile()
및 이에 상응하는, 앞에 _
(밑줄)이 붙은 C++ 함수뿐 아니라 동작 기능이 좀 더 우수한 C 라이브러리 함수 mkstemp()
등의 C 라이브러리 함수가 있습니다.tmpfile()
형식의 함수는 "wb+"
플래그가 전달되면(즉, 읽기/쓰기 모드의 이진 파일로 전달되면) 고유한 파일 이름을 생성하고 fopen()
과 같은 방식으로 파일을 엽니다. 파일이 이미 있으면, 앞에서 언급한 고유한 파일 이름 선택과 이후의 선택한 파일 열기 사이에 존재하는 경쟁 조건(race condition) 관련 보안 문제를 해결하기 위해 tmpfile()
은 크기 0으로 파일을 자릅니다. 하지만 이 동작으로 함수의 보안 문제를 해결하지는 못합니다. 첫째, 공격자는 대체로 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()
호출을 사용하여 파일을 여는 것입니다. 이 방법은 파일이 이미 있을 때는 파일을 열지 않기 때문에 앞서 설명한 종류의 공격을 예방할 수 있습니다. 하지만 공격자가 임시 파일 이름의 순서를 정확하게 예측할 수 있는 경우 응용 프로그램이 필요한 임시 저장소를 여는 것을 막아 Denial Of Service(DoS) 공격을 일으킬 수 있습니다. 이런 종류의 공격은 이들 함수가 생성하는 파일의 이름 선택에 사용된 무작위성의 수준이 낮은 것을 감안하면 어렵지 않게 실행할 수 있습니다.tmpfile()
과 같은 함수가 포함됩니다.tmpfile()
형식의 함수는 "wb+"
플래그가 전달되면(즉, 읽기/쓰기 모드의 이진 파일로 전달되면) 고유한 파일 이름을 생성하고 open()
과 같은 방식으로 파일을 엽니다. 파일이 이미 있으면, 앞에서 언급한 고유한 파일 이름 선택과 이후의 선택한 파일 열기 사이에 존재하는 경쟁 조건(race condition) 관련 보안 문제를 해결하기 위해 tmpfile()
은 크기 0으로 파일을 자릅니다. 하지만 이 동작으로 함수의 보안 문제를 해결하지는 못합니다. 첫째, 공격자는 대체로 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);
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
호출이 예상대로 작동하고 0이 아닌 값을 반환합니다. 이는 파일이 없음을 나타냅니다. 하지만 CBL_CHECK_FILE_EXIST
및 CBL_CREATE_FILE
은 모두 파일 핸들 대신 파일 이름을 사용하여 작업하기 때문에 filename
변수가 CBL_CHECK_FILE_EXIST
에 전달되었을 때 참조하던 파일과 CBL_CREATE_FILE
에 전달될 때 참조하는 파일이 여전히 동일한 것이라는 보장이 없습니다. 공격자가 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
속성은 로밍 응용 프로그램 데이터 저장소에서 컨테이너를 가져옵니다. 이 컨테이너는 2개 이상의 장치에서 데이터를 공유하는 데 사용할 수 있습니다. 개발자는 로밍 앱 데이터 저장소에 저장된 개체를 쓰고 읽음으로써 손상될 위험을 높입니다. 여기에는 로밍 앱 데이터 저장소를 통해 해당 개체를 공유하는 데이터, 응용 프로그램 및 시스템의 기밀성, 무결성 및 가용성이 포함됩니다.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!");
}
}
name
에 "Dick
" 할당name
에 "Jane
" 할당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 또는 기타 코드를 피해자의 브라우저에 다시 돌려보내는 취약한 응용 프로그램에 악성 요청을 전송하도록 하여 공격자는 피해자가 공격자가 제어하는 세션 ID를 다시 사용하도록 하는 쿠키를 만들 수 있습니다.bank.example.com
및 recipes.example.com
과 같이 같은 최상위 도메인에 있는 경우, 한 응용 프로그램의 취약점 때문에 공격자가 도메인 example.com
[2]에 있는 응용 프로그램과의 모든 상호 작용에 사용할 고정 세션 ID로 쿠키를 설정할 수 있습니다.use_strict_mode
속성을 비활성화합니다.
ini_set("session.use_strict_mode", "0");