Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 330 – Python 바이트코드 검증

Author:
Michel Pelletier <michel at users.sourceforge.net>
Status:
Rejected
Type:
Standards Track
Created:
17-Jun-2004
Python-Version:
2.6
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

Python 가상 머신(PVM)의 바이트코드가 “올바른 형식”이 아니면 값 스택의 언더플로 또는 오버플로를 일으키거나 PVM 프로그램 공간의 임의 영역을 읽고 쓰는 등의 다양한 오류를 발생시켜 PVM을 충돌시키거나 악용할 수 있습니다. 실행 전에 PVM 바이트코드가 일련의 간단한 제약을 위반하지 않는지 검증하면 이러한 종류의 오류 대부분을 제거할 수 있습니다.

이 PEP는 Python 가상 머신(PVM) 바이트코드의 형식과 구조에 대한 일련의 제약을 제안하고, 이 검증 과정의 Python 구현을 제공합니다.

선언

Guido는 검증 도구가 어느 정도 가치가 있다고 생각합니다. 누군가 이를 Tools/scripts에 추가하려 한다면 PEP는 필요하지 않습니다.

이러한 도구는 “bytecodehacks”의 출력이나 PYC 파일을 직접 편집한 결과를 검증하는 데 유용할 수 있습니다. 완전히 유효한 바이트코드도 여전히 끔찍한 일을 할 수 있으므로, 보안 수단으로서의 가치는 어느 정도 제한적입니다. 제한된 실행이라는 개념이 성공적으로 부활한다면 이러한 상황은 바뀔 수 있습니다.

동기

Python 가상 머신은 Python 언어에서 바이트코드 표현으로 컴파일된 Python 프로그램을 실행합니다. PVM은 실행되는 모든 바이트코드가 암묵적인 여러 제약과 관련하여 “올바른 형식”이라고 가정합니다. 이러한 제약 중 일부는 런타임에 검사되지만, 대부분은 발생시키는 오버헤드 때문에 검사되지 않습니다.

디버그 모드로 실행할 때 PVM은 특정 바이트코드가 이러한 제약을 위반하지 않도록 여러 런타임 검사를 수행하며, 이러한 검사는 어느 정도 바이트코드가 인터프리터를 충돌시키거나 악용하는 것을 방지합니다. 이러한 검사는 인터프리터에 측정 가능한 오버헤드를 추가하므로 일반적인 사용에서는 대개 비활성화됩니다.

올바른 형식이 아니며 디버그 모드로 실행되지 않는 PVM에서 실행되는 바이트코드는 다양한 치명적 및 비치명적 오류를 일으킬 수 있습니다. 일반적으로 잘못된 형식의 코드는 PVM에서 세그폴트를 일으키고 운영 체제가 인터프리터를 즉시 갑작스럽게 종료하게 합니다.

잘못된 형식의 바이트코드는 인터프리터를 악용하여 Python 바이트코드가 임의의 C 수준 기계 명령을 실행하거나 인터프리터의 비공개 내부 데이터 구조를 수정하도록 만들 수도 있습니다. 이를 영리하게 사용하면 애플리케이션이 객체에 적용하려는 어떤 형태의 보안 정책도 무력화할 수 있습니다.

현실적으로 악의적인 사용자가 악용을 목적으로 PVM에 유효하지 않은 바이트코드를 “주입”하는 것은 어렵겠지만 불가능하지는 않습니다. 버퍼 오버플로 및 메모리 덮어쓰기 공격은 일반적으로 잘 알려져 있으며, 특히 공격 페이로드가 네트워크를 통해 암호화되지 않은 상태로 전송되거나 파일 또는 네트워크 보안 권한의 취약점이 추가 공격의 발판으로 사용되는 경우에 그러합니다.

이상적으로는 바이트코드가 악의적으로 만들어졌는지 여부와 관계없이 PVM의 작동을 무력화하기 위해 기반 C 수준 데이터 구조를 읽거나 쓰는 것이 어떤 바이트코드에도 허용되어서는 안 됩니다. 간단한 실행 전 검증 단계로 바이트코드가 런타임에 값 스택을 오버플로 또는 언더플로하거나 PVM 프로그램 공간의 다른 민감한 영역에 접근할 수 없도록 보장할 수 있습니다.

이 PEP는 Python 바이트코드가 PVM에서 실행되기 전에 해당 명령과 피연산자에 대한 정적 및 구조적 제약을 준수하도록 수행해야 하는 몇 가지 검증 단계를 제안합니다. 이러한 단계는 간단하며 충돌을 일으킬 수 있는 유효하지 않은 바이트코드의 많은 유형을 찾아냅니다. 검증 패스를 통해 일부 런타임 검사를 사전에 제거할 수 있는 가능성도 있습니다.

물론 완전성과 안전성에 대한 모든 정의를 고려할 때 바이트코드가 “완전히 안전하다”고 검증할 방법은 없습니다. 바이트코드 검증을 수행하더라도 Python 프로그램은 다양한 이유로 세그폴트를 일으킬 수 있으며, 앞으로도 그럴 가능성이 높고, 치명적이든 아니든 여러 유형의 런타임 오류를 계속 일으킬 수 있습니다. 여기서 제안하는 검증 단계는 바이트코드 수준에서 많은 유형의 치명적이고 미묘한 오류를 일으킬 수 있는 쉽게 악용 가능한 허점을 단순히 막습니다.

현재 Java 가상 머신(JVM)은 여기서 제안하는 방식과 매우 유사한 방식으로 Java 바이트코드를 검증합니다. 따라서 JVM Specification version 2 [1], Sections 4.8 및 4.9를 아래에서 설명하는 제약 조건 일부의 기반으로 사용했습니다. 모든 Python 바이트코드 검증 구현은 최소한 이러한 제약 조건을 적용해야 하지만, 이러한 제약 조건으로만 제한될 필요는 없습니다.

바이트코드 명령어의 정적 제약 조건

  1. 바이트코드 문자열은 비어 있으면 안 됩니다. (len(co_code) > 0).
  2. 바이트코드 문자열은 최대 크기를 초과할 수 없습니다 (len(co_code) < sizeof(unsigned char) - 1).
  3. 바이트코드 문자열의 첫 번째 명령어는 인덱스 0에서 시작합니다.
  4. 올바른 피연산자 수를 가진 유효한 바이트코드만 바이트코드 문자열에 포함될 수 있습니다.

바이트코드 명령어 피연산자의 정적 제약 조건

  1. 점프 명령어의 대상은 코드 경계 내에 있어야 하며, 명령어와 해당 피연산자 사이가 아니라 명령어 위치에 있어야 합니다.
  2. LOAD_* 명령어의 피연산자는 해당 데이터 구조에 대한 유효한 인덱스여야 합니다.
  3. STORE_* 명령어의 피연산자는 해당 데이터 구조에 대한 유효한 인덱스여야 합니다.

바이트코드 명령어 간 구조적 제약 조건

  1. 각 명령어는 해당 명령어의 실행으로 이어지는 실행 경로와 관계없이 값 스택에 적절한 수의 인자가 있을 때만 실행되어야 합니다.
  2. 하나의 명령어가 여러 다른 실행 경로를 따라 실행될 수 있다면, 어떤 경로를 거쳤는지와 관계없이 해당 명령어가 실행되기 전에 값 스택의 깊이가 같아야 합니다.
  3. 실행 중 어느 시점에도 값 스택은 co_stacksize가 나타내는 깊이보다 더 깊어질 수 없습니다.
  4. 실행이 co_code의 끝을 벗어나서는 안 됩니다.

구현

이 PEP는 Python으로 작성된 Python 바이트코드 검증 구현을 위한 작업 문서입니다. 이 구현은 바이트코드를 실행하기 전에 PVM에서 암묵적으로 사용되지 않으며, 잘못된 바이트코드일 가능성을 우려하는 사용자가 다음 코드 조각을 사용하여 명시적으로 사용해야 합니다.:

import verify
verify.verify(object)

verify 모듈은 dis.dis와 동일한 종류의 인자, 즉 클래스, 메서드, 함수 또는 코드 객체를 받는 verify 함수를 제공합니다. 이 함수는 이 PEP의 사양에 따라 객체의 바이트코드가 올바른 형식인지 검증합니다.

코드 형식이 올바르면 verify 호출은 오류 없이 조용히 반환합니다. 오류가 발견되면 실패 원인을 나타내는 인자를 가진 VerificationError를 발생시킵니다. 어떤 방식으로든 오류를 처리할지, 아니면 그와 관계없이 잘못된 코드를 실행할지는 프로그래머에게 달려 있습니다.

Phillip Eby는 참조 구현에서 사용하는 바이트코드 스택 깊이 검증을 위한 의사 코드 알고리즘을 제안했습니다.

검증 관련 문제

이 PEP는 소수의 검증만 설명합니다. 논의와 분석을 통해 훨씬 더 많은 검증이 이루어지겠지만, 향후 검증을 수행하거나 사용자 정의된 프로젝트별 검증을 수행해야 할 가능성이 매우 높습니다. 이러한 이유로 테스트 구현에 향후 검증기를 등록할 수 있는 검증 등록 인터페이스를 추가하는 것이 바람직할 수 있습니다. 사용자 정의 검증기는 현재 구현을 서브클래싱하고 확장하여 기능을 추가할 수 있으므로 이러한 기능의 필요성은 크지 않습니다.

필요한 변경 사항

Armin Rigo는 여러 바이트코드의 스택 효과를 정적으로 분석할 수 있도록 수정이 필요하다고 지적했습니다. 다음과 같습니다: END_FINALLY, POP_BLOCK, MAKE_CLOSURE. Armin과 Guido는 이미 해당 명령어를 수정하는 방법에 동의했습니다. 현재 Python 구현은 이러한 명령어에 대한 처리를 하지 않습니다.

이 PEP는 검증 단계를 인터프리터에 추가하자고 제안하는 것이 아니라, 선택적으로 사용할 수 있도록 표준 라이브러리에 Python 구현을 제공하기만 합니다. 이 검증 절차를 C로 변환할지, PVM에 포함할지, 어떤 방식으로든 강제할지는 향후 논의로 남겨 둡니다.

참고 문헌