ข้ามไปเนื้อหาหลัก

สร้างแอป Shopify ฝังตัวด้วย Symfony: คู่มือเจาะลึกความปลอดภัยและ Token Exchange ใน PHP

เจาะลึกวิธีการพัฒนาแอปพลิเคชัน Shopify แบบ Embedded บน Symfony 7.4 และ PHP 8.5 ตั้งแต่การจัดการ Session Token, Token Exchange ไปจนถึงส่วนที่เอกสารทางการไม่ค่อยพูดถึง

เรียบเรียงโดย AI
Inewgen
03 Aug 2026ที่มา: Dev.to5 นาทีอ่าน (0 ครั้ง)อัปเดตล่าสุด 04 Aug 2026
แชร์
สร้างแอป Shopify ฝังตัวด้วย Symfony: คู่มือเจาะลึกความปลอดภัยและ Token Exchange ใน PHP

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง

ขนาดตัวอักษร
  • นักพัฒนาที่ใช้ภาษาอื่นนอกเหนือจาก Node ต้องเผชิญความท้าทายในการจัดการแอปฝังตัวใน iframe
  • แอป Shopify ฝังตัวไม่สามารถใช้ PHP session ได้ จึงต้องพึ่งพา Session Token แบบ JWT ที่มีอายุเพียง 1 นาที
  • การแลก Token ใช้มาตรฐาน OAuth 2.0 token exchange (RFC 8693) แทนการทำ authorization-code แบบเดิม
  • บทความนี้นำเสนอโค้ดจริงจากการใช้งานจริงในแอป StockPilot บน Symfony 7.4 และ PHP 8.5

เอกสารทางการของ Shopify มักจะเน้นเส้นทางหลักด้วย Node, Official CLI และเทมเพลต Remix เป็นหลัก ทำให้นักพัฒนาที่เลือกใช้ภาษาอื่นต้องเผชิญกับความท้าทายตั้งแต่ก้าวแรก เนื่องจากแอปพลิเคชันของคุณจะรันอยู่ใน iframe ภายในหน้าผู้ดูแลระบบของ Shopify ซึ่งเบราว์เซอร์จะไม่ยอมส่งคุกกี้ข้ามเว็บไซต์ให้ และ Token ที่จำเป็นต่อการเรียกใช้งาน Admin API ก็จะต้องถูกนำมาแลกเปลี่ยนกันแบบสด ๆ

เรื่องเหล่านี้ไม่ได้ยากเกินความสามารถ แต่มันแค่ขาดเอกสารอธิบายสำหรับนอกเหนือจากฝั่ง JavaScript เท่านั้น บทความนี้จึงรวบรวมเส้นทางด้านความปลอดภัยและการยืนยันตัวตนฉบับสมบูรณ์สำหรับการพัฒนาแอปพลิเคชัน Shopify แบบฝังตัวด้วย Symfony 7.4 และ PHP 8.5 ซึ่งนำมาจาก StockPilot แอปพลิเคชันจริงที่ผ่านการตรวจสอบบน Shopify App Store และเปิดให้บริการอยู่ในปัจจุบัน โดยทุกชุดโค้ดด้านล่างนี้คือโค้ดสำหรับใช้งานจริง ไม่ใช่เพียงโค้ดจำลอง

แอปพลิเคชันแบบฝังตัว (Embedded app) คือหน้าเว็บที่แสดงผลอยู่ภายใน admin.shopify.com ผ่าน iframe แบบ cross-origin ในบริบทนี้ Third-party cookies ไม่สามารถใช้งานได้อีกต่อไป ส่งผลให้ PHP session ไม่สามารถใช้งานได้และไม่มีวันใช้งานได้ คำตอบของ Shopify คือ JWT ระยะสั้นที่เรียกว่า Session Token ซึ่งสร้างโดย App Bridge (ไลบรารี JavaScript ของ Shopify ที่โหลดจาก CDN) ในทุก ๆ คำขอ โดยโทเค็นนี้จะมีอายุการใช้งานเพียง 1 นาทีและถูกแนบไปกับทุกคำขอ fetch() ภายในเว็บไซต์

สถาปัตยกรรมนี้บังคับให้เกิดการแบ่งส่วนที่นักพัฒนา PHP มักจะไม่ค่อยทำโดยปริยาย:

  • ส่วนเชลล์ (Shell) และหน้าต่างผู้ดูแลระบบ: ต้องให้บริการอย่างรวดเร็ว, มีการแคชได้ง่าย และไม่ต้องใช้การยืนยันตัวตนในฝั่งเซิร์ฟเวอร์
  • ส่วนคำขอ API ของแอป: เป็นส่วนที่ stateless ทั้งหมด และต้องทำการตรวจสอบความถูกต้องของ Session Token ในทุก ๆ คำขอที่เข้ามา

การแบ่งส่วนนี้ช่วยพลิกสัญชาตญาณดั้งเดิมของ Symfony ที่มักจะป้องกันตัวคอนโทรลเลอร์และเรนเดอร์ข้อมูลจากฝั่งเซิร์ฟเวอร์ โดยในกรณีนี้ การเรนเดอร์ข้อมูลลงในเชลล์จะหมายถึงการยืนยันตัวตนในขณะโหลดหน้าที่ไม่มีข้อมูลประจำตัวใด ๆ ส่งผลให้เชลล์สามารถแคชได้อย่างง่ายดายและมีขอบเขตความปลอดภัยที่ชัดเจนผ่านไฟร์วอลล์เพียงชุดเดียว

การกำหนดค่าใน config/packages/security.yaml จะมีรูปแบบดังนี้:

  • webhooks: ตรวจสอบความถูกต้องผ่านลายเซ็น HMAC ภายในตัวคอนโทรลเลอร์ แทนที่จะใช้ไฟร์วอลล์
  • api: สำหรับแอปพลิเคชัน Admin API ที่ฝังตัว ใช้ App Bridge session token บนทุกคำขอแบบ stateless
  • main: ตั้งค่าเป็น lazy และใช้ผู้ให้บริการข้อมูลผู้ใช้งานในหน่วยความจำ

Session Token คือ JWT ที่ลงนามด้วยอัลกอริทึม HS256 โดยใช้ Client Secret ของแอปพลิเคชัน การตรวจสอบลายเซ็นเป็นขั้นตอนที่ทุกคนทำกัน แต่การตรวจสอบ Claim 4 ส่วนหลังจากนั้นมักจะถูกละเลย ซึ่งแต่ละส่วนช่วยปิดช่องโหว่สำคัญ ได้แก่ การตรวจสอบ aud เพื่อป้องกันการยอมรับโทเค็นจากแอปอื่น และการตรวจสอบ iss กับ dest เพื่อป้องกันโทเค็นที่ระบุร้านค้าไม่ตรงกัน

ไม่อยากพลาดข่าวใหม่?

สมัครรับสรุปข่าวสารใหม่ทางอีเมล ไม่บ่อยจนรำคาญ

โฆษณา

การทำความเข้าใจสถาปัตยกรรมความปลอดภัยของ Shopify บนเฟรมเวิร์กอย่าง Symfony ถือเป็นกุญแจสำคัญในการสร้างแอปพลิเคชันที่ผ่านมาตรฐานความปลอดภัยระดับสูงของ App Store การที่ Shopify บังคับใช้ stateless API และการตรวจสอบ JWT อย่างเข้มงวดช่วยป้องกันช่องโหว่ประเภท CSRF และการโจมตีแบบ cross-origin ได้อย่างมีประสิทธิภาพ ซึ่งนักพัฒนา PHP จำเป็นต้องปรับเปลี่ยนแนวคิดจากการใช้เซสชันแบบเดิมมาเป็นการจัดการโทเค็นระยะสั้นแทน

Session Token เป็นเพียงเครื่องยืนยันตัวตนว่าใครกำลังร้องขอข้อมูล แต่ยังไม่เพียงพอต่อการเรียกใช้งาน Admin API โดยคุณจะต้องนำมันไปแลกเปลี่ยนเป็น Access Token ผ่านมาตรฐาน OAuth 2.0 token exchange (RFC 8693) แทนการใช้งาน authorization-code แบบเดิมที่มีการเปลี่ยนเส้นทาง (Redirect) ด้วยระบบติดตั้งแบบจัดการเองนี้ จะทำให้ขั้นตอนการติดตั้งทั้งหมดไม่มีเส้นทาง /auth, ไม่มีหน้าเปลี่ยนเส้นทาง และไม่มี callback โดยคำขอแรกที่ผ่านการยืนยันตัวตนจากร้านค้าใหม่จะทำการแลกเปลี่ยนโทเค็นทันที

ในส่วนของ Webhooks จะไม่มีการแนบ Session Token มาด้วย แต่จะใช้การลงนามแบบ HMAC-SHA256 บนเนื้อหาคำขอแบบดิบ (Raw request body) และเข้ารหัส base64 ไว้ในเฮดเดอร์ X-Shopify-Hmac-Sha256 โดยคำว่าดิบหมายถึงข้อมูลก่อนผ่านการแปลงเป็น JSON หรือมิดเดิลแวร์ใด ๆ ทั้งสิ้น ซึ่งการตรวจสอบในส่วนนี้สำคัญมากเพราะการตรวจสอบอัตโนมัติของ Shopify App Store จะส่งเว็บฮุกที่จงใจใส่ลายเซ็นผิดมาทดสอบและต้องการผลลัพธ์เป็นรหัส 401

นอกจากนี้ยังมีข้อกำหนดด้าน Content Security Policy (CSP) ในส่วนของ frame-ancestors ที่ต้องถูกคำนวณแบบรายคำขอจากพารามิเตอร์ของร้านค้า และข้อกำหนดที่สำคัญอีกข้อที่มักมองข้ามในเลย์アウトแบบ Twig คือ App Bridge จะต้องเป็นสคริปต์แรกในแท็ก <head> ที่โหลดแบบ synchronous จาก CDN ของ Shopify โดยห้ามทำการมัดรวม (bundle) หรือเลื่อนการโหลด (defer) เด็ดขาด

บทความต้นฉบับยังได้ยกตัวอย่างปัญหาชวนปวดหัวในระบบแปลภาษาของ Symfony เมื่อข้อความหัวอีเมลอย่างเช่น digest.attachment : ' Attached: your restock list (%count% items).' ถูกตัดคำสองคำแรกออกอย่างไร้ร่องรอยในระบบจริง เนื่องจาก %count% เป็นตัวเลข ทำให้ข้อความถูกส่งผ่านลอจิกการทำพหูพจน์ของ Symfony และตรงกับเงื่อนไขการตรวจจับข้อความที่มีเครื่องหมายโคลอน ทำให้คำนำหน้าถูกกลืนหายไปโดยไม่มีการบันทึก Log หรือแจ้งข้อผิดพลาดใด ๆ บทเรียนสำคัญจากเหตุการณ์นี้คือ อย่าให้ข้อความที่มีตัวแปร %count% ขึ้นต้นด้วยคำที่มีเครื่องหมายโคลอนตามหลัง และควรเขียนชุดทดสอบที่ตรวจสอบสตริงตั้งแต่ตัวอักษรแรกเสมอ

ที่มา: Dev.to

ความคิดเห็น

แสดงความคิดเห็น
0/2000

พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้