Security Blog

คุณแบ่งงานให้เอเจนต์สิบสามตัว แต่ Paperclip ไม่ได้แบ่งกุญแจ

#376

October 2, 2026 · By Marketing team

← All posts

การสแกนเฟรมเวิร์กเอเจนต์ที่มีสตาร์ 71,000 ดวง พบว่าเอเจนต์สิบสองในสิบสามตัวพก token เดียวกันแบบ plaintext เมื่อคุณรันเอเจนต์เป็นฝูง "ความลับอยู่ใน config" จะหยุดเป็นทางลัด และกลายเป็นตัวคูณ

คุณทำแบบสมัยใหม่ แทนที่จะมีเอเจนต์ใหญ่ตัวเดียว คุณตั้งฝูงเอเจนต์ขึ้นมา — ตัวหนึ่งคัดแยก ticket อีกตัวเขียน copy อีกตัวรันท่อส่งงานออกแบบ รวมกันสิบกว่าตัว แต่ละตัวมีงานของตัวเองและ config ของตัวเอง แบบนี้รู้สึกปลอดภัยกว่า รัศมีความเสียหายเล็กลง หลักการให้สิทธิ์ขั้นต่ำมากขึ้น

แล้วการสแกนความปลอดภัยก็ไล่ดู config ของเฟรมเวิร์กเอเจนต์ชื่อ Paperclip ที่มีสตาร์ 71,000 ดวง และพบว่าเอเจนต์สิบสองในสิบสามตัวพกข้อมูลรับรองชุดเดียวกัน — token ตัวเดียวกันเป๊ะ วางแบบ plaintext ลงใน config ของเอเจนต์แต่ละตัว [1] Anthropic API key ตัวหนึ่งถูก hardcode ไว้ใน config ของเอเจนต์ออกแบบอย่างเปิดเผย Bot token ที่ควรจะใช้กับเอเจนต์ตัวหนึ่ง กลับอ่านได้จากเอเจนต์ที่ไม่เกี่ยวข้องกับมัน

ประตูสิบสามบาน กุญแจดอกเดียว คัดลอกไปไว้ในสิบสองบาน ขโมยจากเอเจนต์ที่อ่อนแอที่สุดตัวเดียว ก็ได้ทั้งอีกสิบเอ็ดตัว

เกิดอะไรขึ้นจริง

Paperclip ให้ MCP server config กับเอเจนต์แต่ละตัว — ไฟล์ที่บอกเอเจนต์ว่าเข้าถึงเครื่องมือและบริการอะไรได้บ้าง และยืนยันตัวตนกับสิ่งเหล่านั้นอย่างไร ระหว่างทาง ความลับถูกใส่ลงในไฟล์เหล่านั้นตรง ๆ เป็นข้อความจริง: n8n JWT, bearer token, Anthropic API key ไม่ใช่การอ้างอิง ไม่ใช่การ inject ตอน runtime แต่พิมพ์เข้าไป — แล้วพอการตั้งเอเจนต์ตัวถัดไปเป็นเรื่องของ copy-paste มันก็ถูกทำซ้ำไปทั้งฝูง

การสแกนตั้งสามเรื่องไว้: plaintext token ที่ใช้ร่วมกันในเอเจนต์สิบสองตัว (ระดับ HIGH) Anthropic key ที่วางเปิดเผยใน config ของเอเจนต์ออกแบบ (ระดับ CRITICAL) และ bot token ที่ให้ขอบเขตไว้กับเอเจนต์ซึ่งไม่ได้ตั้งใจให้ใช้ — เป็นการพลาดหลักการให้สิทธิ์ขั้นต่ำชัด ๆ [1] ต้องชื่นชมทีม Paperclip ที่ขยับเร็ว: ย้ายไปใช้ credential reference, redacted config ตอนอ่านข้ามเอเจนต์, และเริ่มบังคับ binding sync [2] ไปในทิศทางที่ถูกต้อง

นี่ไม่ใช่ความประมาทของ Paperclip

ตรงนี้แหละที่ควรหยุดคิด Paperclip ทำสิ่งที่เฟรมเวิร์กแทบทุกตัวทำ การใส่ความลับลงในไฟล์ config เป็นวิธีที่ซอฟต์แวร์ยืนยันตัวตนมาสามสิบปี มันใช้ได้เพราะมีแอปเดียว config เดียว ผู้ดูแลคนเดียวที่รู้ว่ากุญแจอยู่ตรงไหน

แต่ยุคสมัยเปลี่ยนไปใต้นิสัยนั้น ระบบ multi-agent ไม่ใช่แอปเดียวที่มี config เดียว — มันคือกระบวนการสิบกว่าตัว แต่ละตัวมีไฟล์ของตัวเอง แต่ละไฟล์เป็นสำเนาของไฟล์ก่อนหน้า plaintext ใน config เป็นทางลัดที่พอรับได้เมื่อมีจุดรั่วจุดเดียว แต่พอเป็นสิบสาม ทางลัดเดิมหมายความว่ารั่วจุดเดียวเท่ากับรั่วสิบสามจุด — และคำถามว่า "เอเจนต์ตัวไหนทำ?" จะไม่มีคำตอบ เพราะ token ใน log เป็นของทุกตัว

credential reference — ที่ Paperpatch... ที่ Paperclip ส่งเป็นวิธีแก้ — ดีขึ้นจริง แต่ให้ดูว่ามันเปลี่ยนอะไรและไม่เปลี่ยนอะไร reference ยังแปลงเป็นความลับจริงในที่ที่เอเจนต์รันอยู่ เอเจนต์ หรืออะไรก็ตามที่เจาะเข้าไป ยังอ่านค่าที่แปลงแล้วได้ และ bug tracker ของเฟรมเวิร์กเองก็แสดงโหมดความล้มเหลวถัดไปแล้ว: reference หลุดซิงก์กับ binding ของมัน จน config ดูเหมือน มีค่าครบ ขณะที่ validation ล้มเหลวเงียบ ๆ [3] ความลับแค่ขยับถอยไปอีกชั้น ไม่ได้ออกจากตัวตึก

ไม่ใช่แค่ Paperclip

สัปดาห์เดียวกัน root cause เดียวกัน แต่คนละ repo เอเจนต์เขียนโค้ดที่ใช้กันแพร่หลายตัวหนึ่งถูกรายงานว่าพิมพ์ค่า .env ดิบ — รหัสผ่าน, token, API key — ลงใน output ของแชตตรง ๆ Agent runner อีกตัวถูกพบว่าส่ง environment ทั้งหมดของ parent ไปให้ subprocess ทำให้ provider key ทุกตัวมองเห็นได้จาก child process [4] Voice hook ตัวหนึ่งเขียน transcript พร้อม credential ทั้งหมดลงใน /tmp ที่ world-readable [5] ทีมคนละทีม threat model คนละชุด แต่มีสมมติฐานเดียวกัน: ว่าความลับอยู่ในที่ที่เอเจนต์มองเห็นได้เป็นเรื่องปกติ ทั้งหมดที่ผู้โจมตีต้องแย้งคือว่ามันไม่ปกติ

ออกแบบมาเพื่อเรื่องนี้โดยเฉพาะ

Clavitor ตั้งต้นจากสมมติฐานฝั่งตรงข้าม: เอเจนต์ไม่เคยถือ credential เลย มันขอดำเนินการการขอนั้นถูกดักไว้ ยืนยันตัวตนเทียบกับความลับที่เอเจนต์อ่านไม่ได้ แล้วจึงดำเนินการให้ ไม่มี config ให้วาง token เพราะไม่มี token ใน config ไม่มีอะไรให้คัดลอกไปทั้งสิบสามตัว เพราะ environment ของเอเจนต์ไม่เคยถือของที่คุ้มค่าต่อการขโมย

เอเจนต์แต่ละตัวเข้าถึงเฉพาะสิ่งที่ระบุชื่อไว้เท่านั้น — ไม่ใช่ทั้งคลัง — จึงเป็นไปไม่ได้ที่ bot token จะไปลงเอยในเอเจนต์ที่ไม่เคยร้องขอ และทุกการกระทำถูกบันทึกลงผู้กระทำรายนั้น ๆ ไม่ใช่ token ที่เอเจนต์สิบสองตัวใช้ร่วมกัน — จึงตอบได้ว่า "ตัวไหนทำ?"

ข้อจำกัดที่ต้องบอกตรง ๆ: สิ่งนี้ไม่ได้ทำให้เอเจนต์เจาะไม่เข้า เอเจนต์ที่ถูกเจาะยังทำในขณะนั้นได้ในสิ่งที่มีสิทธิ์ทำ สิ่งที่ทำไม่ได้คือหอบกุญแจไปแล้วกลายเป็นอีกสิบสองตัว — เพราะไม่มีกุญแจในมือให้หอบไป

บทเรียนไม่ใช่ "หมุนเวียน token"

Paperclip จะหมุนเวียน token, ย้ายระบบให้เสร็จ, และปิด issue ดี — ควรทำ แต่การหมุนเวียนไม่ใช่บทเรียน บทเรียนคือทันทีที่คุณมีฝูงเอเจนต์แทนแอปเดียว "ความลับอยู่ใน config" จะหยุดเป็นทางลัดและกลายเป็นตัวคูณ คุณแก้ตัวคูณด้วยการทำให้ความลับอ่านยากขึ้นนิดหน่อยไม่ได้ คุณแก้ด้วยการตรวจสอบให้แน่ใจว่าความลับไม่เคยอยู่ในมือเอเจนต์ตั้งแต่แรก

เราเขียนกฎที่เราคิดว่าเครื่องมือจัดการ credential ควรรักษาไว้ในยุคเอเจนต์ — รวมถึงว่าความลับไม่เคยอยู่ในที่ที่โค้ดรันอยู่ และเอเจนต์เข้าถึงเฉพาะสิ่งที่ระบุชื่อไว้ ลองเอาของคุณไปไล่เทียบดู: clavitor.ai/rules

Clavitor (@clavitorai) คือ credential vault ที่สร้างเพื่อเอเจนต์ AI และเพื่อรับมือกับเอเจนต์ AI clavitor.ai

แหล่งอ้างอิง

[1] Paperclip agent framework — ผลการตรวจสุขอนามัยข้อมูลรับรอง (CFG-H1 shared plaintext tokens, CFG-C1 hardcoded Anthropic key, CFG-H2 mis-scoped bot token): https://github.com/paperclipai/paperclip

[2] Paperclip — enforce agent secret-binding sync across lifecycle flows (merged): https://github.com/paperclipai/paperclip/pull/8307

[3] Paperclip — รายการ env secret_ref อาจหลุดซิงก์กับแถว secret_bindings ทำให้ config ดูมีค่าครบ แต่ validation ล้มเหลวเงียบ ๆ (#8309): https://github.com/paperclipai/paperclip/issues/8309

[4] Chetter — runBatchAgent สืบทอด runner environment ทั้งหมด ทำให้ provider API key รั่วไปยัง subprocess (#56): https://github.com/flatout-works/chetter/issues/56

[5] Claude Code voice hook — transcript ทั้งหมด (รวม credential) ถูกเขียนลง /tmp ที่ world-readable (#58): https://github.com/rodlaneedu-hash/claude-code-voice-hook/issues/58