0Pricing
AI Agents · Lekcja

Zasady bezpieczeństwa wykonywania kodu

Ograniczenia uprawnień, izolacja sieci, limity systemu plików i limity czasu.

Zasady bezpieczeństwa wykonywania kodu to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Dlaczego same zasady nie wystarczą

Powiedzenie LLM „nie uzyskuj dostępu do sieci” nie jest mechanizmem bezpieczeństwa — to tylko wskazówka. LLM może ją zignorować, ulec jailbreakowi, a wygenerowany kod może pośrednio naruszyć tę zasadę.

Rzeczywiste zasady bezpieczeństwa muszą być wymuszane na warstwie wykonawczej, a nie wyłącznie w promptcie.

Przegląd ograniczeń uprawnień

Ograniczenia uprawnień określają na poziomie systemu operacyjnego, jakie działania może wykonywać proces uruchomiony w piaskownicy. Mechanizm capabilities systemu Linux dzieli uprawnienia roota na precyzyjnie określone możliwości, które można niezależnie odbierać.

Odebranie wszystkich uprawnień z wyjątkiem minimalnego niezbędnego zestawu nosi nazwę zasady najmniejszych uprawnień.

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())

Blokowanie zapisów na dysku poza /tmp

Należy użyć głównego systemu plików tylko do odczytu i zamontować jako zapisywalny wyłącznie katalog /tmp. Zapobiega to modyfikowaniu przez kod plików systemowych lub utrwalaniu danych poza wyznaczonym obszarem roboczym.

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

Profile seccomp: filtrowanie wywołań systemowych

seccomp (tryb bezpiecznego przetwarzania) pozwala określić listę dozwolonych wywołań systemowych. Każde wywołanie systemowe, którego nie ma na liście, powoduje zakończenie procesu z sygnałem SIGSYS.

Docker dostarcza domyślny profil seccomp. Można podać niestandardowy profil JSON, aby wprowadzić bardziej restrykcyjne ograniczenia.

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())

Limity zasobów: CPU, pamięć i czas

Trzy limity zasobów, które powinna wymuszać każda piaskownica:

  • Pamięć: mem_limit — zapobieganie atakom OOM
  • CPU: nano_cpus lub cpu_quota — zapobieganie wyczerpaniu zasobów CPU
  • Czas: limit czasu wykonania — zapobieganie nieskończonym pętlom
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

Tworzenie listy dozwolonych importów Pythona

Przed wykonaniem kodu należy przeanalizować AST pod kątem instrukcji importu. Należy odrzucić każdy import, którego nie ma na liście dozwolonych. Jest to warstwa obrony wielowarstwowej — nie zastępuje kontroli na poziomie systemu operacyjnego, ale pomaga wcześnie wykrywać oczywiste naruszenia zasad.

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)'))  # []

Wykrywanie niebezpiecznych wzorców kodu

Oprócz importów należy sprawdzać wzorce omijające ograniczenia importowania: __import__, exec(), eval(), compile() oraz open() ze ścieżkami znajdującymi się poza /tmp.

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']

Limity rozmiaru danych wyjściowych

Niekończąca się pętla print może wygenerować gigabajty danych wyjściowych. Należy ograniczyć stdout/stderr do rozsądnego rozmiaru (np. 1 MB), a po przekroczeniu limitu skrócić dane lub zakończyć działanie kontenera.

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'])

Flaga no-new-privileges

Opcja bezpieczeństwa no-new-privileges uniemożliwia procesowi uzyskanie dodatkowych uprawnień za pośrednictwem plików binarnych setuid lub sudo. Nawet jeśli kod otworzy powłokę, nie może eskalować uprawnień.

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'

Łączenie wszystkich zasad

Sandbox klasy produkcyjnej łączy wszystkie warstwy: usunięte uprawnienia systemu operacyjnego, profil seccomp, system plików tylko do odczytu, no-new-privileges, wyłączoną sieć, limity zasobów oraz limit czasu. Strategia obrony wielowarstwowej sprawia, że obejście jednej warstwy zostaje zatrzymane przez inną.

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)}

Rejestrowanie naruszeń zasad

Po wykryciu naruszenia zasad (zablokowanego importu, niebezpiecznego wywołania lub przekroczenia limitu czasu) należy zarejestrować zdarzenie wraz z identyfikatorem sesji agenta, skrótem fragmentu kodu oraz typem naruszenia. Dane te zasilają pulpit bezpieczeństwa i pomagają z czasem dostrajać zasady.

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

Co robi ustawienie „defaultAction: SCMP_ACT_ERRNO” w profilu seccomp?

Profile seccomp określają, które wywołania systemowe proces może wykonywać. Pole defaultAction określa, co dzieje się z wywołaniami systemowymi, których nie wymieniono jawnie w profilu.

Podsumowanie zasad bezpieczeństwa

Skuteczne zabezpieczenie wykonywania kodu wymaga wielu warstw: usunięcia uprawnień systemu Linux, filtrowania wywołań systemowych za pomocą seccomp, systemu plików tylko do odczytu z przestrzenią roboczą tmpfs, no-new-privileges, wyłączonej sieci, limitów pamięci/CPU/czasu oraz sprawdzania importów na poziomie AST.

Pojedyncza warstwa nie wystarcza — obrona wielowarstwowa oznacza, że każda warstwa kompensuje obejścia pozostałych.

Często zadawane pytania

Czy lekcja „Zasady bezpieczeństwa wykonywania kodu” jest bezpłatna?

Tak — pełny tekst „Zasady bezpieczeństwa wykonywania kodu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Zasady bezpieczeństwa wykonywania kodu”?

Ograniczenia uprawnień, izolacja sieci, limity systemu plików i limity czasu. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Zasady bezpieczeństwa wykonywania kodu”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Sandboxy agentów oparte na Dockerze
  2. Izolacja maszyn wirtualnych dla agentów wykonujących kod o wysokim poziomie bezpieczeństwa
  3. E2B i chmurowe usługi sandboxów
  4. Zasady bezpieczeństwa wykonywania kodu
← Powrót do AI Agents