• security
  • oauth
  • PKCE

การทบทวนความปลอดภัยของ OAuth อย่างสั้น ๆ

คุณมีความรู้ด้านมาตรการป้องกันที่ใช้งานโดย OAuth มากน้อยแค่ไหน? ระบบของคุณปฏิบัติตามมาตรฐานเปิดของ OAuth หรือไม่? คุณตระหนักถึงความเสี่ยงที่อาจเกิดขึ้นในระหว่างขั้นตอนการยืนยันตัวตนของผู้ใช้หรือไม่? มาทบทวนสิ่งที่เราได้เรียนรู้เกี่ยวกับ OAuth กันอย่างสั้น ๆ

Simeng
Simeng
Developer

บทนำ

เมื่อไม่กี่วันที่ผ่านมา มีบทความเกี่ยวกับช่องโหว่ของ OAuth ที่น่าสนใจออกมา ช่องโหว่ OAuth ใหม่ที่อาจกระทบต่อบริการออนไลน์หลายร้อยรายการ โดย SALT Lab โพสต์นี้เฉพาะเจาะจงเน้นถึงช่องโหว่ที่ค้นพบใน Expo, เฟรมเวิร์กที่ใช้กันอย่างแพร่หลายสำหรับการใช้งาน OAuth และฟังก์ชันอื่น ๆ โดยเฉพาะเนื้อหาเรื่องช่องโหว่ในห้องสมุด expo-auth-session ซึ่งได้รับการจัดการและแก้ไขแล้ว

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

มันทำให้ฉันนึกถึงช่วงเวลาที่พวกเราเพิ่งเริ่มต้น เราใช้เวลาเรียนรู้และศึกษารายละเอียดของโปรโตคอล OAuth และ OIDC เป็นอย่างมาก มันทั้งเจ็บปวดและน่าเบื่อหน่าย แต่มันก็คุ้มค่ามาก ๆ ถึงแม้บางท่านในทีมของเราอาจจะไม่เชี่ยวชาญในเรื่องของ OAuth แต่ทุกคนก็มุ่งมั่นที่จะพยายามรักษาความปลอดภัยและความระมัดระวังต่อไป ด้วยความพยายามเหล่านั้น ผลิตภัณฑ์ Logto ก็ได้พัฒนาเป็นอย่างมากในวันนี้

ขอบคุณโอกาสดี ๆ นี้ เราจึงอยากจะทบทวนรายละเอียดบางส่วนเกี่ยวกับความปลอดภัยของ OAuth ที่นี่

แอบดูที่ OAuth Authorization Code Flow

OAuth 2.0 มี flows การอนุญาตหลายแบบที่รองรับประเภทไคลเอ็นต์และความต้องการที่หลากหลาย ซึ่งรวมถึง Implicit Flow, Client Credentials Flow, Resource Owner Password Credentials Flow, และ Device Authorization Flow อย่างไรก็ตาม Authorization Code Flow โดดเด่นในฐานะที่ปลอดภัยที่สุดและใช้งานอย่างแพร่หลายที่สุด ซึ่งแตกต่างจาก flow อื่น ๆ, มันแยกการยืนยันตัวของผู้ใช้ออกจากแอปพลิเคชันลูกค้าและเกี่ยวข้องกับการแลกเปลี่ยน Authorization Code สำหรับ tokens วิธีนี้ให้ชั้นความปลอดภัยเพิ่มเติม เพราะ tokens ที่มีความละเอียดอ่อนจะไม่ถูกเปิดเผยต่อไคลเอ็นต์ นอกจากนี้, Authorization Code Flow ยังรองรับการจัดการ token ด้านฝั่งเซิร์ฟเวอร์, ทำให้มันเหมาะสำหรับเว็บแอปพลิเคชันที่ต้องการความปลอดภัยที่แข็งแกร่งและการควบคุมทั่วไปในการเข้าถึงของผู้ใช้ที่พัฒนาขึ้น

นี่คือแผนผังของ Authorization Code Flow ที่ง่ายที่สุด:

มาดูกันที่คำขอที่สำคัญที่สุดสองคำขอที่ใช้ใน Authorization Code Grant flow และส่วนที่ดูเหมือนจะไม่สำคัญแต่เล่นบทบาทสำคัญในการป้องกันการฉ้อโกง

จุดสิ้นสุดการอนุญาต:

จุดสิ้นสุดการแลกเปลี่ยน token:

ข้อมูลไคลเอ็นต์

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

ข้อมูลไคลเอ็นต์มักประกอบด้วยสองส่วนประกอบ:

  1. Client ID: ตัวระบุตัวที่ไม่ซ้ำกันที่กำหนดให้กับแอปพลิเคชันไคลเอ็นต์โดยเซิร์ฟเวอร์การอนุญาต เป็นค่าที่เป็นสาธารณะที่โดยทั่วไปไม่ถือว่ามีความละเอียดอ่อน
  2. Client Secret: ค่าแบบลับและเก็บไว้ในที่ปลอดภัยที่รู้เพียงไคลเอ็นต์และเซิร์ฟเวอร์การอนุญาตเท่านั้น มันทำหน้าที่ในการยืนยันตัวของแอปพลิเคชันไคลเอ็นต์และใช้ในการตรวจสอบตัวเครื่องของไคลเอ็นต์เมื่อทำการร้องขอไปยังเซิร์ฟเวอร์การอนุญาต

ตามที่คุณเห็น, การรวมกันระหว่าง Client ID และ Client Secret ใช้ในระหว่างคำขอ token เพื่อยืนยันตัวของไคลเอ็นต์และการรับ Access Tokens.

ข้อมูลไคลเอ็นต์มีบทบาทสำคัญในการรับประกันความปลอดภัยของ flow ของ OAuth. มันช่วยให้เซิร์ฟเวอร์การอนุญาตตรวจสอบความถูกต้องของแอปพลิเคชันไคลเอ็นต์และควบคุมการเข้าถึงทรัพยากรที่ได้รับการป้องกัน การจัดการข้อมูลไคลเอ็นต์อย่างปลอดภัยและป้องกันการเข้าถึงที่ไม่ได้รับอนุญาตเป็นสิ่งสำคัญ Logto จัดประเภทแอปพลิเคชันไคลเอ็นต์ตามสองระดับความปลอดภัยที่แตกต่างกัน:

  • ไคลเอ็นต์ที่ไว้ใจได้: รวมถึงแอปพลิเคชันเว็บที่สร้างบนเซิร์ฟเวอร์และแอปพลิเคชันเครื่องจักรต่อเครื่องจักร (M2M) ในกรณีของไคลเอ็นต์ที่ไว้ใจได้ ข้อมูลที่เกี่ยวข้องกับการอนุญาตทั้งหมด รวมถึงข้อมูลไคลเอ็นต์ ถูกเก็บไว้อย่างปลอดภัยที่ฝั่งเซิร์ฟเวอร์ นอกจากนี้ คำขอแลกเปลี่ยนข้อมูลทุกรายการจะถูกเข้ารหัสเพื่อให้แน่ใจได้ถึงความปลอดภัยของข้อมูล ความเสี่ยงจากการหลุดรั่วของข้อมูลไคลเอ็นต์ในไคลเอ็นต์ที่ไว้ใจได้มีอยู่น้อยมาก ทำให้พวกมันมีความปลอดภัยโดยธรรมชาติในระดับสูงกว่า ดังนั้น ไคลเอ็นต์ที่ไว้ใจได้จะได้รับการปฏิบัติด้วยระดับความปลอดภัยที่สูงกว่าตามค่าเริ่มต้น ใน flow การแลกเปลี่ยน token, การแสดง Client Secret เป็นสิ่งที่จำเป็นต้องทำ
  • ไคลเอ็นต์สาธารณะ: รวมถึงแอปพลิเคชันเว็บหน้าเดียว (SPA) และแอปพลิเคชันพื้นเมือง สำหรับไคลเอ็นต์สาธารณะ ข้อมูลไคลเอ็นต์มักจะถูกเก็บรหัสอย่างแข็งลงไปที่ฝั่งไคลเอ็นต์ เช่น ภายในแพ็กเกจ JavaScript หรือแพ็กเกจแอปในแพลตฟอร์มพื้นเมือง ความเสี่ยงต่อการหลุดรั่วของข้อมูลไคลเอ็นต์สูงกว่าเมื่อเทียบกับไคลเอ็นต์ที่ไว้ใจได้ เนื่องจากการเปิดเผยข้อมูลไคลเอ็นต์โดยธรรมชาติในโค้ดฝั่งลูกค้า ใน flow การแลกเปลี่ยน token, การแสดง Client Secret เป็นทางเลือก Logto จะไม่เชื่อมั่นในข้อมูลจากไคลเอ็นต์สาธารณะตามค่าเริ่มต้น

สเตต

ใน flow ของ OAuth, พารามิเตอร์ state คือค่าสุ่มที่ถูกสร้างขึ้นและรวมไว้ในคำขอการอนุญาตที่ส่งโดยไคลเอ็นต์ไปยังเซิร์ฟเวอร์การอนุญาต วัตถุประสงค์ของมันคือการรักษาสถานะหรือบริบทของคำขอของไคลเอ็นต์ตลอดกระบวนการอนุญาต

พารามิเตอร์ state ทำหน้าที่เป็นมาตรการรักษาความปลอดภัยเพื่อป้องกันการโจมตี cross-site request forgery (CSRF) เมื่อเซิร์ฟเวอร์การอนุญาตถูกเปลี่ยนทิศทางผู้ใช้กลับไปยังแอปพลิเคชันไคลเอ็นต์หลังจากการยืนยันตัวและการอนุญาต มันจะรวมค่าสถานะเดียวกันในคำตอบ แอปพลิเคชันไคลเอ็นต์ จะต้อง เปรียบเทียบค่านี้กับค่าสถานะแรกเริ่มที่มันส่งในคำขอการอนุญาต

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

ลองดูตัวอย่างการโจมตี CSRF ในกรณีศึกษาสมมุติ:

การโจมตี CSRF: การฉ้อโกงเพื่อผูกบัญชีโซเชียล - ปัญหา

ด้วยกลไกการตรวจสอบ state ที่เหมาะสม, ไคลเอ็นต์สามารถตรวจจับการโจมตีและป้องกันผู้ใช้จากการถูกเปลี่ยนเส้นทางไปยังเว็บไซต์ของผู้โจมตีได้:

การโจมตี CSRF: การฉ้อโกงเพื่อผูกบัญชีโซเชียล - วิธีแก้ไข

PKCE

ตามที่ได้กล่าวไปแล้วก่อนหน้านี้, ไคลเอ็นต์สาธารณะ เช่น เว็บแอป SPA และแอปพลิเคชันพื้นเมือง มีความเสี่ยงสูงที่จะเกิดการหลุดรั่วของข้อมูลการยืนยันตน, รวมถึง Authorization Code ที่ออกโดยเซิร์ฟเวอร์การอนุญาต

PKCE ย่อมาจาก Proof Key for Code Exchange. มันเป็นการขยายฟังก์ชัน OAuth 2.0 Authorization Code Flow ที่เพิ่มความปลอดภัยให้กับไคลเอ็นต์สาธารณะ

PKCE ถูกเสนอให้มาเพื่อบรรเทาความเสี่ยงที่ผู้โจมตีจะดักจับ Authorization Code และแลกเปลี่ยนมันเพื่อ Access Token โดยไม่ให้ไคลเอ็นต์รู้ การโจมตีแบบนี้, ซึ่งเรียกว่า Authorization Code interception attack, มักพบในสิ่งแวดล้อมที่แอปพลิเคชันไคลเอ็นต์ไม่สามารถเก็บ Client Secret ได้อย่างปลอดภัย

เพื่อใช้งาน PKCE, แอปพลิเคชันไคลเอ็นต์สร้าง Code Verifier แบบสุ่มและสร้าง Code Challengeจากมันโดยใช้ algorithm hashing (มักเป็น SHA-256) Code Challenge จะถูกรวมไว้ในคำขออนุญาตแรกเริ่มที่ส่งไปยังเซิร์ฟเวอร์การอนุญาต

เมื่อเซิร์ฟเวอร์การอนุญาตออก Authorization Code, แอปพลิเคชันไคลเอ็นต์รวม Code Verifier แรกเริ่มในคำขอ token เซิร์ฟเวอร์จะตรวจสอบว่า Code Verifier ตรงกับ Code Challenge ที่เก็บไว้หรือไม่ ถ้าใช่ก็จะออก Access Token

โดยการใช้ PKCE, แอปพลิเคชันไคลเอ็นต์ทำให้มั่นใจได้ว่า Authorization Code เพียงอย่างเดียวไม่เพียงพอในการรับ Access Token กลไกนี้เพิ่มชั้นความปลอดภัยให้กับ flow การอนุญาตโดยเฉพาะสำหรับไคลเอ็นต์สาธารณะที่การจัดเก็บ Client Secrets เป็นเรื่องท้าทาย

Logto ใช้ PKCE เป็น flow การอนุญาตเดียวสำหรับแอปพลิเคชันไคลเอ็นต์สาธารณะทุกประเภท อย่างไรก็ตาม, PKCE สามารถละไว้ได้สำหรับไคลเอ็นต์ที่ไว้ใจได้

Redirect URI

Redirect URI (Uniform Resource Identifier) เป็น endpoint หรือ URL เฉพาะที่เซิร์ฟเวอร์การอนุญาตเปลี่ยนทิศทางผู้ใช้กลับไปหลังจากกระบวนการยืนยันตัวและการอนุญาตใน OAuth

ในระหว่าง flow ของ OAuth, แอปพลิเคชันไคลเอ็นต์รวม Redirect URI เป็นส่วนหนึ่งของคำขอการอนุญาตแรกเริ่ม URI นี้ทำหน้าที่เป็น URL ย้อนกลับที่ผู้ใช้จะถูกเปลี่ยนทิศทางกลับไปหลังจากการยืนยันตัวตนและให้สิทธิ์ไคลเอ็นต์สิ้นสุดสมบูรณ์

เมื่อตัวผู้ใช้สมบูรณ์กระบวนการยืนยันตัวตน, เซิร์ฟเวอร์การอนุญาตสร้างคำตอบที่รวม Authorization Code และเปลี่ยนทิศทางผู้ใช้กลับไปยัง Redirect URI ที่ระบุ

การตรวจสอบ Redirect URI เป็นขั้นตอนสำคัญในการรับประกันความปลอดภัยและความซื่อสัตย์ของ flow ของ OAuth มันเกี่ยวข้องกับการตรวจสอบว่า Redirect URI ที่ใช้ในคำขอการอนุญาตและการเปลี่ยนทิศทางต่อมายังคงเป็นจริงและเชื่อถือได้

ลองดูที่รายงาน ช่องโหว่ของ OAuth ดั้งเดิม (ส่วนต่อไปนี้อ้างอิงจาก โพสต์ ดั้งเดิม)

เมื่อผู้ใช้คลิก “login with facebook” โดยใช้แอปมือถือใน Expo Go, มันจะเปลี่ยนทิศทางผู้ใช้ไปยังลิงก์ดังนี้:

https://auth.expo.io/@moreisless3/me321/start?authUrl=https://www.facebook.com/v6.0/dialog/oauth?code_challenge=...&display=popup&auth_nonce=...&code_challenge_method=S256&redirect_uri=https://auth.expo.io/@moreisless3/me321&client_id=3287341734837076&response_type=code,token&state=gBpzi0quEg&scope=public_profile,email&returnUrl=exp://192.168.14.41:19000/--/expo-auth-session

ในคำตอบ, auth.expo.io กำหนด cookie ต่อไปนี้: ru=exp://192.168.14.41:19000/--/expo-auth-session. ค่าของ RU จะถูกใช้ภายหลังเป็นการย้อนกลับในขั้นตอน 5. จากนั้นแสดงข้อความยืนยันแก่ผู้ใช้ และถ้าผู้ใช้ยอมรับ, มันก็จะเปลี่ยนทิศทางเขาไปยังการล็อกอิน Facebook เพื่อดำเนินการต่อกระบวนการยืนยันตัวตน

หน้านี้อ่านตัวพารามิเตอร์ query “returnUrl” และกำหนด cookie ตามนั้น.

ลองเปลี่ยน returnUrl เป็น hTTps://attacker.com (https ไม่ได้รับอนุญาต ดังนั้นฉันพยายามแทรกตัวอักษรตัวใหญ่และมันได้ผล), ซึ่งกำหนด RU (Return Url)ใน cookie เป็น https://attacker.com.

ในกรณีข้างต้น, การละเว้นพารามิเตอร์ redirect_uri เดิม, Expo ได้แนะนำพารามิเตอร์ใหม่ที่เรียกว่า returnUrl โดยไม่มีการตรวจสอบที่เหมาะสม การละเว้นนี้ให้โอกาสผู้โจมตีเข้าถึง Authorization Code ที่ Facebook คืนมา รายละเอียดเพิ่มเติมโปรดดูที่ โพสต์ ดั้งเดิม

การตรวจสอบ Redirect URI มีวัตถุประสงค์หลายประการ:

  1. ป้องกันการโจมตีแบบฟิชชิง: โดยการตรวจสอบ Redirect URI, เซิร์ฟเวอร์การอนุญาตควบคุมให้ผู้ใช้ถูกเปลี่ยนทิศทางกลับไปยัง endpoint ที่เชื่อถือได้และได้รับการอนุญาต วิธีนี้ช่วยป้องกันไม่ให้ผู้โจมตีเปลี่ยนทิศทางผู้ใช้ไปยังสถานที่ที่เป็นพิษหรือที่ไม่ได้รับอนุญาต
  2. การป้องกันการเปลี่ยนทางที่เปิด: การเปลี่ยนทางที่เปิดเป็นจุดอ่อนที่สามารถถูกใช้ประโยชน์ในการเปลี่ยนทิศทางผู้ใช้ไปยังเว็บไซต์ที่ไม่ปลอดภัย โดยการตรวจสอบ Redirect URI, เซิร์ฟเวอร์การอนุญาตสามารถมั่นใจได้ว่าการเปลี่ยนทิศทางยังคงอยู่ภายในขอบเขตของโดเมนที่ได้รับอนุญาตหรือชุดของโดเมนที่เชื่อถือได้
  3. การรับรองการจัดการคำตอบการอนุญาตที่ถูกต้อง: การตรวจสอบ Redirect URI ช่วยประกันว่าเซิร์ฟเวอร์การอนุญาตเปลี่ยนทิศทางผู้ใช้กลับไปยังแอปพลิเคชันไคลเอ็นต์ที่ตั้งใจ คำตอบ, เช่น Authorization Code หรือ Access Token, จะถูกส่งไปยังจุดหมายที่ถูกต้อง

ใน Logto, การลงทะเบียน redirect_uri เป็นสิ่งบังคับสำหรับแอปพลิเคชันทุกประเภท เราเปรียบเทียบและจับค่าที่ได้รับกับค่าที่ลงทะเบียนในเซิร์ฟเวอร์ Logto ซึ่งรวมถึงพารามิเตอร์การค้นหาเฉพาะเจาะจง ถ้าคำขอการอนุญาตล้มเหลวในการตรวจสอบเนื่องจาก redirect_uri ที่ขาดหาย, ไม่ถูกต้อง, หรือไม่ตรงกัน, ข้อผิดพลาด Redirect URI ที่ไม่ถูกต้องจะถูกส่งคืนไปยัง redirect_uri ที่ลงทะเบียนไว้ในแฟ้มนั้น

สรุป

เนื่องจากมีลักษณะที่ซับซ้อนและละเอียดอ่อน มันเป็นเรื่องที่เข้าใจได้ว่าอาจถูกมองข้ามรายละเอียดเหล่านี้ บางอย่างเป็นเพียงสตริงสุ่มเช่น state

อย่างไรก็ตาม, เราควรตระหนักว่ามาตรการรักษาความปลอดภัยเหล่านี้ช่วยเพิ่มความคุ้มครองในการอนุญาตของผู้ใช้ ลดความเสี่ยงจากการโจมตี CSRF การดักข้อมูล Authorization Code และการเปลี่ยนทางที่ไม่ได้รับอนุญาต

สิ่งเหล่านี้เป็นเพียงเศษเล็ก ๆ ของความปลอดภัยที่ครอบคลุมที่เสนอโดยโปรโตคอล OAuth OAuth มอบโครงร่างที่แข็งแกร่งสำหรับการยืนยันตัวตนและการอนุญาตที่ปลอดภัย มันยังเสนอตัวขับเคลื่อนที่ยืดหยุ่นและเปิดสำหรับการตอบสนองต่อความต้องการที่หลากหลายในแอปพลิเคชันผลิตภัณฑ์ในโลกจริง

ในฐานะนักพัฒนาและผู้ให้บริการ, มันเป็นสิ่งสำคัญที่จะให้ความสำคัญต่อความปลอดภัยของ flow การอนุญาตของผู้ใช้อย่างต่อเนื่อง การให้ความสำคัญ, การปฏิบัติตามแนวทางที่ดีที่สุด และการศึกษาพัฒนาการล่าสุดในโลกของ OAuth เป็นสิ่งสำคัญในการประกันความซื่อสัตย์และป้องกันตัวตนของผู้ใช้และข้อมูลที่ละเอียดอ่อน เราจะมุ่งมั่นในการรักษามาตรฐานความปลอดภัยสูงสุดในการใช้งาน OAuth และรักษาความเป็นส่วนตัวและความเชื่อถือของผู้ใช้ของเรา