사용자 제어 시스템 프롬프트

사용자 제어 시스템 프롬프트

설명

서버가 사용자 입력이나 외부에서 받은 신뢰되지 않은 데이터를 system, developer, instructions 같은 고권한 프롬프트 위치에 직접 넣으면 모델 동작을 의도와 다르게 재정의하도록 유도할 수 있습니다. 이 경우 안전 정책 우회, 민감 정보 노출, 권한 있는 도구 호출 오용 같은 문제가 발생할 수 있습니다.

잠재적 영향

  • 모델의 정책과 역할이 사용자 입력으로 덮어써질 수 있습니다.
  • 안전 장치 우회, 내부 지침 노출, 민감 작업 오용으로 이어질 수 있습니다.
  • 이후 단계의 툴 호출이나 후속 워크플로가 잘못된 방향으로 실행될 수 있습니다.

해결 방법

  • 시스템 프롬프트와 개발자 지침은 상수로 유지하고, 사용자 입력은 일반 메시지나 구조화된 데이터 필드로만 전달하세요.
  • 사용자 입력으로 정책 문자열을 만들지 않고, 서버가 소유한 상수 프롬프트 맵에서만 동작 모드를 선택하세요.
  • 외부 문서와 사용자 콘텐츠는 신뢰할 수 없는 데이터로 구분하세요. 문자열 정화나 낮은 권한의 메시지 배치만으로 프롬프트 인젝션을 막을 수 있다고 가정하지 마세요.
  • 도구 호출은 모델 외부에서 별도로 인가하고, 인수 스키마와 허용 목록, 최소 권한을 적용하세요.

예시

변경 전

javascript
const express = require("express");
const OpenAI = require("openai");
const app = express();
app.use(express.json());
const client = new OpenAI();

app.post("/bad1", async (req, res) => {
  await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      { role: "system", content: req.body.systemPrompt },
      { role: "user", content: "hello" }
    ]
  });
  res.send("ok");
});

변경 후

javascript
const express = require("express");
const OpenAI = require("openai");
const app = express();
app.use(express.json());
const client = new OpenAI();

app.post("/good", async (req, res) => {
  if (typeof req.body.message !== "string") {
    return res.status(400).send("bad message");
  }
  await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      { role: "system", content: "You are a careful support assistant." },
      { role: "user", content: req.body.message }
    ]
  });
  res.send("ok");
});

app.post("/good-mode", async (req, res) => {
  if (typeof req.body.message !== "string") {
    return res.status(400).send("bad message");
  }
  const SYSTEM_PROMPTS = {
    support: "You are a careful support assistant.",
    sales: "You are a careful sales assistant.",
  };
  const mode = String(req.body.mode || "support");
  if (!Object.hasOwn(SYSTEM_PROMPTS, mode)) {
    return res.status(400).send("bad mode");
  }

  await client.responses.create({
    model: "gpt-4o-mini",
    instructions: SYSTEM_PROMPTS[mode],
    input: req.body.message
  });
  res.send("ok");
});

설명:

  • 변경 전: 사용자 입력이 system 메시지 자리에 직접 들어가 모델 정책을 재정의할 수 있습니다.
  • 변경 후: 시스템 프롬프트를 상수 또는 서버가 소유한 상수 맵으로 유지하고, 사용자 입력이 문자열인지 검증한 뒤 일반 사용자 메시지나 Responses의 텍스트 input으로 전달합니다. 역할을 지정할 수 있는 메시지 배열은 받지 않습니다.

참조