Python 코드 인젝션

Python eval 및 exec 코드 인젝션

설명

Python의 eval()과 exec()는 문자열을 단순히 변환하는 API가 아닙니다. eval()은 문자열을 Python 표현식으로 해석하고, exec()는 문자열을 Python 문장 모음으로 해석하여 애플리케이션의 권한으로 실행합니다. HTTP 요청 등 신뢰할 수 없는 입력의 문자열을 이 함수에 전달하면 공격자가 애플리케이션의 제어 흐름을 바꾸는 Python 코드를 제공할 수 있습니다.

전역 및 지역 네임스페이스를 제한하거나 __builtins__ 매핑을 바꾸더라도 신뢰할 수 없는 코드를 안전하게 실행하는 보안 샌드박스가 되지는 않습니다. ast.literal_eval()은 Python 코드를 실행하지 않지만, Python 문서가 메모리·스택·CPU 고갈 가능성을 명시하므로 적대적 입력을 위한 범용 대안으로 권장할 수 없습니다.

잠재적 영향

  • 애플리케이션이 접근할 수 있는 데이터의 조회, 변경 또는 유출
  • 애플리케이션 상태와 제어 흐름의 변경
  • 프로세스 권한으로 가능한 파일·네트워크·운영 체제 기능의 실행
  • 예외, 무한 계산 또는 과도한 자원 소비로 인한 서비스 중단

명령 실행이 곧 운영 체제 권한 상승을 뜻하지는 않습니다. 실제 영향은 실행 프로세스의 권한, 사용 가능한 모듈, 비밀정보 및 네트워크 접근 범위에 따라 달라집니다.

해결 방법

  • 동적 평가 경로를 제거합니다. JSON처럼 정의된 데이터 형식이 필요하면 json.loads()로 파싱하고, 요청 크기를 제한하며, 파싱 결과의 구조·형식·범위를 검증한 뒤 데이터로만 처리합니다. JSON 파싱도 큰 입력으로 자원을 소모할 수 있으므로 입력 크기 제한을 함께 적용합니다.
  • 사용자가 작업을 선택해야 한다면 서버가 소유한 소수의 식별자를 사전 정의된 호출 가능 객체에 매핑하고, 선택한 객체를 직접 호출합니다. 허용 목록을 통과한 이름이라도 다시 문자열에 삽입하여 eval()이나 exec()에 전달하면 안 됩니다.
  • 제한된 전역 네임스페이스, 변경한 __builtins__, 단순 문자열 치환 또는 이름이 sanitize인 도우미를 보안 경계로 사용하지 않습니다.
  • 신뢰할 수 없는 Python 실행 자체가 명시적인 제품 요구사항이라면 애플리케이션 프로세스에서 실행하지 않습니다. 별도의 강화된 런타임에 격리하고 CPU·메모리·실행 시간·파일 시스템·네트워크·권한을 명시적으로 제한합니다.

예시

변경 전

python
from flask import Flask, request

app = Flask(__name__)

@app.get("/evaluate")
def evaluate():
    expression = request.args.get("expression", "")
    result = eval(expression)  # 취약: 요청 문자열을 Python 코드로 실행
    return {"result": str(result)}

변경 후

python
from flask import Flask, abort, request

app = Flask(__name__)

def service_status():
    return "healthy"

def application_version():
    return "2.0"

OPERATIONS = {
    "status": service_status,
    "version": application_version,
}

@app.get("/operation")
def run_operation():
    operation = OPERATIONS.get(request.args.get("name", ""))
    if operation is None:
        abort(400, description="Unsupported operation")

    return {"result": operation()}  # 서버가 등록한 객체를 직접 호출

안전한 예시는 요청값을 Python 문법으로 해석하지 않습니다. 요청값은 서버가 정의한 매핑의 키로만 사용되고, 선택된 호출 가능 객체가 직접 실행됩니다.

참조