0Pricing
AI Agents · 강의

코드 실행을 위한 보안 정책

권한 제한, 네트워크 격리, 파일 시스템 제한, 시간 초과를 설정합니다.

코드 실행을 위한 보안 정책은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

정책만으로는 충분하지 않은 이유

LLM에 '네트워크에 접근하지 마십시오'라고 말하는 것은 보안 통제가 아니라 힌트일 뿐입니다. LLM이 이를 무시하거나 탈옥될 수 있고, 생성된 코드가 간접적으로 규칙을 위반할 수도 있습니다.

실제 보안 정책은 프롬프트에만 적용하지 말고 실행 계층에서 적용해야 합니다.

기능 제한 개요

기능 제한은 샌드박스 처리된 프로세스가 OS 수준에서 수행할 수 있는 작업을 제한합니다. Linux 기능은 root 권한을 세분화된 능력으로 나누므로 각 능력을 개별적으로 제거할 수 있습니다.

필요한 최소한의 기능을 제외한 모든 기능을 제거하는 것을 최소 권한 원칙이라고 합니다.

import docker
client = docker.from_env()

# Drop all Linux capabilities
output = client.containers.run(
    'python:3.12-slim',
    'python -c "print(\"safe run\")"',
    cap_drop=['ALL'],
    network_disabled=True,
    mem_limit='256m',
    auto_remove=True
)
print(output.decode())

외부 디스크 쓰기 차단하기

읽기 전용 루트 파일 시스템을 사용하고 /tmp만 쓰기 가능하도록 마운트하십시오. 이렇게 하면 코드가 시스템 파일을 수정하거나 지정된 임시 작업 영역 외부에 데이터를 저장하는 것을 방지할 수 있습니다.

import tempfile, docker
client = docker.from_env()

with tempfile.TemporaryDirectory() as tmpdir:
    output = client.containers.run(
        'python:3.12-slim',
        'python -c "open(\"/tmp/ok.txt\",\"w\").write(\"ok\"); print(open(\"/tmp/ok.txt\").read())"',
        read_only=True,
        tmpfs={'/tmp': 'size=64m,mode=1777'},
        network_disabled=True,
        mem_limit='128m',
        auto_remove=True
    )
    print(output.decode())  # 'ok' — /tmp write allowed
    # Writing anywhere else raises ReadOnlyFileSystem

seccomp 프로필: 시스템 호출 필터링

seccomp(보안 컴퓨팅 모드)를 사용하면 허용되는 시스템 호출의 허용 목록을 지정할 수 있습니다. 목록에 없는 시스템 호출은 프로세스를 SIGSYS와 함께 종료시킵니다.

Docker에는 기본 seccomp 프로필이 포함되어 있습니다. 더 엄격하게 제한하려면 사용자 지정 JSON 프로필을 제공할 수 있습니다.

import json, docker
client = docker.from_env()

# Minimal seccomp profile: only allow read, write, open, close, exit
seccomp_profile = json.dumps({
    'defaultAction': 'SCMP_ACT_ERRNO',
    'architectures': ['SCMP_ARCH_X86_64'],
    'syscalls': [
        {'names': ['read', 'write', 'open', 'openat', 'close',
                   'fstat', 'mmap', 'mprotect', 'munmap', 'brk',
                   'exit', 'exit_group', 'rt_sigaction',
                   'rt_sigprocmask', 'futex'],
         'action': 'SCMP_ACT_ALLOW'}
    ]
})

output = client.containers.run(
    'python:3.12-slim',
    'python -c "print(1+1)"',
    security_opt=[f'seccomp={seccomp_profile}'],
    auto_remove=True
)
print(output.decode())

리소스 제한: CPU, 메모리, 시간

모든 샌드박스가 적용해야 하는 세 가지 리소스 제한은 다음과 같습니다.

  • 메모리: mem_limit — OOM 공격 방지
  • CPU: nano_cpus 또는 cpu_quota — CPU 고갈 방지
  • 시간: 실행 제한 시간 — 무한 루프 방지
output = client.containers.run(
    'python:3.12-slim',
    'python -c "import time; time.sleep(100)"',
    mem_limit='128m',
    nano_cpus=500_000_000,   # 0.5 CPU
    network_disabled=True,
    auto_remove=True,
    timeout=10               # kill after 10 seconds
)
# Raises ReadTimeout after 10 seconds

Python 가져오기 허용 목록 만들기

코드를 실행하기 전에 AST에서 가져오기 문을 검색하십시오. 허용 목록에 없는 가져오기는 모두 거부하십시오. 이는 심층 방어 계층으로, OS 수준 제어를 대체하지는 않지만 명백한 정책 위반을 조기에 발견하는 데 유용합니다.

import ast

ALLOWED_IMPORTS = {'math', 'statistics', 'json', 'csv', 'datetime', 'collections', 're'}

def check_imports(code: str) -> list[str]:
    tree = ast.parse(code)
    blocked = []
    for node in ast.walk(tree):
        if isinstance(node, ast.Import):
            for alias in node.names:
                if alias.name.split('.')[0] not in ALLOWED_IMPORTS:
                    blocked.append(alias.name)
        elif isinstance(node, ast.ImportFrom):
            if node.module and node.module.split('.')[0] not in ALLOWED_IMPORTS:
                blocked.append(node.module)
    return blocked

print(check_imports('import socket; print(1)'))   # ['socket']
print(check_imports('import math; print(math.pi)'))  # []

위험한 코드 패턴 감지

가져오기뿐만 아니라 가져오기 제한을 우회하는 패턴도 검사해야 합니다. 예를 들어 __import__, exec(), eval(), compile(), 그리고 /tmp 외부 경로를 사용하는 open()이 있습니다.

import ast

DANGEROUS_CALLS = {'exec', 'eval', 'compile', '__import__', 'breakpoint'}

def check_dangerous_calls(code: str) -> list[str]:
    tree = ast.parse(code)
    found = []
    for node in ast.walk(tree):
        if isinstance(node, ast.Call):
            if isinstance(node.func, ast.Name):
                if node.func.id in DANGEROUS_CALLS:
                    found.append(node.func.id)
            elif isinstance(node.func, ast.Attribute):
                if node.func.attr in DANGEROUS_CALLS:
                    found.append(node.func.attr)
    return found

print(check_dangerous_calls('exec("import os")'))  # ['exec']

출력 크기 제한

무한히 실행되는 출력 반복문은 기가바이트 단위의 출력을 생성할 수 있습니다. 표준 출력과 표준 오류의 크기를 적절한 수준(예: 1MB)으로 제한하고, 한도를 초과하면 컨테이너의 출력을 잘라 내거나 컨테이너를 종료해야 합니다.

MAX_OUTPUT_BYTES = 1 * 1024 * 1024  # 1 MB

def run_with_output_limit(client, code: str) -> dict:
    container = client.containers.run(
        'python:3.12-slim',
        f'python -c "{code}"',
        mem_limit='256m',
        network_disabled=True,
        detach=True
    )
    try:
        container.wait(timeout=30)
        logs = container.logs(stdout=True, stderr=True)
        if len(logs) > MAX_OUTPUT_BYTES:
            return {'success': False, 'error': 'Output too large'}
        return {'success': True, 'output': logs.decode(errors='replace')}
    finally:
        container.remove(force=True)

if __name__ == '__main__':
    class FakeContainer:
        def wait(self, timeout=30): pass
        def logs(self, stdout=True, stderr=True): return b'Analysis complete: 42 rows processed.'
        def remove(self, force=True): pass
    class FakeContainers:
        def run(self, *args, **kwargs): return FakeContainer()
    class FakeClient:
        containers = FakeContainers()

    result = run_with_output_limit(FakeClient(), 'print(42)')
    print('Success:', result['success'])
    print('Output :', result['output'])

새 권한 금지 플래그

no-new-privileges 보안 옵션은 setuid 바이너리나 sudo를 통해 프로세스가 추가 권한을 얻지 못하도록 합니다. 코드가 셸로 진입하더라도 권한을 상승시킬 수 없습니다.

output = client.containers.run(
    'python:3.12-slim',
    'python -c "import os; print(os.getuid())"',
    user='nobody',                              # run as non-root
    security_opt=['no-new-privileges:true'],    # no privilege escalation
    cap_drop=['ALL'],                           # no capabilities
    network_disabled=True,
    mem_limit='128m',
    auto_remove=True
)
print(output.decode())  # numeric UID of 'nobody'

모든 정책 결합

운영 환경 수준의 샌드박스는 모든 보안 계층을 결합합니다. OS 기능을 제거하고, seccomp 프로필을 적용하며, 읽기 전용 파일 시스템과 새 권한 금지 설정을 사용하고, 네트워크를 비활성화하며, 리소스 제한과 시간 제한을 설정합니다. 심층 방어를 적용하면 한 계층의 우회가 발생해도 다른 계층에서 차단할 수 있습니다.

import tempfile, json, docker
client = docker.from_env()

def maximum_security_run(code: str) -> dict:
    with tempfile.TemporaryDirectory() as tmpdir:
        try:
            out = client.containers.run(
                'python:3.12-slim',
                f'python -c "{code}"',
                user='nobody',
                security_opt=['no-new-privileges:true'],
                cap_drop=['ALL'],
                network_disabled=True,
                read_only=True,
                tmpfs={'/tmp': 'size=32m,mode=1777'},
                mem_limit='128m',
                nano_cpus=500_000_000,
                auto_remove=True,
                timeout=15
            )
            return {'success': True, 'output': out.decode()}
        except Exception as e:
            return {'success': False, 'error': str(e)}

정책 위반 기록

정책 위반(차단된 가져오기, 위험한 호출, 시간 제한 초과)이 감지되면 agent 세션 ID, 코드 조각의 해시, 위반 유형과 함께 이벤트를 기록해야 합니다. 이 기록은 보안 대시보드에 제공되며 시간이 지나면서 정책을 조정하는 데 도움이 됩니다.

import hashlib, logging, time

logging.basicConfig(level=logging.INFO)
security_logger = logging.getLogger('sandbox.security')

def policy_check_and_run(code: str, session_id: str) -> dict:
    violations = check_imports(code) + check_dangerous_calls(code)
    code_hash = hashlib.sha256(code.encode()).hexdigest()[:16]

    if violations:
        security_logger.warning(
            'POLICY_VIOLATION session=%s hash=%s violations=%s',
            session_id, code_hash, violations
        )
        return {'success': False, 'error': f'Blocked: {violations}'}

    result = maximum_security_run(code)
    security_logger.info(
        'EXEC session=%s hash=%s success=%s',
        session_id, code_hash, result['success']
    )
    return result

seccomp 프로필의 'defaultAction: SCMP_ACT_ERRNO' 설정은 무엇을 하나요?

seccomp 프로필은 프로세스가 호출할 수 있는 시스템 호출을 제어합니다. defaultAction 필드는 프로필에 명시적으로 나열되지 않은 시스템 호출에 어떤 동작을 적용할지 결정합니다.

보안 정책 요약

효과적인 코드 실행 보안을 위해서는 여러 계층이 필요합니다. Linux 기능 제거, seccomp 시스템 호출 필터링, 임시 작업 공간으로 사용할 tmpfs를 포함한 읽기 전용 파일 시스템, 새 권한 금지, 네트워크 비활성화, 메모리/CPU/시간 제한, AST 수준의 가져오기 검사가 이에 해당합니다.

어느 한 계층만으로는 충분하지 않습니다. 심층 방어란 각 계층이 다른 계층의 우회를 보완하도록 하는 것입니다.

자주 묻는 질문

“코드 실행을 위한 보안 정책” 강의는 무료인가요?

네 — “코드 실행을 위한 보안 정책” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“코드 실행을 위한 보안 정책”에서 뭘 배우나요?

권한 제한, 네트워크 격리, 파일 시스템 제한, 시간 초과를 설정합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“코드 실행을 위한 보안 정책” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Docker 기반 에이전트 샌드박스
  2. 고도 보안 코드 에이전트를 위한 VM 격리
  3. E2B 및 클라우드 샌드박스 서비스
  4. 코드 실행을 위한 보안 정책
← AI Agents(으)로 돌아가기