0Pricing
AI Agents · Lezione

Policy di sicurezza per l’esecuzione del codice

Limitazioni delle capacità, isolamento della rete, limiti del file system e timeout.

Policy di sicurezza per l’esecuzione del codice è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.

Perché le policy da sole non bastano

Dire all'LLM "non accedere alla rete" non è un controllo di sicurezza, ma solo un suggerimento. L'LLM potrebbe ignorarlo, essere sottoposto a jailbreak oppure il codice generato potrebbe violare indirettamente la regola.

Le policy di sicurezza reali devono essere applicate al livello di esecuzione, non solo nel prompt.

Panoramica delle restrizioni delle capability

Le restrizioni delle capability limitano ciò che un processo in sandbox può fare a livello del sistema operativo. Le capability Linux suddividono i privilegi di root in funzionalità granulari che possono essere rimosse singolarmente.

La rimozione di tutte le capability tranne quelle minime necessarie è chiamata principio del privilegio minimo.

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

Blocco delle scritture su disco al di fuori di /tmp

Utilizzi un file system radice in sola lettura e monti solo /tmp come percorso scrivibile. In questo modo si impedisce al codice di modificare i file di sistema o di rendere persistenti i dati al di fuori dell'area temporanea designata.

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

Profili seccomp: filtro delle chiamate di sistema

seccomp (secure computing mode) consente di specificare una allowlist delle chiamate di sistema autorizzate. Qualsiasi chiamata di sistema non presente nell'elenco causa la terminazione del processo con SIGSYS.

Docker include un profilo seccomp predefinito. È possibile fornire un profilo JSON personalizzato per applicare restrizioni più severe.

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

Limiti delle risorse: CPU, memoria e tempo

Tre limiti delle risorse che ogni sandbox dovrebbe applicare:

  • Memoria: mem_limit — previene gli attacchi OOM
  • CPU: nano_cpus o cpu_quota — previene l'esaurimento della CPU
  • Tempo: timeout di esecuzione — previene i cicli infiniti
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

Allowlist degli import Python

Prima di eseguire il codice, analizzi l'AST alla ricerca delle istruzioni di importazione. Rifiuti qualsiasi importazione che non sia presente nella allowlist. Si tratta di un livello di difesa in profondità, non di un sostituto dei controlli a livello del sistema operativo, ma è utile per individuare tempestivamente le violazioni più evidenti delle policy.

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

Rilevamento di pattern di codice pericolosi

Oltre agli import, verifichi la presenza di pattern che aggirano le restrizioni sugli import: __import__, exec(), eval(), compile(), open() con percorsi esterni a /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']

Limiti delle dimensioni dell'output

Un ciclo di stampa senza fine può generare gigabyte di output. Limiti stdout/stderr a una dimensione ragionevole, ad esempio 1 MB, e tronchi l'output o termini il container se il limite viene superato.

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

Flag no-new-privileges

L'opzione di sicurezza no-new-privileges impedisce al processo di ottenere privilegi aggiuntivi tramite binari setuid o sudo. Anche se il codice apre una shell, non può aumentare i propri privilegi.

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'

Combinazione di tutte le policy

Un sandbox adatto alla produzione combina tutti i livelli: capability del sistema operativo rimosse, profilo seccomp, filesystem in sola lettura, no-new-privileges, rete disabilitata, limiti delle risorse e timeout. La difesa in profondità garantisce che un aggiramento di un livello venga bloccato da un altro.

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

Registrazione delle violazioni delle policy

Quando viene rilevata una violazione di una policy, ad esempio un import bloccato, una chiamata pericolosa o un timeout, registri l'evento con l'ID della sessione dell'agente, l'hash dello snippet di codice e il tipo di violazione. Questi dati alimentano un dashboard di sicurezza e aiutano a perfezionare le policy nel tempo.

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

Che cosa fa l'impostazione 'defaultAction: SCMP_ACT_ERRNO' del profilo seccomp?

I profili seccomp controllano quali chiamate di sistema può effettuare un processo. Il campo defaultAction determina che cosa accade alle syscall non elencate esplicitamente nel profilo.

Riepilogo delle policy di sicurezza

Per proteggere efficacemente l'esecuzione del codice sono necessari più livelli: capability Linux rimosse, filtraggio delle syscall tramite seccomp, filesystem in sola lettura con spazio temporaneo tmpfs, no-new-privileges, rete disabilitata, limiti di memoria/CPU/tempo e verifica degli import a livello di AST.

Nessun singolo livello è sufficiente: la difesa in profondità fa sì che ogni livello compensi gli aggiramenti degli altri.

Domande Frequenti

La lezione «Policy di sicurezza per l’esecuzione del codice» è gratuita?

Sì — il testo completo di «Policy di sicurezza per l’esecuzione del codice» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.

Cosa imparerò in «Policy di sicurezza per l’esecuzione del codice»?

Limitazioni delle capacità, isolamento della rete, limiti del file system e timeout. Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Agents?

Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Policy di sicurezza per l’esecuzione del codice»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Agents?

Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Sandbox per agenti basate su Docker
  2. Isolamento tramite VM per agenti che eseguono codice con elevati requisiti di sicurezza
  3. E2B e servizi sandbox cloud
  4. Policy di sicurezza per l’esecuzione del codice
← Torna a AI Agents