CSRF Token จำเป็นเมื่อใช้ Stateless (= Sessionless) Authentication?


125

จำเป็นหรือไม่ที่จะต้องใช้การป้องกัน CSRF เมื่อแอปพลิเคชันต้องอาศัยการพิสูจน์ตัวตนแบบไร้สัญชาติ (โดยใช้บางอย่างเช่น HMAC)

ตัวอย่าง:

  • เรามีแอพหน้าเดียว (มิฉะนั้นเราต้องต่อท้ายโทเค็นในแต่ละลิงค์: <a href="...?token=xyz">...</a>.

  • POST /authผู้ใช้จะตรวจสอบตัวเองใช้ ในการตรวจสอบสิทธิ์สำเร็จเซิร์ฟเวอร์จะส่งคืนโทเค็นบางส่วน

  • โทเค็นจะถูกจัดเก็บผ่าน JavaScript ในตัวแปรบางตัวภายในแอพหน้าเดียว

  • โทเค็นนี้จะใช้ในการ จำกัด การเข้าถึง URL /adminที่ชอบ

  • โทเค็นจะถูกส่งภายใน HTTP Headers เสมอ

  • ไม่มีเซสชัน Http และไม่มีคุกกี้

เท่าที่ฉันเข้าใจมี (?!) ไม่ควรใช้การโจมตีข้ามไซต์เนื่องจากเบราว์เซอร์จะไม่เก็บโทเค็นและด้วยเหตุนี้จึงไม่สามารถส่งไปยังเซิร์ฟเวอร์โดยอัตโนมัติได้ (นั่นคือสิ่งที่จะเกิดขึ้นเมื่อใช้คุกกี้ / เซสชั่น)

ฉันพลาดอะไรไปรึเปล่า?


6
ระมัดระวังเกี่ยวกับการตรวจสอบสิทธิ์ขั้นพื้นฐาน เบราว์เซอร์จำนวนมากจะส่งส่วนหัวการตรวจสอบสิทธิ์พื้นฐานโดยอัตโนมัติสำหรับส่วนที่เหลือของเซสชัน ซึ่งอาจทำให้การตรวจสอบสิทธิ์ขั้นพื้นฐานมีความเสี่ยงต่อ CSRF เช่นเดียวกับการตรวจสอบสิทธิ์คุกกี้
ไฟแล

คำตอบ:


159

ฉันพบข้อมูลบางอย่างเกี่ยวกับ CSRF + โดยไม่ใช้คุกกี้ในการตรวจสอบสิทธิ์:

  1. https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
    "เนื่องจากคุณไม่ได้อาศัยคุกกี้คุณจึงไม่จำเป็นต้องป้องกันคำขอข้ามไซต์"

  2. http://angular-tips.com/blog/2014/05/json-web-tokens-introduction/
    "หากเราใช้คุกกี้คุณจำเป็นต้องทำ CSRF เพื่อหลีกเลี่ยงการร้องขอข้ามไซต์นั่นคือสิ่งที่เราทำได้ ลืมไปเมื่อใช้ JWT อย่างที่คุณเห็น "
    (JWT = Json Web Token การพิสูจน์ตัวตนโดยใช้โทเค็นสำหรับแอปไร้สัญชาติ)

  3. http://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services
    "วิธีที่ง่ายที่สุดในการตรวจสอบสิทธิ์โดยไม่เสี่ยงต่อช่องโหว่ของ CSRF คือหลีกเลี่ยงการใช้คุกกี้เพื่อระบุตัวผู้ใช้ "

  4. http://sitr.us/2011/08/26/cookies-are-bad-for-you.html
    "ปัญหาใหญ่ที่สุดของ CSRF คือคุกกี้ไม่สามารถป้องกันการโจมตีประเภทนี้ได้อย่างแน่นอนหากคุณใช้การตรวจสอบสิทธิ์คุกกี้ คุณต้องใช้มาตรการเพิ่มเติมเพื่อป้องกัน CSRF ข้อควรระวังขั้นพื้นฐานที่สุดที่คุณสามารถทำได้คือตรวจสอบให้แน่ใจว่าแอปพลิเคชันของคุณไม่ก่อให้เกิดผลข้างเคียงใด ๆ ในการตอบสนองคำขอ GET "

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


หากคุณต้องการจำผู้ใช้มี 2 ตัวเลือก:

  1. localStorage: ที่เก็บคีย์ - ค่าในเบราว์เซอร์ ข้อมูลที่จัดเก็บจะพร้อมใช้งานแม้ว่าผู้ใช้จะปิดหน้าต่างเบราว์เซอร์ เว็บไซต์อื่นไม่สามารถเข้าถึงข้อมูลได้เนื่องจากทุกไซต์มีพื้นที่เก็บข้อมูลของตัวเอง

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

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

(สำหรับทั้งสองดูได้ที่นี่: http://www.w3schools.com/html/html5_webstorage.asp )


มีมาตรฐานอย่างเป็นทางการสำหรับการตรวจสอบความถูกต้องโทเค็นหรือไม่?

JWT (Json Web Token): ฉันคิดว่ามันยังคงเป็นแบบร่าง แต่หลายคนใช้ไปแล้วและแนวคิดก็ดูเรียบง่ายและปลอดภัย (IETF: http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25 )
นอกจากนี้ยังมีไลบรารีสำหรับเฟรมเวิร์กมากมาย เพียงแค่ Google มัน!


37
บทสรุปที่ดีเกี่ยวกับ CSRF! ฉันจะทราบว่าการจัดเก็บโทเค็นของคุณใน localStorage หรือ sessionStorage มีความเสี่ยงต่อการโจมตี XSS และสคริปต์ในหน้าสามารถดูข้อมูลได้ดังนั้นหากคุณมีสคริปต์ที่ถูกบุกรุกจาก CDN หรือหากมีรหัสที่เป็นอันตรายในหนึ่งในของคุณ ไลบรารี JS สามารถขโมยโทเค็นจากที่เก็บข้อมูลเหล่านั้นได้ ดู: stormpath.com/blog/…ฉันคิดว่าวิธีที่ปลอดภัยที่สุดคือการจัดเก็บโทเค็น JWT + CSRF ในคุกกี้จากนั้นวาง JWT ที่คำนวณแล้วของคุณโดยมีโทเค็น CSRF อยู่ข้างในในส่วนหัวของคำขอ
Aaron Grey

เกี่ยวกับ: "ข้อควรระวังขั้นพื้นฐานที่สุดที่คุณสามารถทำได้คือตรวจสอบให้แน่ใจว่าแอปพลิเคชันของคุณไม่มีผลข้างเคียงใด ๆ ในการตอบสนองคำขอ GET" เป็นไปได้หรือไม่ที่การโจมตี CSRF จะปลอมคำขอ POST
Costa

ขึ้นอยู่กับแอปพลิเคชันฝั่งเซิร์ฟเวอร์สามารถเป็นไปได้ มีกรอบงานเว็บที่ใช้บางอย่างเช่นhttp://.../someRestResource?method=POST. ดังนั้นจึงเป็นGETคำขอโดยทั่วไปแต่แอปพลิเคชันเซิร์ฟเวอร์ตีความว่าเป็นPOSTคำขอเนื่องจากได้รับการกำหนดค่าให้ใช้methodพารามิเตอร์แทนส่วนหัว HTTP ...สำหรับเว็บเบราว์เซอร์ทั่วไปพวกเขาบังคับใช้นโยบายแหล่งกำเนิดเดียวกันและจะดำเนินการตามGETคำขอไปยังเซิร์ฟเวอร์ต่างประเทศเท่านั้น แม้ว่าจะเป็นไปได้ที่จะดำเนินการตามPOSTคำขอหากเว็บเบราว์เซอร์ไม่ใช้มาตรฐานเว็บเหล่านั้น (ข้อบกพร่องมัลแวร์)
Benjamin M

1
นอกเหนือจากServer Side App: ยังไม่สามารถส่ง Request Body ได้เนื่องจากเบราว์เซอร์ทั่วไปไม่อนุญาต อย่างไรก็ตามหากแอปเซิร์ฟเวอร์อนุญาตแอปmethod=POSTอาจอนุญาตให้body={someJson}แทนที่เนื้อหาคำขอเริ่มต้น นั่นเป็นการออกแบบ API ที่แย่มากและมีความเสี่ยงอย่างมาก แม้ว่าแอพเซิร์ฟเวอร์ของคุณจะอนุญาตให้http://...?method=POST&body={someJson}คุณควรคิดทบทวนว่าคุณทำอะไรที่นั่นทำไมและถ้าจำเป็นเลย (ฉันบอกว่าใน 99,9999% ของกรณีไม่จำเป็น) นอกจากนี้เบราว์เซอร์สามารถส่งข้อมูลได้เพียงไม่กี่กิโลไบต์ด้วยวิธีนี้
Benjamin M

@BenjaminM สังเกตว่านโยบายจุดเริ่มต้นเดียวกันเพียงป้องกันไม่ให้รหัส javaScript เข้าถึงผลลัพธ์ดังนั้นในขณะที่คำขอถูก "บล็อก" แต่ก็ไปถึงเซิร์ฟเวอร์ - jsbin.com/mewaxikuqo/edit?html,js,outputฉันทดสอบสิ่งนี้บน firefox เท่านั้น แต่คุณสามารถเปิดเครื่องมือสำหรับนักพัฒนาและดูว่าแม้ว่าคุณจะได้รับ "คำขอข้ามแหล่งที่มาที่ถูกบล็อก" เซิร์ฟเวอร์ระยะไกลก็ยังเห็นคำขอทั้งหมด นั่นคือเหตุผลที่คุณต้องมีโทเค็นหรือส่วนหัวที่กำหนดเอง (และถ้าเป็นไปได้ทั้งสองอย่าง) สำหรับคำขอ POST ทั้งหมดของคุณ
Yoni Jah

59

TL; DR

JWT หากใช้โดยไม่มีคุกกี้จะปฏิเสธความจำเป็นในการใช้โทเค็น CSRF - แต่! โดยการจัดเก็บ JWT ใน session / localStorage คุณจะเปิดเผย JWT และข้อมูลประจำตัวของผู้ใช้หากไซต์ของคุณมีช่องโหว่ XSS (พบได้บ่อย) ควรเพิ่มcsrfTokenคีย์ให้กับ JWT และจัดเก็บ JWT ในคุกกี้ที่มีsecureและhttp-onlyแอตทริบิวต์ที่ตั้งไว้

อ่านบทความนี้พร้อมคำอธิบายที่ดีสำหรับข้อมูลเพิ่มเติม https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

คุณสามารถทำให้การป้องกัน CSRF นี้ไร้สถานะโดยรวมการอ้างสิทธิ์ xsrfToken JWT:

{ "iss": "http://galaxies.com", "exp": 1300819380, "scopes": ["explorer", "solar-harvester", "seller"], "sub": "tom@andromeda.com", "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e" }

ดังนั้นคุณจะต้องจัดเก็บ csrfToken ใน localStorage / sessionStorage เช่นเดียวกับใน JWT เอง (ซึ่งเก็บไว้ใน http เท่านั้นและคุกกี้ที่ปลอดภัย) จากนั้นสำหรับการป้องกัน csrf ให้ตรวจสอบว่าโทเค็น csrf ใน JWT ตรงกับส่วนหัว csrf-token ที่ส่งมา


2
ควรยกเว้นการใช้โทเค็น csrf ระหว่างการพิสูจน์ตัวตนผู้ใช้ api หรือไม่
user805981

3
เป็นสิ่งที่ควรค่าแก่การชี้ให้เห็น (ตามที่คนอื่น ๆ ได้กล่าวไว้ในความคิดเห็นในลิงก์ต้นทาง) ว่าการลด CSRF ใด ๆ ที่ใช้ a) คุกกี้ซึ่งไม่ใช่ http เท่านั้นหรือ b) การจัดเก็บโทเค็น CSRF ในพื้นที่จัดเก็บในตัวเครื่องนั้นเสี่ยงต่อ XSS ซึ่งหมายความว่าแนวทางที่นำเสนออาจช่วยรักษาความลับ JWT จากผู้โจมตีที่ใช้ XSS ได้ แต่ผู้โจมตียังคงสามารถดำเนินการตามคำขอที่เป็นอันตรายบน API ของคุณได้เนื่องจากเขาสามารถให้ JWT ที่ถูกต้องได้ (ผ่านคุกกี้ขอบคุณเบราว์เซอร์) และโทเค็น CSRF (อ่านผ่าน JS ที่ฉีดจากที่จัดเก็บ / คุกกี้ในเครื่อง)
Johannes Rudolph

1
จริงๆแล้วแม้แต่โทเค็น CSRF ก็ไม่สามารถปกป้องคุณในระดับ XSS นี้ได้เนื่องจากคุณสมมติว่าผู้โจมตีสามารถเข้าถึง localStorage ได้ซึ่งวิธีเดียวในการเข้าถึงในปัจจุบันคือการเข้าถึงระดับสคริปต์ซึ่งพวกเขาสามารถดูโทเค็น CSRF ได้อยู่ดี .
มันไม่ถูกต้อง

1
นั่นคือสิ่งที่ @JohannesRudolph พูดใช่ไหม ทันทีที่คุณจัดเก็บโทเค็น CSRF ไว้ในที่เก็บข้อมูลบนเว็บ / คุกกี้ที่ไม่ใช่ http เท่านั้นคุณกำลังเพิ่มขนาดของการโจมตี XSS เนื่องจากสามารถเข้าถึงได้ผ่าน JS
adam-beck

1
ไม่ใช่ผู้เชี่ยวชาญทั้งหมดที่นี่ แต่ถ้าคุณยังคงสัมผัสกับ XSS เหมือนตอนแรกฉันไม่แน่ใจว่าส่วนนี้จะดีกว่าที่จะเพิ่ม ...ถือจริงๆ อาจจะซับซ้อนกว่าเล็กน้อย (?) สำหรับผู้โจมตีที่จะได้รับโทเค็น CSRF แต่ในตอนท้ายเขายังคงสามารถดำเนินการตามคำขอในนามของคุณได้แม้ว่าจะไม่รู้จริง ๆ ว่าโทเค็น JWT ก็ตาม ถูกต้องหรือไม่ ขอบคุณ
superjos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.