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.

VorgangstypBerechtigungsstatusBeschreibung
Hochgeladene Dateien lesenErlaubtBeschränkt auf /mnt/user-data/uploads/
Ausgabedateien schreibenErlaubtBeschränkt auf /mnt/user-data/outputs/ und /home/claude/
Systemdateien lesenRestrictedSchreibgeschützter Mount, Systemdateien können nicht geändert werden
Auf externes Netzwerk zugreifenRestrictedNur Domains auf der Whitelist sind erlaubt
Beliebige Systembefehle ausführenRestrictedKann sudo nicht verwenden, kann die Systemkonfiguration nicht ändern
Zugriff auf Daten anderer BenutzerVerbotenDurch 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

# Dateipfad: scripts/safe_path.py
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.

MethodeSicherheitEmpfehlungsstufe
Hardcodiert in SkriptenExtrem niedrig, wird in Git eingechecktVerboten
UmgebungsvariablenMittel, ProzessisolierungEmpfohlen (Entwicklungsphase)
.env-Datei (zu .gitignore hinzugefügt)Mittel, lokale DateispeicherungEmpfohlen (lokale Nutzung)
Systemschlüsselmanager (wie Vault)Hoch, zentrale VerwaltungEmpfohlen (Produktionsumgebung)

Beispiel

# Dateipfad: scripts/config.py
# 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

# Dateipfad: scripts/safe_exec.py
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

Verwenden Sie niemalsos.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.

Andere Erweiterungen