Skills-Berechtigungen und Sicherheitskontrolle
Skills können auf das Dateisystem zugreifen, externe APIs aufrufen und Skripte ausführen. Bei Missbrauch können diese Fähigkeiten Sicherheitsrisiken bergen.
Dieser Artikel erklärt, wie Sie bereits in der Entwurfsphase Sicherheitsgrenzen festlegen, um zu verhindern, dass ein Skill Operationen ausführt, die über die Erwartungen hinausgehen.
Standard-Berechtigungsumfang von Skills
In Claudes Ausführungsumgebung werden die Berechtigungen von Skills durch die Sandbox-Umgebung bestimmt und sind nicht unbegrenzt.
| Vorgangstyp | Berechtigungsstatus | Beschreibung |
|---|---|---|
| Hochgeladene Dateien lesen | Erlaubt | Beschränkt auf /mnt/user-data/uploads/ |
| Ausgabedateien schreiben | Erlaubt | Beschränkt auf /mnt/user-data/outputs/ und /home/claude/ |
| Systemdateien lesen | Restricted | Schreibgeschützter Mount, Systemdateien können nicht geändert werden |
| Auf externes Netzwerk zugreifen | Restricted | Nur Domains auf der Whitelist sind erlaubt |
| Beliebige Systembefehle ausführen | Restricted | Kann sudo nicht verwenden, kann die Systemkonfiguration nicht ändern |
| Zugriff auf Daten anderer Benutzer | Verboten | Durch Sandbox-Isolation gewährleistet |
Skills sind eine praktische Umsetzung des Prinzips der geringsten Berechtigung im Design: Ein Skill kann nur auf die Ressourcen zugreifen, die er ausdrücklich benötigt. Wenn ein Skill Berechtigungen über den oben genannten Umfang hinaus benötigt, sollte der Designansatz überdacht werden.
Berechtigungsgrenzen in SKILL.md klar definieren
In der Skill-Dokumentation klar angeben, auf welche Ressourcen zugegriffen wird, damit Benutzer den Verhaltensumfang des Skills vor der Verwendung verstehen können.
## 权限说明 本 Skill 会进行以下操作,请确认你已了解: **文件访问** - 读取:/mnt/user-data/uploads/ 下用户上传的文件 - 写入:/mnt/user-data/outputs/ 下的输出文件 **网络访问** - 无(本 Skill 不访问任何外部网络) **不会进行的操作** - 不读取系统文件 - 不修改已上传的原始文件 - 不访问任何外部服务
Verhinderung von Path-Traversal-Angriffen
Wenn ein Skript einen vom Benutzer bereitgestellten Dateipfad akzeptiert, muss es prüfen, ob der Pfad innerhalb des zulässigen Bereichs liegt, und verhindern, dass Benutzer../auf Verzeichnisse zugreifen, auf die sie nicht zugreifen sollten.
Beispiel
import os
# Verzeichnisse, die zum Lesen erlaubt sind
ALLOWED_READ_DIRS = [
"/mnt/user-data/uploads",
"/mnt/skills/public",
]
# Zum Schreiben erlaubte Verzeichnisse
ALLOWED_WRITE_DIRS = [
"/mnt/user-data/outputs",
"/home/claude",
]
def is_safe_path(path: str, allowed_dirs: list) -> bool:
"""
Prüfen, ob der Pfad innerhalb des erlaubten Verzeichnisbereichs liegt
Path-Traversal-Angriffe wie ../../../etc/passwd verhindern
"""
# In einen absoluten Pfad auflösen (.. und symbolische Links eliminieren)
real_path = os.path.realpath(os.path.abspath(path))
for allowed in allowed_dirs:
real_allowed = os.path.realpath(allowed)
# Prüfen, ob real_path mit dem erlaubten Verzeichnis beginnt
if real_path.startswith(real_allowed + os.sep) or real_path == real_allowed:
return True
return False
def safe_read_path(user_input: str) -> str:
"""Lese-Pfad validieren, Ausnahme auslösen, wenn ungültig"""
if not is_safe_path(user_input, ALLOWED_READ_DIRS):
raise PermissionError(
f"Zugriff verweigert: {user_input}\n"
f"Nur die folgenden Verzeichnisse sind zum Lesen erlaubt: {ALLOWED_READ_DIRS}"
)
return os.path.realpath(user_input)
def safe_write_path(user_input: str) -> str:
"""Schreibpfad validieren, bei Ungültigkeit eine Ausnahme auslösen"""
if not is_safe_path(user_input, ALLOWED_WRITE_DIRS):
raise PermissionError(
f"Schreiben verweigert: {user_input}\n"
f"Nur die folgenden Verzeichnisse sind zum Schreiben erlaubt: {ALLOWED_WRITE_DIRS}"
)
return os.path.realpath(user_input)
# Verwendungsbeispiel
if __name__ == "__main__":
# Normaler Pfad: bestanden
ok_path = safe_read_path("/mnt/user-data/uploads/example.csv")
print(f"Bestanden: {ok_path}")
# Traversal-Pfad: abgelehnt
try:
bad_path = safe_read_path("/mnt/user-data/uploads/../../etc/passwd")
except PermissionError as e:
print(f"Blockiert: {e}")
通过:/mnt/user-data/uploads/example.csv 已拦截:拒绝访问:/mnt/user-data/uploads/../../etc/passwd 只允许读取以下目录:['/mnt/user-data/uploads', '/mnt/skills/public']
Sichere Speicherung von API-Schlüsseln
Verschiedene Methoden der Schlüsselverwaltung unterscheiden sich deutlich im Sicherheitsniveau.
| Methode | Sicherheit | Empfehlungsstufe |
|---|---|---|
| Hardcodiert in Skripten | Extrem niedrig, wird in Git eingecheckt | Verboten |
| Umgebungsvariablen | Mittel, Prozessisolierung | Empfohlen (Entwicklungsphase) |
| .env-Datei (zu .gitignore hinzugefügt) | Mittel, lokale Dateispeicherung | Empfohlen (lokale Nutzung) |
| Systemschlüsselmanager (wie Vault) | Hoch, zentrale Verwaltung | Empfohlen (Produktionsumgebung) |
Beispiel
# Konfiguration sicher aus mehreren Quellen lesen, in absteigender Prioritätsreihenfolge suchen
import os
def get_secret(key: str, required: bool = True) -> str:
"""
Schlüssel aus den folgenden Quellen in der Reihenfolge ihrer Priorität lesen:
1. Umgebungsvariablen (höchste Priorität)
2. Datei /home/claude/.skill_secrets (lokale Schlüsseldatei)
3. Wenn required=True und nicht gefunden, eine Ausnahme auslösen
"""
# 1. Umgebungsvariablen
value = os.environ.get(key)
if value:
return value
# 2. Lokale Schlüsseldatei (Format jeder Zeile: KEY=VALUE)
secrets_file = "/home/claude/.skill_secrets"
if os.path.exists(secrets_file):
with open(secrets_file) as f:
for line in f:
line = line.strip()
if line.startswith(f"{key}="):
return line[len(key)+1:]
# 3. Nicht gefunden
if required:
raise EnvironmentError(
f"Erforderlicher Schlüssel fehlt: {key}\n"
f"Bitte setzen Sie die Umgebungsvariable: export {key}='Ihr Schlüssel'"
)
return ""
Sichere Filterung von Eingabeinhalten
Wenn ein Skill Benutzereingaben an Shell-Befehle übergibt, muss die Eingabe maskiert werden, um Befehlsinjektion zu verhindern.
Beispiel
import subprocess
import shlex
def safe_shell_exec(template: str, user_input: str) -> str:
"""
Benutzereingaben sicher in Shell-Befehle einbetten
Falscher Ansatz: os.system(f"process {user_input}") # Risiko der Befehlsinjektion!
Richtiger Ansatz: eine Argumentliste verwenden und subprocess die Escapierung übernehmen lassen
"""
# Verwenden Sie eine Liste statt einer Zeichenkette; subprocess übernimmt die Escapierung automatisch
cmd = ["python", "scripts/process.py", user_input]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return result.stdout
# Wenn Sie einen Befehl als String erstellen müssen, verwenden Sie shlex.quote, um Benutzereingaben zu escapen
def safe_string_exec(user_filename: str) -> str:
safe_name = shlex.quote(user_filename) # Automatisch Anführungszeichen hinzufügen und Sonderzeichen escapen
cmd = f"wc -l {safe_name}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.stdout
Andere ErweiterungenVerwenden Sie niemals
os.system(f"cmd {user_input}")diese Art, Befehle auszuführen. Wenn Benutzereingaben; rm -rf /oder ähnliche Inhalte enthalten, wird dies zu katastrophalen Folgen führen. Verwenden Sie immer die Listenargument-Form von subprocess.