ข้อมูลรั่วและการรักษา system prompt: injection→exfiltration chain
บท2–3 กางภัยให้เห็นครบ บท4–6 สร้างชั้น guard สามชั้นแรก — input guard (บท4), output guard/redactor (บท5), least-privilege + approval gate (บท6) บทนี้ปิดชั้นสุดท้ายก่อน capstone ด้วยการโฟกัสภัยที่เป็น ปลายทาง ของทุกอย่างที่ผ่านมา: ข้อมูลรั่วออกไปหาผู้โจมตี และภัยพี่น้องที่คนมักเข้าใจผิดว่าเป็นเรื่องเดียวกัน — การรั่วของ system prompt เอง
สองภัยนี้คือ OWASP LLM02 (Sensitive Information Disclosure) กับ LLM07 (System Prompt Leakage) แต่หัวใจของบทนี้คือมันไม่ได้อยู่แยกกัน — มันเป็น สายโซ่เดียว ที่ LLM01 (injection) กับ LLM06 (excessive agency) ป้อนเข้ามา นี่คือ lethal trifecta ที่ปิดวงจรจริงๆ
บทนี้ต่อยอด agent Order ตัวเดิม (พร้อม context layer จาก #16 และ resilient runtime จาก #17) — repo kaen-food-ordering (กำลังจัดทำ) บทนี้เป็นบท นโยบายเป็นหลัก บวกการนำ redactor/scoping ที่สร้างไว้แล้วใน 🔁 บท5 (output guardrails) และ 🔁 บท6 (excessive agency) มาวางลงบน สายโซ่ exfiltration โดยเฉพาะ ไม่มี tool หรือ store ใหม่ ทุกกติกา API ฝั่งแชตจาก #15 ยังยึดเดิมทุกข้อ
สองภัย สายโซ่เดียว
หัวข้อที่มีชื่อว่า “สองภัย สายโซ่เดียว”เริ่มที่ปลายทางก่อน Data ExfiltrationData Exfiltrationการลักลอบส่งข้อมูลออกนอกขอบเขต — ขา exfil ของ lethal trifecta และปลายทางของ injection→exfiltration chain: poisoned Order note (untrusted content) หลอก agent (private data) ให้ปล่อย PII หรือ order ของลูกค้าคนอื่นไปยัง channel ของผู้โจมตี รักษาด้วย: minimize sensitive data เข้า context (โยง #16 curation), redact PII ทั้งขาเข้าและขาออก, scope การเข้าถึงข้อมูลต่อ request, egress controlProcess คือการลักลอบส่งข้อมูลออกนอกขอบเขตที่ควรอยู่ — และมันคือ ขา exfil ของ lethal trifecta ที่ Willison ตั้งชื่อไว้ (S10) agent Order มีของให้รั่วเต็มมือ OWASP LLM02 นิยามขอบเขตของ “ข้อมูลอ่อนไหว” ไว้ตรงๆ (S4, verbatim):
“Sensitive information can affect both the LLM and its application context. This includes personal identifiable information (PII), financial details, health records, confidential business data, security credentials, and legal documents.”
getOrder / getDeliveryStatus คืนข้อมูลที่ตรงนิยามนี้ทุกช่อง — ที่อยู่ เบอร์โทร ยอดเงิน ประวัติออเดอร์ และเคสที่ OWASP ยกเป็นตัวอย่าง canonical นั้นคือความล้มเหลวเป๊ะๆ ที่ agent Order เสี่ยงจะทำ (S4, verbatim):
“A user receives a response containing another user’s personal data due to inadequate data sanitization.”
ลูกค้าคนหนึ่งได้ order ของลูกค้าอีกคน — นี่คือรูปแบบข้อมูลรั่วที่ agent multi-tenant ต้องกลัวที่สุด และ OWASP ชี้ชัดว่า injection คือ พาหะ ที่ลากไปสู่จุดนั้น (S4, verbatim):
“An attacker bypasses input filters to extract sensitive information.”
เอา claim เหล่านี้มาต่อกันเป็นสายโซ่ แล้วจะเห็นว่าทุกบทที่ผ่านมาป้อนเข้ามาที่นี่:
- untrusted content เข้า — poisoned Order note (indirect injection, บท2) ที่
getOrderซึ่งเป็น tool ที่เชื่อถือได้ คืนกลับมา - private data ถูกเข้าถึง — agent อ่านข้อมูลลูกค้าจริงเพื่อทำงาน
- ส่งออก — model ถูกหลอกให้ปล่อยข้อมูลนั้นไปยัง channel ที่ผู้โจมตีเห็น (ข้อความตอบ, log, outbound call)
นี่คือ injection→exfiltration chain — และมันคือ lethal trifecta ที่ ปิดวงจร พอดี Willison สรุปสายโซ่นี้ในประโยคเดียว (S10, verbatim):
“an attacker can easily trick it into accessing your private data and sending it to that attacker.”
thesis คมของ LLM07: การรั่วของ prompt เอง ไม่ใช่ ช่องโหว่
หัวข้อที่มีชื่อว่า “thesis คมของ LLM07: การรั่วของ prompt เอง ไม่ใช่ ช่องโหว่”ภัยพี่น้องที่คนสับสนบ่อยที่สุดคือ System Prompt LeakageSystem Prompt LeakageOWASP LLM07 — ความเสี่ยงที่ system prompt (คำสั่ง steer model) ถูกดึงออกมา ⚠️ thesis คม: การรั่วของ prompt เอง 'ไม่ใช่' ช่องโหว่ ช่องโหว่คือการใส่ 'secret' ไว้ใน prompt แล้วพึ่ง prompt เป็น security control — 'should not be considered a secret, nor should it be used as a security control' ฉะนั้นสมมติว่า prompt ถูกดึงออกได้เสมอ: ห้ามใส่ API key, refund threshold, ชื่อ DB, role table ใน prompt; บังคับ control 'นอก' model (จึงมี handler-side authz ใน บท6)Process — OWASP LLM07 นิยามว่า (S7, verbatim):
“The system prompt leakage vulnerability in LLMs refers to the risk that the system prompts or instructions used to steer the behavior of the model can also contain sensitive information that was not intended to be discovered.”
สัญชาตญาณแรกของหลายคนคือ “งั้นก็ต้องกัน system prompt ไม่ให้รั่ว” — และนั่นคือการเข้าใจผิดที่ OWASP ตั้งใจแก้ให้ตรงในหน้านี้เอง thesis ที่คมที่สุดของทั้งบทคือ (S7, verbatim):
“disclosure of the system prompt itself does not present the real risk — the security risk lies with the underlying elements…”
พูดอีกอย่าง: การที่ system prompt รั่วไม่ใช่ปัญหา ปัญหาคือคุณเก็บอะไรไว้ใน system prompt และคุณพึ่งมันทำหน้าที่อะไร OWASP วางกฎไว้เป็นสองประโยคที่ต้องแปะไว้เหนือโต๊ะ (S7, verbatim):
“The system prompt should not be considered a secret, nor should it be used as a security control.”
จากกฎนี้ออกมาเป็นข้อปฏิบัติสองข้อ ข้อแรก — อย่าเอา secret ไปฝากไว้ในนั้น (S7, verbatim):
“Avoid embedding any sensitive information (e.g. API keys, auth keys, database names, user roles, permission structure of the application) directly in the system prompts.”
ข้อสอง — อย่าใช้ system prompt เป็นตัวบังคับพฤติกรรมด้านความปลอดภัย ให้บังคับ นอก model (S7, verbatim):
“Avoid using system prompts to control the model behavior where possible. Instead, rely on systems outside of the LLM to ensure this behavior.”
ประโยคหลังนี้คือเหตุผลที่ 🔁 บท6 วาง authz ไว้ใน tool handler ไม่ใช่ในคำสั่งที่ป้อน model — เพราะถ้า refund threshold หรือ role table อยู่ใน prompt ผู้โจมตีก็แค่ดึง prompt ออกมาอ่านแล้วออกแบบ injection ให้เลี่ยงเงื่อนไขที่เห็น สมมติเสมอว่า system prompt ถูกดึงออกได้ แล้วออกแบบให้เรื่องนั้นไม่สำคัญ
ถ้าคุณทำตามบท6 มาแล้ว — IssueRefund handler เช็ค authz + amount cap + idempotency ใน code ไม่ใช่ใน model — คุณ ก็รักษา LLM07 ไปแล้วครึ่งหนึ่งโดยอัตโนมัติ เพราะ security control อยู่นอก prompt อยู่แล้ว บทนี้แค่ทำให้เจตนานั้นชัด: ทุก secret (credential, ชื่อ DB, threshold, permission structure) อยู่ใน handler + config ไม่เคย อยู่ใน ChatMessage(ChatRole.System, ...)
รักษา PII ทั้งขาเข้าและขาออก — และ scope ข้อมูลต่อ request
หัวข้อที่มีชื่อว่า “รักษา PII ทั้งขาเข้าและขาออก — และ scope ข้อมูลต่อ request”สายโซ่ exfiltration ตัดได้หลายจุด และหลักคือ อย่าพึ่งจุดเดียว — ตัดให้ได้มากจุดที่สุด คันโยกที่ต่อยอดจากบทก่อน:
คันโยกที่ 1 — scope การเข้าถึงข้อมูลต่อ request (ตัดที่ต้นทาง) นี่คือ complete mediation จากบท6 นำมาใช้กับ ข้อมูล โดยตรง OWASP LLM02 สั่งไว้ตรงๆ (S4, verbatim):
“Limit access to sensitive data based on the principle of least privilege”
getOrder handler ต้องบังคับว่า “caller คนนี้อ่านได้เฉพาะ order ของตัวเอง” — เช็คใน code ที่ data source ไม่ใช่ให้ model ตัดสิน ถ้าบังคับตรงนี้ได้ เคส “another user’s personal data” (S4) ก็ถูกตัดตั้งแต่ก่อนข้อมูลจะเข้า model ผู้โจมตีจะ inject เก่งแค่ไหน agent ก็ ไม่มี order ของคนอื่นอยู่ในมือให้รั่วตั้งแต่แรก และคู่กันคือ (S4, verbatim):
“Limit model access to external data sources.”
คันโยกที่ 2 — minimize + redact ขาเข้า (ตัดก่อนเข้า context) ก่อนข้อมูลอ่อนไหวจะไหลเข้า context อันไหนที่ model ไม่จำเป็นต้องเห็นเพื่อทำงาน ก็อย่าป้อน — sanitize/ตัดออกก่อนถึง model นี่โยงตรงกับ curation ของ 🔁 #16 (context engineering) แต่คนละเจตนา — จะพูดถึงความต่างนั้นในหัวข้อ anti-overlap
คันโยกที่ 3 — redact PII ขาออก (ตัดก่อนไหลออก) คือ output redactor จากบท5 พอดี — และ security corollary ของ invariant ฝั่งแชตยังยึดเต็ม: redactor ต้อง วนทั่ว Messages[].Contents[] ไม่ใช่แค่ convenience string .Text เพราะ PII/secret ซ่อนใน tool-call argument หรือ tool result ได้
using Microsoft.Extensions.AI;
// คันโยกที่ 3 — redact ขาออก: วนทั่ว Contents[] ทุก message (ไม่ใช่แค่ .Text)static void RedactOutbound(ChatResponse response){ foreach (var msg in response.Messages) for (int i = 0; i < msg.Contents.Count; i++) if (msg.Contents[i] is TextContent t) msg.Contents[i] = new TextContent(PiiRedactor.Scrub(t.Text)); // email, PAN, เบอร์, ที่อยู่, รูป API-key // สำคัญ: ตรวจ FunctionCallContent.Arguments / FunctionResultContent.Result ด้วย — // secret ซ่อนตรงนั้นได้ ไม่ได้อยู่แค่ TextContent (invariant #5 corollary)}คันโยกที่ 1 อยู่ใน handler ของ tool ไม่ใช่ใน guard wrapper — เพราะมันคือ authz ที่ data source แต่มันคือส่วนหนึ่งของ pipeline เดียวกัน: scope ที่ต้นทาง, redact ที่ปลายทาง, ไม่มี secret ใน prompt ตรงกลาง
// คันโยกที่ 1 — scope ต่อ request: handler ตัดสิน authz ไม่ใช่ model (complete mediation)async Task<OrderView> IssueGetOrder(string orderId, CallerContext caller){ var order = await _orders.FindAsync(orderId); if (order is null || order.CustomerId != caller.CustomerId) throw new NotAuthorizedToReadOrder(orderId); // caller เห็นเฉพาะ order ของตัวเอง return OrderView.Of(order); // ไม่ป้อน field ที่ model ไม่ต้องใช้ (minimize ขาเข้า)}จับสังเกตว่าคันโยกที่ 1 บังคับ ก่อน คำสั่ง injection จะมีความหมายอะไรเลย — นั่นคือทำไม least privilege บนข้อมูลถึงตัดสายโซ่ได้แข็งกว่าการมาไล่ redact ทีหลัง: ปลายทางจับได้เฉพาะ pattern ที่ enumerate ไว้ แต่ต้นทางตัด ทั้งชุดข้อมูล ที่ไม่ควรอยู่ในมือ agent ออกไปเลย
คันโยกที่ 4 — egress control (คุมปลายทางที่ส่งออก) ต่อให้ redact ขาออกแล้ว ก็ยังต้องคุม ช่องทาง ที่ agent ส่งข้อมูลออกได้ — จำกัดปลายทาง outbound ให้เหลือ allowlist เท่าที่จำเป็น ไม่เปิด tool ส่งออกอิสระ (เช่น httpGet ไปที่ไหนก็ได้) นี่คือขา external comms ของ lethal trifecta: ถ้าตัดท่อส่งออกได้ ต่อให้ payload หลอก agent ให้ อยาก ปล่อยข้อมูล ก็ไม่มีช่องให้ไหลไปถึงผู้โจมตี — จุดตัดที่สี่ (D4) บน diagram ด้านล่าง เสริมสามคันโยกก่อนหน้าให้ครบทุกด้านของสายโซ่
NIST AI 600-1 (Generative AI Profile, 2024-07-26, S19) จัดหมวดความเสี่ยงไว้สองหมวดที่ตรงกับบทนี้ — Data Privacy (การรั่ว/เปิดเผยโดยไม่ได้รับอนุญาต/de-anonymization ของ PII และข้อมูลอ่อนไหว) และ Information Security (รวม prompt injection ทั้ง direct + indirect) — และวางงานความเสี่ยงเป็นวงจร GOVERN/MAP/MEASURE/MANAGE นำเสนอแบบเรียบเรียง ไม่ใช่ verbatim (ยังไม่ได้ยืนยัน string §2 ตรงตัวจากต้นฉบับ) มาตรฐานบอกว่าต้องกลัวอะไรและเรียกมันว่าอะไร — code ที่ scope + redact ต่างหากที่ลดความเสี่ยงจริง
แผนภาพ: injection→exfiltration chain และจุดที่ defense ตัดสายโซ่
หัวข้อที่มีชื่อว่า “แผนภาพ: injection→exfiltration chain และจุดที่ defense ตัดสายโซ่”flowchart TD
ADV["ผู้โจมตี"]
ADV -->|"ฝัง payload"| NOTE["poisoned note<br/>ใน getOrder data<br/>(untrusted content)"]
NOTE --> READ["agent อ่าน private data<br/>order/PII ลูกค้า"]
READ --> EMIT["agent ปล่อยข้อมูล<br/>ไป channel ผู้โจมตี"]
EMIT --> LEAK["ข้อมูลรั่ว (LLM02)"]
subgraph CUTS["จุดที่ defense ตัดสายโซ่ — ตัดหลายจุด อย่าพึ่งจุดเดียว"]
D1["1 · scope ข้อมูลต่อ request<br/>getOrder เห็นเฉพาะ order ของ caller"]
D2["2 · minimize + redact ขาเข้า<br/>โยง #16 curation"]
D3["3 · redact PII ขาออก<br/>วนทั่ว Contents[]"]
D4["4 · egress control<br/>คุมปลายทางที่ส่งออก"]
end
D1 -. ตัดที่ .-> READ
D2 -. ตัดที่ .-> NOTE
D3 -. ตัดที่ .-> EMIT
D4 -. ตัดที่ .-> EMIT
PROMPT["system prompt hygiene<br/>ไม่มี secret ใน prompt (LLM07)"]
PROMPT -. รักษาแยกสาย .-> READ
classDef danger fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
classDef guard fill:#475569,stroke:#1e293b,color:#f8fafc;
class ADV,NOTE,READ,EMIT,LEAK danger;
class D1,D2,D3,D4,PROMPT guard;
คำบรรยายภาพ: สายสีแดงคือ injection→exfiltration chain — ผู้โจมตีฝัง payload ใน data ที่ tool เชื่อถือได้คืนมา agent อ่าน private data แล้วถูกหลอกให้ปล่อยออกไปหาผู้โจมตี (LLM02) สี่กล่องสีเทาคือจุดที่ defense ตัดสายโซ่ได้: scope ข้อมูลต่อ request (ตัดที่ต้นทาง แข็งที่สุด), redact ขาเข้า, redact ขาออก, egress control — บวก system prompt hygiene ที่รักษาแยกสาย (LLM07) ไม่มีจุดตัดใดจุดเดียวสมบูรณ์ ตัดหลายจุดจึงหด blast radius ได้
บทนี้ ไม่ทับ บทไหน (anti-overlap)
หัวข้อที่มีชื่อว่า “บทนี้ ไม่ทับ บทไหน (anti-overlap)”- vs #16 (context engineering) — #16 curate context เพื่อ คุณภาพ — ป้อนอะไรให้ model ตอบดีที่สุดในงบ token #18 minimize ข้อมูลอ่อนไหวใน context เพื่อ ความลับ (confidentiality) และปฏิบัติต่อข้อมูลที่ retrieve มาเป็น ศัตรู คนละเจตนาบนคันโยกเดียวกัน — curation ที่ดีของ #16 ช่วยงานนี้ แต่ไม่ได้ตั้งใจทำเพื่อความปลอดภัย
- vs #14 (designing MCP servers) — #14 รักษา auth ที่ตัว tool-server / protocol MCP บทนี้คือ agent-level data-leak defense ที่ต้องยืนได้ แม้ tool/server จะเชื่อถือได้เต็มร้อย — poisoned note มากับ data ที่ tool สะอาดคืนมา ไม่ใช่ช่องโหว่ของ server
- vs #17 (robustness) — #17 กัน reply ที่ verbose เกินไปโดยบังเอิญ หรือ tool result ที่เพี้ยน #18 กัน exfiltration ที่ ตั้งใจ — คำตอบยาวผิดปกติในบทนี้อาจไม่ใช่ bug แต่เป็นผู้โจมตีกำลังดูดข้อมูลออก threat model ตรงข้าม รูป guard เดียวกัน
สรุปก่อนไปต่อ
หัวข้อที่มีชื่อว่า “สรุปก่อนไปต่อ”LLM02 กับ LLM07 ไม่ใช่สองเรื่องแยกกัน — เป็นสายโซ่เดียว injection (บท2) หลอก agent (private data) ให้ exfiltrate PII หรือ order ของลูกค้าคนอื่นออกไปหาผู้โจมตี thesis คมของ LLM07 คือ การรั่วของ system prompt เองไม่ใช่ช่องโหว่ — ช่องโหว่คือการเก็บ secret ไว้ในนั้นหรือพึ่งมันเป็น security control ฉะนั้นสมมติว่า prompt ถูกดึงออกได้เสมอ แล้วบังคับ control นอก model ส่วนสายโซ่ exfiltration ตัดได้หลายจุด: scope ข้อมูลต่อ request (แข็งที่สุด เพราะตัดที่ต้นทาง), redact ทั้งขาเข้าและขาออก, egress control — ตัดหลายจุด อย่าพึ่งจุดเดียว บทหน้าเราจะประกอบทั้งสี่ชั้น guard เข้าเป็น pipeline เดียวใน capstone แล้วโชว์ residual attack ที่ยังลอด detection ได้ — แต่ถูก กัน ไว้
ความปลอดภัยคือกระบวนการ ไม่ใช่ checkbox — คุณทำให้ system prompt กันรั่วสนิท ไม่ได้ และทำให้ redaction สมบูรณ์แบบ ก็ไม่ได้ Willison พูดตรงว่า “we still don’t know how to 100% reliably prevent this from happening” (S10) ทางออกจึงไม่ใช่พึ่งชั้นใดชั้นหนึ่ง แต่คือ อย่าพึ่งชั้นเดียว: ไม่มี secret ใน prompt, authz นอก model, least privilege บนข้อมูล, redaction เป็นหนึ่งชั้น ลดและกัน ไม่ใช่กำจัด
บทนี้อิงต้นทางที่ลงวันที่กำกับ อ่านต่อได้โดยตรง:
- OWASP, “LLM07:2025 System Prompt Leakage” (2025, หมวดใหม่) — thesis คม “disclosure of the system prompt itself does not present the real risk”, “The system prompt should not be considered a secret, nor should it be used as a security control.” และกฎ “อย่าฝัง secret ใน prompt / บังคับ control นอก model”
- OWASP, “LLM02:2025 Sensitive Information Disclosure” (2025) — ขอบเขต PII/ข้อมูลอ่อนไหว, เคส canonical “another user’s personal data due to inadequate data sanitization”, “An attacker bypasses input filters to extract sensitive information.” และ least-privilege บนข้อมูล
- Simon Willison, “The lethal trifecta for AI agents” (2025-06-16) — สายโซ่ exfiltration “an attacker can easily trick it into accessing your private data and sending it to that attacker” และ “we still don’t know how to 100% reliably prevent this from happening.”
- NIST, “AI 600-1, Generative AI Profile” (2024-07-26) — หมวด Data Privacy + Information Security และวงจร GOVERN/MAP/MEASURE/MANAGE (นำเสนอแบบเรียบเรียง)
เช็กความเข้าใจ — บทที่ 7
ข้อ 1 / 3thesis คมของ OWASP LLM07 (System Prompt Leakage) คืออะไร?