PHP Session Fixation / Hijacking


145

ฉันกำลังพยายามทำความเข้าใจเพิ่มเติมเกี่ยวกับ PHP Session Fixation & hijacking และวิธีป้องกันปัญหาเหล่านี้ ฉันได้อ่านบทความสองเรื่องต่อไปนี้บนเว็บไซต์ของ Chris Shiflett:

อย่างไรก็ตามฉันไม่แน่ใจว่าฉันเข้าใจสิ่งต่าง ๆ อย่างถูกต้อง

เพื่อช่วยป้องกันการแก้ไขเซสชันเพียงพอที่จะเรียก session_regenerate_id (จริง); หลังจากลงชื่อเข้าใช้บางคนสำเร็จแล้ว ฉันคิดว่าฉันเข้าใจถูกต้อง

นอกจากนี้เขายังพูดถึงการใช้โทเค็นที่ส่งผ่านไปใน URL ผ่าน $ _GET เพื่อป้องกันการขโมยเซสชัน สิ่งนี้จะเกิดขึ้นได้อย่างไร ฉันคาดเดาว่าเมื่อมีคนลงชื่อเข้าใช้คุณสร้างโทเค็น & เก็บไว้ในตัวแปรเซสชันแล้วในแต่ละหน้าคุณจะเปรียบเทียบตัวแปรเซสชันนั้นกับค่าของตัวแปร $ _GET หรือไม่

โทเค็นนี้จำเป็นต้องเปลี่ยนเพียงหนึ่งครั้งต่อเซสชันหรือในแต่ละหน้าโหลดหรือไม่

ยังเป็นวิธีที่ดีในการป้องกันการหักหลังโดยไม่ต้องผ่านค่าตามใน urls? มันจะง่ายขึ้นมาก


บางทีคุณอาจเพิ่มลิงก์ไปยังหน้าที่คุณพบคำแนะนำเหล่านี้
Gumbo

คำตอบ:


219

ตกลงมีสองแยกต่างหาก แต่ปัญหาที่เกี่ยวข้องและแต่ละจัดการแตกต่างกัน

การแก้ไขเซสชัน

นี่คือที่ผู้โจมตีตั้งค่าตัวระบุเซสชันของเซสชันสำหรับผู้ใช้อย่างชัดเจน โดยปกติใน PHP มันทำโดยให้พวกเขา URL http://www.example.com/index...?session_name=sessionidเช่น เมื่อผู้โจมตีให้ URL แก่ลูกค้าการโจมตีจะเหมือนกับเซสชันที่ถูกไฮแจ็กโจมตี

มีสองสามวิธีในการป้องกันการแก้ไขเซสชั่น (ทำทั้งหมด):

  • ตั้งอยู่session.use_trans_sid = 0ในphp.iniไฟล์ของคุณ สิ่งนี้จะบอก PHP ว่าจะไม่รวมตัวระบุใน URL และไม่อ่าน URL สำหรับตัวระบุ

  • ตั้งอยู่session.use_only_cookies = 1ในphp.iniไฟล์ของคุณ สิ่งนี้จะบอก PHP ว่าจะไม่ใช้ URL กับตัวระบุเซสชัน

  • สร้างรหัสเซสชันขึ้นใหม่ทุกครั้งที่มีการเปลี่ยนแปลงสถานะของเซสชัน นั่นหมายถึงสิ่งใดสิ่งหนึ่งต่อไปนี้:

    • การตรวจสอบผู้ใช้
    • การจัดเก็บข้อมูลที่ละเอียดอ่อนในเซสชั่น
    • การเปลี่ยนแปลงอะไรก็ได้เกี่ยวกับเซสชั่น
    • ฯลฯ ...

เซสชั่นหักหลัง

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

คุณไม่สามารถป้องกันการจี้เซสชันโดยตรง อย่างไรก็ตามคุณสามารถใส่ขั้นตอนต่าง ๆ ในการทำให้ใช้งานได้ยากมากขึ้น

  • ใช้ตัวระบุแฮชเซสชั่นที่แข็งแกร่ง: ในsession.hash_function php.iniถ้า PHP <5.3 ให้ตั้งเป็นsession.hash_function = 1SHA1 ถ้า PHP> = 5.3 กำหนดให้หรือsession.hash_function = sha256session.hash_function = sha512

  • ส่งกัญชาเข้มแข็งในsession.hash_bits_per_character ตั้งค่านี้php.ini session.hash_bits_per_character = 5แม้ว่าสิ่งนี้จะไม่ทำให้แตกได้ยากขึ้นแต่ก็สร้างความแตกต่างเมื่อผู้โจมตีพยายามคาดเดาตัวระบุเซสชัน ID จะสั้นลง แต่ใช้อักขระได้มากกว่า

  • ตั้งค่าเอนโทรปีเพิ่มเติมด้วยsession.entropy_fileและsession.entropy_lengthในphp.iniไฟล์ของคุณ ตั้งอดีตและหลังจำนวนไบต์ที่จะอ่านจากไฟล์เอนโทรปียกตัวอย่างเช่นsession.entropy_file = /dev/urandomsession.entropy_length = 256

  • เปลี่ยนชื่อของเซสชันจากค่าเริ่มต้น PHPSESSID นี่คือความสำเร็จโดยการเรียกด้วยชื่อตัวระบุของคุณเองเป็นพารามิเตอร์แรกก่อนที่จะมีการเรียก session_name()session_start

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

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

  • รวมตัวแทนผู้ใช้จาก$_SERVER['HTTP_USER_AGENT']ในเซสชัน $_SESSION['user_agent']โดยทั่วไปเมื่อเซสชั่นเริ่มต้นเก็บไว้ในสิ่งที่ต้องการ จากนั้นให้ตรวจสอบคำขอแต่ละครั้งในภายหลังว่าตรงกันหรือไม่ โปรดทราบว่านี่อาจปลอมได้ดังนั้นจึงไม่น่าเชื่อถือ 100% แต่ดีกว่าไม่

  • รวมที่อยู่ IP ของผู้ใช้จาก$_SERVER['REMOTE_ADDR']ในเซสชัน $_SESSION['remote_ip']โดยทั่วไปเมื่อเซสชั่นเริ่มต้นเก็บไว้ในสิ่งที่ต้องการ นี่อาจเป็นปัญหาจาก ISP บางรายที่ใช้ที่อยู่ IP หลายรายการสำหรับผู้ใช้ (เช่น AOL เคยทำ) แต่ถ้าคุณใช้มันจะปลอดภัยกว่ามาก วิธีเดียวที่ผู้โจมตีจะปลอมที่อยู่ IP คือการประนีประนอมเครือข่ายในบางจุดระหว่างผู้ใช้จริงกับคุณ และหากพวกเขาประนีประนอมเครือข่ายพวกเขาสามารถทำได้แย่กว่าการจี้ (เช่นการโจมตี MITM และอื่น ๆ )

  • รวมโทเค็นในเซสชันและทางด้านเบราว์เซอร์ที่คุณเพิ่มและเปรียบเทียบบ่อยครั้ง โดยทั่วไปสำหรับแต่ละคำขอทำ$_SESSION['counter']++ในฝั่งเซิร์ฟเวอร์ ทำบางสิ่งบางอย่างใน JS ที่ด้านเบราว์เซอร์เพื่อทำสิ่งเดียวกัน (ใช้ที่เก็บข้อมูลในเครื่อง) จากนั้นเมื่อคุณส่งคำร้องขอเพียงรับโทเค็นที่ไม่ได้ระบุและตรวจสอบว่า nonce นั้นเหมือนกันบนเซิร์ฟเวอร์ โดยการทำเช่นนี้คุณควรจะสามารถตรวจพบเซสชั่น hijacked เนื่องจากผู้โจมตีจะไม่มีตัวนับที่แน่นอนหรือถ้าพวกเขาทำคุณจะมี 2 ระบบที่ส่งจำนวนเดียวกันและบอกได้ว่ามีการปลอมแปลง สิ่งนี้จะไม่ทำงานสำหรับแอปพลิเคชันทั้งหมด แต่เป็นวิธีหนึ่งในการแก้ไขปัญหา

หมายเหตุเกี่ยวกับทั้งสอง

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

การฟื้นฟู ID เซสชัน

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

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

ทำลายเซสชั่น

หากคุณกำลังจะทำลายเซสชั่น (เมื่อออกจากระบบตัวอย่าง) ตรวจสอบให้แน่ใจว่าคุณทำลายมันอย่างละเอียด รวมถึงการยกเลิกการตั้งค่าคุกกี้ การใช้session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}

4
การใช้ 5 แทน 4 บิตต่อตัวละครจะไม่เปลี่ยน "ความแข็งแกร่ง" ในทางใดทางหนึ่ง (สิ่งใดก็ตามที่ "ความแข็งแกร่ง" หมายถึงในกรณีนี้) แต่ถึงแม้ว่าคะแนนของคุณจะแนะนำโดยทั่วไป แต่ก็ไม่มีรายละเอียดที่สำคัญ ตัวอย่างเช่นเกิดอะไรขึ้นกับเซสชันที่เชื่อมโยงกับ ID เซสชันเก่าหรือวิธีจัดการเซสชันที่มี ID เซสชันเก่านั้นควรได้รับการจัดการอย่างไรหลังจากมันไม่ถูกต้อง
Gumbo

2
@battal: ไม่นั่นคือประเด็น session_regenerate_idไม่ทำให้เซสชันที่เชื่อมโยงกับ ID เก่าใช้ไม่ได้ หากพารามิเตอร์delete_old_sessionถูกตั้งค่าเป็นจริงเซสชันจะถูกทำลาย แต่ถ้าผู้โจมตีเริ่มต้นการสร้าง ID ใหม่
Gumbo

6
ฉันไม่เห็นด้วยกับการสร้างเซสชั่นทุกครั้งที่คุณเปลี่ยนตัวแปรเซสชั่นมันควรจะทำเฉพาะในการเข้าสู่ระบบ / ออกจากระบบ การตรวจสอบ user-agent นั้นไม่มีความหมายและการตรวจสอบ REMOTE_ADDR ก็เป็นปัญหาเช่นกัน session.entropy_file = /dev/urandomสิ่งหนึ่งที่ผมอยากจะเพิ่มเป็น การสร้างเอนโทรปีภายในของ PHP นั้นพิสูจน์ได้ว่าอ่อนแอมากและพูลเอนโทรปีที่จัดทำโดย / dev / random หรือ / dev / uranom นั้นดีที่สุดที่คุณจะได้รับบนเว็บเซิร์ฟเวอร์โดยไม่ต้องใช้ฮาร์ดแวร์ rng
rook

4
นอกจากนี้คุณควรเพิ่มและsession.cookie_httponly session.cookie_secureคนแรกช่วยป้องกัน xss (แต่มันไม่สมบูรณ์แบบ) ครั้งที่ 2 คือวิธีที่ดีที่สุดที่จะหยุด OWASP A9 ...
โกง

4
ไม่เข้าใจคำตอบที่ยอดเยี่ยม แต่ขาดชิ้นส่วนที่นำเข้ามากที่สุด: ใช้ SSL / HTTPS การเพิ่มจำนวนตัวนับเป็นสาเหตุของปัญหาที่เกิดขึ้นกับการร้องขอหลายครั้งอย่างรวดเร็วซึ่งกันและกันผู้ใช้จะรีเฟรชหน้าสองครั้งหรือกดปุ่มส่งสองครั้ง การแก้ปัญหาที่อยู่ IP เป็นปัญหาในปัจจุบันกับผู้ใช้มือถือและเปลี่ยน IP คุณสามารถดู IP ชุดแรกได้ แต่ก็ยังมีปัญหาอยู่ ดีที่สุดคือการป้องกันไม่ให้ค้นพบรหัสเซสชันในตอนแรกและนั่นคือการใช้ SSL / HTTPS
Sanne

37

การโจมตีทั้งสองครั้งมีเป้าหมายเดียวกัน: เข้าใช้งานเซสชันที่ถูกกฎหมายของผู้ใช้รายอื่น แต่เวกเตอร์การโจมตีนั้นต่างกัน:

  • ในการโจมตีการตรึงเซสชั่นผู้โจมตีมีการเข้าถึงเซสชั่นที่ถูกต้องแล้วและพยายามที่จะบังคับให้เหยื่อที่จะใช้เซสชั่นนี้โดยเฉพาะ

  • ในการโจมตีด้วยเซสชัน Hijackingผู้โจมตีพยายามรับ ID ของเซสชันของเหยื่อเพื่อใช้เซสชันของตน

ในการโจมตีทั้งสองครั้ง ID ของเซสชั่นเป็นข้อมูลที่ละเอียดอ่อนการโจมตีเหล่านี้มุ่งเน้นไปที่ ดังนั้นจึงเป็นรหัสเซสชันที่ต้องได้รับการปกป้องทั้งการเข้าถึงแบบอ่าน (การหักหลังเซสชัน) และการเข้าถึงการเขียน (การแก้ไขเซสชัน)

กฎทั่วไปของการปกป้องข้อมูลที่สำคัญโดยใช้ HTTPS ก็มีผลในกรณีนี้เช่นกัน นอกจากนี้คุณควรทำสิ่งต่อไปนี้:

ในการป้องกันการโจมตีSession Fixationตรวจสอบให้แน่ใจว่า:

  • ID เซสชันนั้นได้รับการยอมรับจากคุกกี้เท่านั้น (ตั้งค่าsession.use_only_cookiesเป็นtrue) และทำให้เป็น HTTPS ต่อเมื่อเป็นไปได้ (ตั้งค่าsession.cookie_secureเป็นtrue); คุณสามารถทำได้ทั้งกับsession_set_cookie_paramsคุณสามารถทำทั้งสองด้วย

เพื่อป้องกันการโจมตีด้วยเซสชันจี้ตรวจสอบให้แน่ใจว่า:

เพื่อป้องกันการโจมตีทั้งสองครั้งตรวจสอบให้แน่ใจว่า:

  • เพื่อยอมรับเฉพาะเซสชันที่แอปพลิเคชันของคุณเริ่มต้น คุณสามารถทำได้โดยพิมพ์ลายนิ้วมือเซสชันเมื่อเริ่มต้นด้วยข้อมูลเฉพาะลูกค้า คุณสามารถใช้ID ผู้ใช้ - ตัวแทนแต่ไม่ใช้ที่อยู่ IP ระยะไกลหรือข้อมูลอื่น ๆ ที่อาจเปลี่ยนแปลงจากระหว่างการร้องขอ
  • เพื่อเปลี่ยน ID เซสชันที่ใช้session_regenerate_id(true)หลังจากพยายามพิสูจน์ตัวตน ( trueสำเร็จเท่านั้น) หรือเปลี่ยนสิทธิ์และทำลายเซสชันเก่า (อย่าลืมเก็บการเปลี่ยนแปลงการ$_SESSIONใช้งานไว้session_write_close ก่อนหน้านี้สร้าง ID ใหม่หากคุณต้องการสงวนเซสชันที่เชื่อมโยงกับ ID เก่าไว้มิฉะนั้นเฉพาะเซสชันที่มี ID ใหม่จะได้รับผลกระทบจากการเปลี่ยนแปลงเหล่านั้น)
  • เพื่อใช้การดำเนินการหมดอายุเซสชันที่เหมาะสม (ดูฉันจะหมดอายุเซสชัน PHP หลังจาก 30 นาทีได้อย่างไร )

โพสต์ที่น่ากลัวโดยเฉพาะอย่างยิ่งในส่วนสุดท้าย
Mattis

6

โทเค็นที่คุณพูดถึงเป็น "nonce" - จำนวนที่ใช้เพียงครั้งเดียว พวกเขาไม่จำเป็นต้องใช้เพียงครั้งเดียว แต่ยิ่งใช้นานเท่าไรอัตราต่อรองที่สูงขึ้นที่ nonce สามารถถูกจับและใช้เพื่อจี้เซสชั่น

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

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

หากคุณไม่มีวิธีการติดตามหลาย ๆ หน้าต่างคุณจะได้เก็บ nonce ไว้เพียงอันเดียวนั่นคือหน้าต่าง B / Q เมื่อผู้ใช้แล้วส่งโพสต์ของพวกเขาจากหน้าต่างและผ่านไปในขณะปัจจุบัน 'P' ระบบ ths P != Qจะปฏิเสธโพสต์เป็น


ดังนั้นสิ่งนี้เกี่ยวข้องกับการแก้ไขเซสชันหรือไม่
rook

2
เขามีจุดที่ถูกต้องโดยเฉพาะในขอบเขตของการใช้คำขอ AJAX จำนวนมากพร้อมกัน
DanielG

2

ฉันไม่ได้อ่านบทความของ Shiflett แต่ฉันคิดว่าคุณเข้าใจผิด

โดยค่าเริ่มต้น PHP จะส่งโทเค็นเซสชันใน URL เมื่อใดก็ตามที่ไคลเอนต์ไม่ยอมรับคุกกี้ ในกรณีส่วนใหญ่โทเค็นเซสชันจะถูกเก็บไว้เป็นคุกกี้

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

ดังที่ฉันแน่ใจว่า Shiflett อธิบายว่าสิ่งที่ต้องทำตามปกติคือการสร้างโทเค็นที่แตกต่างกันใหม่ทุกครั้งที่มีการเปลี่ยนแปลงสิทธิ์ของผู้ใช้


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

0

ใช่คุณสามารถป้องกันการตรึงเซสชั่นโดยการสร้างรหัสเซสชั่นอีกครั้งเมื่อเข้าสู่ระบบ วิธีนี้หากผู้โจมตีไม่ทราบค่าคุกกี้ของเซสชันที่ได้รับการรับรองความถูกต้องใหม่ วิธีการอื่นที่หยุดปัญหาโดยสิ้นเชิงคือการตั้งค่าsession.use_only_cookies=Trueในการกำหนดค่ารันไทม์ของคุณ ผู้โจมตีไม่สามารถตั้งค่าคุกกี้ในบริบทของโดเมนอื่น การแก้ไขเซสชันใช้การส่งค่าคุกกี้เป็น GET หรือ POST

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.