[시큐어코딩 가이드] 경쟁조건: 검사시점과 사용시점(TOCTOU)

 

 

 

■ 시간 및 상태

 

동시 또는 거의 동시 수행을 지원하는 병렬 시스템이나 하나 이상의 프로세스가 동작되는 환경에서 시간 및 상태를 부적절하게 관리하여 발생할 수 있는 보안약점이다.

 

경쟁조건: 검사시점과 사용시점(TOCTOU)

 

■ 개요

 

병렬시스템(멀티프로세스로 구현한 응용프로그램)에서는 자원(파일, 소켓 등)을 사용하기에 앞서 자원의 상태를 검사한다.

 

하지만, 자원을 사용하는 시점과 검사하는 시점이 다르기 때문에, 검사하는 시점(Time Of Check)에 존재하던 자원이 사용하던 시점(Time Of Use)에 사라지는 등 자원의 상태가 변하는 경우가 발생한다.


예를 들어, 프로세스 A와 B가 존재하는 병렬시스템 환경에서 프로세스 A는 자원사용(파일 읽기)에 앞서 해당 자원(파일)의 존재 여부를 검사(TOC) 한다. 

 

이 때는 프로세스 B가 해당 자원(파일)을 아직 사용(삭제)하지 않았기 때문에, 프로세스 A는 해당 자원(파일)이 존재한다고 판단한다. 그러나 프로세스 A가 자원 사용(파일읽기)을 시도하는 시점(TOU)에 해당 자원(파일)은 사용불가능 상태이기 때문에 오류 등이 발생할 수 있다.


이와 같이 하나의 자원에 대하여 동시에 검사시점과 사용시점이 달라 생기는 보안약점으로 인해 동기화 오류뿐만 아니라 교착상태 등과 같은 문제점이 발생할 수 있다.


Python에서 멀티스레드 환경에서 공유 자원에 여러 쓰레드가 접근하는 것을 막기 위해 Lock 객체를 제공한다. 두 가지 기본 메서드가 제공된다. 

 

자원의 상태를 잠금으로 변경하는 acquire() 메서드와 사용 중인 자원을 해제하는 release()메서드를 제공한다.

 

 

■ 안전한 코딩 기법

 

공유자원(예: 파일)을 여러 프로세스가 접근하여 사용할 경우, 동기화 구문을 사용하여 한 번에 하나의 프로세스만 접근 가능하도록 하는 한편, 성능에 미치는 영향을 최소화하기 위해 임계코드 주변만 동기화 구문을 사용한다.


Python의 Lock 객체를 사용 시 lock.acquire()로 자원을 잠그고 lock.release()로 자원을 해제해야 하지만 with문을 사용하여 간단하게 표현도 가능하다.

 

 

코드예제

 

다음 예제는 파일의 검사 후 사용하는 코드가 호출되는 사이의 짧은 시간에도 파일이 사라져서 해당 동작을 할 수 없는 경우가 발생할 수 있다

 

안전하지 않은 코드의 예
import io
import datetime
import threading

def write_shared_file(filename, content):
# 멀티스레드 환경에서는 다른 사용자들의 작업에 따라 파일이 사라질
# 수 있기 때문에 공유 자원에 대해서는 검사와 사용을 동시에 해야 한다.
if os.path.isfile(filename) is True:
f = open(filename, 'w')
f.seek(0, io.SEEK_END)
f.write(content)
f.close()

def start():
filename = ‘./temp.txt’
content = ‘start time is ’ + datetime.datetime.now()
my_thread = threading.Thread(target=write_shared_file, args=(filename, content))
my_thread.start()

 

검사 후 파일이 삭제되거나 변동되는 것을 예방하기 위해 lock을 사용하여 각 쓰레드에서 공유자원에 접근하는 것을 통제하는 예제이다. lock을 acquire하면 해당 쓰레드만 공유 데이터에 접근 할 수 있고, lock을 release 해야만 다른 쓰레드에서 공유 데이터에 접근 할 수 있다.

 

안전한 코드의 예
import io
import datetime
import threading

lock = threading.Lock()
def write_shared_file(filename, content):
# lock을 이용하여 여러 사용자가 동시에 파일에 접근하지 못하도록 제한
with lock:
if os.path.isfile(filename) is True:
f = open(filename, 'w')
f.seek(0, io.SEEK_END)
f.write(content)
f.close()

def start():
filename = ‘./temp.txt’
content = ‘start time is ’ + datetime.datetime.now()
my_thread = threading.Thread(target=write_shared_file, args=(filename, content))
my_thread.start()

 

 

댓글

Designed by JB FACTORY