代码执行的安全策略
能力限制、网络隔离、文件系统限制和超时设置
代码执行的安全策略 是 CoddyKit 上的免费 AI Agents 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 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 之外写入磁盘
使用只读根文件系统,并仅将 /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 ReadOnlyFileSystemseccomp 配置文件:过滤系统调用
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']输出大小限制
失控的打印循环可能生成数 GB 的输出。请将标准输出和标准错误限制在合理大小(例如 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 获取额外权限。即使代码进入 shell,也无法提升权限。
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'组合所有策略
生产级沙箱会组合使用所有防护层:移除操作系统权限能力、配置 seccomp 配置文件、使用只读文件系统、启用 no-new-privileges、禁用网络、限制资源并设置超时。深度防御可确保某一层被绕过时,另一层能够阻止攻击。
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)}记录策略违规
检测到策略违规(被阻止的导入、危险调用或超时)时,请记录包含代理会话 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 resultseccomp 配置文件中的“defaultAction: SCMP_ACT_ERRNO”设置有什么作用?
seccomp 配置文件控制进程可以进行哪些系统调用。defaultAction 字段决定对于配置文件中未明确列出的系统调用将采取什么措施。
安全策略回顾
有效的代码执行安全机制需要多层防护:移除 Linux 权限能力、使用 seccomp 过滤系统调用、使用带有 tmpfs 临时空间的只读文件系统、启用 no-new-privileges、禁用网络、限制内存、CPU 和时间,以及在 AST 层面检查导入。
单独任何一层都不够充分——深度防御意味着每一层都能弥补其他层可能被绕过的漏洞。
常见问题解答
「代码执行的安全策略」课时是免费的吗?
是的 — 「代码执行的安全策略」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。
「代码执行的安全策略」这节课中我会学到什么?
能力限制、网络隔离、文件系统限制和超时设置 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Agents 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Agents 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「代码执行的安全策略」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Agents 课中编写并运行代码吗?
能。每节 AI Agents 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。