PEP 578
Python Runtime Audit Hooks
Abstract
이 PEP는 Python API에 대한 추가 사항과 Python 런타임에서 수행한 작업을 감사 도구에 표시하도록 하는 CPython 구현에 대한 특정 동작을 설명합니다. 이러한 작업에 대한 가시성은 테스트 프레임워크, 로깅 프레임워크 및 보안 도구가 런타임에서 수행하는 작업을 모니터링하고 선택적으로 제한할 수 있는 기회를 제공합니다.
이 PEP는 실행 중인 Python 응용 프로그램에 대한 통찰력을 제공하기 위해 임의의 이벤트에 대한 API와 모듈 가져오기 시스템에 특정한 다른 API를 추가하는 것을 제안합니다. API는 모든 Python 구현에서 사용할 수 있도록 의도되었지만 사용된 특정 메시지와 값은 구현에서 사용자에게 정보를 제공하는 최선의 방법을 자유롭게 결정할 수 있도록 여기에서 지정되지 않습니다. CPython에서 사용될 가능성이 있는 몇 가지 예는 설명을 위해 제공됩니다.
이러한 감사 API를 사용하여 Python 런타임의 보안을 강화하는 방법에 대한 토론 및 권장 사항은 PEP 551을 참조하십시오.
간단한 사용법
def audit(event, args):
if event == 'code.__new__':
print(f'audit: {event} with args={args}')
sys.addaudithook(audit)
Overview
오늘날 많은 프로그래밍이 언어로 된 광범위한 생태계의 도움으로 수행됩니다. 따라서 각 프로그램은 주어진 문제를 해결하기 위해 다른 프로그램을 기반으로 합니다. 프로그램이 성장함에 따라 코드베이스에 추가된 다른 사람이 이미 해결한 작업이 점점 더 많이 수행되어야 합니다. 그러나 이것은 본질적으로 모듈의 유지 관리, 릴리스 주기, 문서, 안정성 등과 같은 특정 요소를 기반으로 합니다. 더 중요한 것은 코드가 광고하는 대로 수행해야 한다는 신뢰를 기반으로 한다는 것입니다. 많은 모듈이 오픈 소스이지만 API가 사용되지만 프로그래머는 소스 코드를 읽는 데 덜 귀찮습니다.
그러나 누군가가 의심스러운 활동을 조사하고 다른 사람에게 공개적으로 보고하지 않는 한 오랫동안 발견되지 않을 수 있는 한 릴리스 과정에서 모듈이 악의적인 작업을 수행하는 경우가 많습니다. 이것은 Ruby의 유명한 CSS 관련 라이브러리가 약 2,800만 번 다운로드한 관련 없는 코드에서 eval을 사용하여 GitHub 에 보고되는 보안 사고로 이어지는 몇 달 전과 같이 여러 번 발생했습니다 . Python도 이러한 공격과 다르지 않습니다. 타사 코드가 네트워크 요청, 평가 기능 등과 같은 작업인지 확인하는 구체적인 방법이 없기 때문입니다.
이 PEP는 특정 이벤트에 대해 트리거되는 감사 가능한 후크를 추가할 수 있는 API와 모니터링 등을 위한 추가 정보를 제공하기 위해 기존 시스템에서 사용할 수 있는 방법을 제공합니다. 아래에서 이것이 어떻게 사용될 수 있는지에 대한 간단한 프로그램을 설명할 것입니다.
어떤 수치 작업을 수행하는 프로그램을 작성하고 시리즈의 곱을 계산하려고 한다고 가정해 보겠습니다. 당연히 아래와 같이 쓸 수 있습니다.
# stats.py
from functools import reduce
def product(series):
return reduce(lambda acc, num: acc * num, series)
우리는 이미 이것을 수행하는 종속성으로 PyPI의 수학 라이브러리를 가지고 있으며 이를 유틸리티 함수로 다시 구현하고 싶지 않습니다. 그래서 우리는 프로그램에서 그것을 사용합니다.
모듈이 하는 일을 수행한다는 고유한 신뢰가 있으므로 API가 변경되지 않는 한 통계 종속성의 새로운 릴리스에 대해서도 코드를 살펴보는 데 신경쓰지 않아도 됩니다. 한 상태에서 라이브러리는 이제 http 요청을 하고 새 코드는 아래와 같은 악의적인 변경을 얻을 수 있습니다.
# stats.py
from functools import reduce
def product(series):
import urllib.request
try:
urllib.request.urlopen("http://example.com")
except:
pass
return reduce(lambda acc, num: acc * num, series)
코드를 다시 살펴보거나 네트워크 요청이 모니터링되는 샌드박스 환경이 없는 한 수행된 http 활동에 대한 단서가 거의 없습니다. PEP 578은 이를 모니터링할 수 있는 방법을 제공하고 sys.addaudithook생성된 모든 정의된 이벤트 집합에 대한 콜백으로 함수를 추가할 수 있는 sys 모듈에 함수를 추가합니다. 이것은 sys.settrace(trace)모든 함수 호출이 추적 함수를 거치는 것과 유사하지만 이 경우에는 이벤트일 뿐입니다. 목록 은 PEP에서 사용할 수 있습니다. 목록에는 urllib.Request모듈에 추가된 이벤트가 있으므로 urllib 모듈의 모든 http 요청에 대해 이벤트가 트리거됩니다. 콜백은 이벤트의 이름과 이벤트가 트리거되는 인수를 가져옵니다. 그래서 우리는 그것을 필터링 할 수 있습니다
코드를 다시 살펴보거나 네트워크 요청이 모니터링되는 샌드박스 환경이 없는 한 수행된 http 활동에 대한 단서가 거의 없습니다. PEP 578은 이를 모니터링할 수 있는 방법을 제공하고 sys 모듈 sys.addaudithook에 함수를 추가하여 정의된 모든 이벤트 집합에 대한 콜백으로 함수를 추가할 수 있습니다. 이것은 모든 함수 호출이 추적 함수를 거치지만 이 경우에는 이벤트일 뿐인 sys.settrace(trace)와 유사합니다. 목록은 PEP에서 사용할 수 있습니다. 목록에는 모듈에 추가된 urllib.Request 이벤트가 있으므로 urllib 모듈의 모든 http 요청에 대해 이벤트가 트리거됩니다. 콜백은 이벤트의 이름과 이벤트가 트리거되는 인수를 가져옵니다. 그래서 우리는 그것을 필터링 할 수 있습니다.
import sys
import stats
def audit_hook(event, args):
if event in ['urllib.Request']:
print(f"Network {event=} {args=}")
sys.addaudithook(audit_hook)
print(stats.product(range(1, 10)))
./python.exe audit_hooks_tut/app.py
Network event='urllib.Request' args=('http://example.com', None, {}, 'GET')
362880
위의 프로그램을 실행하면 이벤트 이름과 이벤트 인수가 제공됩니다. 이 이벤트는 sys.audit(event, args)를 통해 Python 프로그램을 통해 추가할 수 있으며 sys.audit('urllib.Request', req.full_url, req.data, req.headers, req.get_method())
형식의 공개 함수로 작성된 urllib.request에 대한 감사 이벤트를 볼 수 있으므로 urllib.request를 통해 이루어진 모든 http 요청에 대해 다음을 수행합니다. 이벤트는 hook 함수를 통해 전달됩니다.
Types of audit events
HTTP 요청은 하나의 이벤트이지만 해서는 안 되는 가져오기를 수행하고 있는지 확인하기 위해 모듈에서 가져온 가져오기를 알고 싶은 것과 같은 다른 것들이 있습니다. 위의 예에서 수학과 관련된 코드 또는 종속성이 이 사용 사례에는 필요하지 않지만 urllib.request 모듈에서 내부적으로 가져온 소켓 모듈을 가져오는지 확인하기 위해 다시 작성할 수 있습니다.
import sys
import stats
suspicious_modules = ['socket']
def audit_hook(event, args):
if event in ['urllib.Request']:
print(f"Network {event=} {args=}")
elif event in ['import'] and args[0] in suspicious_modules:
print(f"Suspicious import action {event=} module={args[0]}")
sys.addaudithook(audit_hook)
print(stats.product(range(1, 10)))
./python.exe audit_hooks_tut/app.py
Suspicious import action event='import' module=socket
Network event='urllib.Request' args=('http://example.com', None, {}, 'GET')
362880
다른 이벤트에는 평가, 컴파일을 사용한 코드 컴파일, 피클링, 파일 열기 등이 포함되며 필요하지 않을 수도 있습니다. 이 이벤트는 Python에서 정의할 수 있으므로 타사 라이브러리가 이벤트 세트를 제공할 수 있도록 프로그램에서 사용자 정의 이벤트를 정의할 수도 있습니다. 예를 들어 stats 모듈과 PyPI의 mod1을 사용할 때 내 프로그램에서도 사용되며 stats 모듈에서 간접적으로 사용하는 경우 내 프로그램의 mod1에 의해 정의된 이벤트가 있는지 모니터링할 수 있습니다. 이 경우 mod1은 감사할 수 있는 stats.py
에서 사용하는 도우미 make_request
를 제공할 수 있습니다.
# stats.py
from functools import reduce
def product(series):
import mod1
mod1.make_request("http://example.com")
return reduce(lambda acc, num: acc * num, series)
# app.py
import sys
import stats
suspicious_modules = ['socket']
def audit_hook(event, args):
if event in ['urllib.Request', 'make_request']:
print(f"Network {event=} {args=}")
elif event in ['import'] and args[0] in suspicious_modules:
print(f"Suspicious import action {event=} module={args[0]}")
sys.addaudithook(audit_hook)
print(stats.product(range(1, 10)))
With mod1 defining a custom audit hook we can audit it from our program as shown. Thus with the above setup it gives the below output.
./python.exe audit_hooks_tut/app.py
Network event='make_request' args=('http://example.com',)
362880
이것은 또한 라이브러리 작성자가 감사 목적으로도 사용할 수 있도록 라이브러리에 대한 후크를 제공할 수 있도록 합니다. 이것은 또한 연결된 구현 PR에 설명된 보다 세분화된 감사를 가능하게 하는 C API 및 기타 수단을 제공합니다. PEP에는 샌드박싱의 제한 사항을 설명하는 섹션도 있습니다. 이 PEP의 성능 영향도 무시할 수 있는 것으로 나타났습니다. 이 PEP는 PEP 551-Python 런타임의 보안 투명성과도 밀접한 관련이 있습니다.
이 트윗에서 Steve Dower가 언급했듯이. 위의 방법은 프로덕션 앱에서 사용하는 것이 좋습니다. PEP 551을 참조하십시오.