เหตุใดจึงไม่ควรขอข้อมูลการเปลี่ยนแปลง GET บนเซิร์ฟเวอร์


109

ทั่วอินเทอร์เน็ตฉันเห็นคำแนะนำต่อไปนี้:

GET ไม่ควรเปลี่ยนแปลงข้อมูลบนเซิร์ฟเวอร์ - ใช้คำขอ POST สำหรับสิ่งนั้น

พื้นฐานของความคิดนี้คืออะไร

ถ้าฉันสร้างบริการ php ซึ่งแทรกข้อมูลในฐานข้อมูลและส่งผ่านพารามิเตอร์ในสตริงการสืบค้น GET ทำไมจึงเป็นเช่นนั้น (ฉันใช้คำสั่งที่เตรียมไว้เพื่อดูแล SQL Injection) คำขอ POST มีความปลอดภัยมากกว่าหรือไม่

หรือมีเหตุผลทางประวัติศาสตร์สำหรับเรื่องนี้? คำแนะนำนี้วันนี้มีความถูกต้องหรือไม่?




ขอขอบคุณสำหรับการถามคำถามนี้และขอขอบคุณ @Oded สำหรับคำตอบ phrased ดีฉันมักจะจำเป็นต้องมีการอ้างอิงในการส่งคนที่ถามคำถามนี้ต่อ :)
เบนจามิน Gruenbaum

โปรดดู HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest (พร้อมหมายเหตุเกี่ยวกับการเป็น idempotent)
Bratch

2
@JoachimSauer ในขณะที่ GET จะบันทึกพวกเขาจากตัวตระเวน แต่ปัญหาหลักคือการขาดการรับรองความถูกต้อง สคริปต์ตัวเล็ก ๆ ก็สามารถโพสต์ข้อความเหล่านั้นด้วยการให้อภัย
CodesInChaos

คำตอบ:


185

นี่ไม่ใช่คำแนะนำ

GETถูกกำหนดในลักษณะนี้ในโปรโตคอล HTTP มันควรจะidempotentและปลอดภัย

สำหรับสาเหตุ - GETสามารถแคชและในเบราว์เซอร์รีเฟรช เป็นวรรคเป็นเวร

ซึ่งหมายความว่าถ้าคุณทำแบบเดียวกันGETอีกครั้งคุณจะใส่ลงในฐานข้อมูลของคุณอีกครั้ง

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

ฉันยังขอแนะนำให้อ่านยูริ, แอดเดรสและการใช้ HTTP GET และ POST


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

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


35
เครื่องมือเครื่องมือยูทิลิตี้โปรแกรมรวบรวมข้อมูลเว็บและอุปกรณ์อื่น ๆ อีกมากมายหลายคนคิดว่าGETจะไม่เป็นการกระทำที่ทำลายล้าง (ถูกต้องเนื่องจากถูกระบุด้วยวิธีนี้) หากคุณทำลายแอปพลิเคชันของคุณโดยการทำลายข้อกำหนดดังกล่าวคุณจะได้รับการรักษาทั้งสองส่วนของแอปพลิเคชันของคุณ
Joachim Sauer

7
@NimChimpsky: GETมันไม่ได้รับการเปลี่ยนแปลงโดย คำแนะนำนั้นผิด ปลอดภัยหมายถึงผู้ใช้ไม่สามารถรับผิดชอบต่อผลข้างเคียงได้ไม่ว่าจะไม่มีผลข้างเคียง ไม่เช่นนั้นคุณจะไม่มีไฟล์บันทึกสำหรับเซิร์ฟเวอร์ของคุณซึ่งจะไร้สาระ! มีการสะกดคำออกมาค่อนข้างชัดเจนในหัวข้อ 9.1.1 ของ RFC2616
Jörg W Mittag

8
@ JörgWMittag: ฉันจะไม่พูดว่า "แค่ผิด" ฉันจะพูดว่า "ใช้คำไม่ถูกต้อง" GET ไม่ควรเปลี่ยนแปลงตามเป้าหมาย แน่นอนว่าคุณได้รับอนุญาตให้นับบันทึกและปฏิบัติตามคำขอของ GET แต่ไม่ควรแก้ไขข้อมูลธุรกิจจริงของคุณ
Joachim Sauer

23
@NimChimpsky A GETไม่ควรเปลี่ยนทรัพยากรที่ขอโดยGETแต่นั่นไม่ได้หมายความว่า 'ไม่มีอะไรบนเซิร์ฟเวอร์ควรเปลี่ยน' สิ่งที่แน่นอนเช่นบันทึกเคาน์เตอร์และสถานะเซิร์ฟเวอร์อื่น ๆ อาจเปลี่ยนแปลงได้ในระหว่างการร้องขอใด ๆ
Eric King

8
เมื่อหลายปีก่อน Google เปิดตัว Add-on ของเบราว์เซอร์ (iirc) ที่จะดึงข้อมูลหน้าเว็บล่วงหน้าผ่านลิงก์ สิ่งนี้เกิดขึ้นในแผงควบคุมบางตัวที่ออกแบบมาไม่ดี - URL จะทำให้เกิดการบันทึกหรือบางสิ่งที่จะเขียนหรือลบบนเซิร์ฟเวอร์ (คิดว่า post? action = delete) สิ่งนี้ทำให้เกิดการกระทำที่จะดำเนินการโดยที่ผู้ใช้ไม่ทราบ Google ได้ยกเลิก addon นั้นด้วยเหตุผลดังกล่าว iirc แม้ว่ามันจะเป็นความผิดของผู้ผลิตเว็บแอปที่ใช้ GETs เพื่อเปลี่ยนสถานะ
คธูลู

24

กริยา HTTP แต่ละตัวมีความรับผิดชอบของตัวเอง ตัวอย่างเช่นGETตามที่กำหนดโดยRFC

หมายถึงการดึงข้อมูลใด ๆ (ในรูปแบบของเอนทิตี) ถูกระบุโดย Request-URI

POSTในทางกลับกันหมายถึงการแทรกอย่างเป็นทางการ

เมธอด POST ใช้เพื่อร้องขอให้เซิร์ฟเวอร์ต้นทางยอมรับ
เอนทิตีที่อยู่ในคำขอเป็นผู้ใต้บังคับบัญชาใหม่ของทรัพยากรที่
ระบุโดย Request-URI ใน Request-Line

เหตุผลที่ทำให้มันเป็นแบบนี้:

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

เพื่อความสมบูรณ์และเพื่อบังคับใช้การใช้งานที่ถูกต้อง(แหล่งที่มา) :

  • GETพารามิเตอร์จะถูกส่งผ่านเป็นส่วนหนึ่งของ URL ซึ่งมีความยาวขนาดเล็กและ จำกัด 256 ตัวอักษรโดยค่าเริ่มต้นโดยเซิร์ฟเวอร์บางตัวรองรับ 4000+ ตัวอักษร หากคุณต้องการแทรกบันทึกที่ยาวก็ไม่มีวิธีที่ถูกต้องในการส่งผ่านข้อมูลนี้
  • เมื่อใช้การรักษาความปลอดภัยการเชื่อมต่อ̶เช่น TLS, ̶ URL ไม่ได้รับการเข้ารหัส̶ดังนั้นทุกพารามิเตอร์ของ̶ ̶G̶E̶T̶̶จะถูกโอนข้อความธรรมดา URL นั้นถูกเข้ารหัสด้วย TLS ดังนั้น TLS จึงใช้ได้
  • การแทรกข้อมูลไบนารีหรืออักขระที่ไม่ใช่ ASCII โดยใช้GETไม่ได้ผล
  • GET ถูกดำเนินการอีกครั้งหากผู้ใช้กดปุ่มย้อนกลับในเบราว์เซอร์
  • ซอฟต์แวร์รวบรวมข้อมูลที่เก่ากว่าบางรายการไม่สามารถสร้างดัชนี URL ด้วยการ?ลงชื่อเข้าใช้

1
คุณแน่ใจหรือไม่ว่า URL นั้นไม่ได้เข้ารหัสผ่าน TLS ฉันรู้สึกว่า SSL / TLS handshakes เกิดขึ้นก่อนที่จะมีการถ่ายโอนส่วนหัว HTTP นี่คือเหตุผลว่าทำไมการโฮสต์เว็บไซต์ HTTPS เสมือนผ่านที่อยู่ IP เดียวนั้นยาก ฉันเข้าใจผิด
Brandon

ถูกต้องฉันซ่อมมัน
oleksii

2
@Brandon เบราว์เซอร์ที่ทันสมัยส่งโดเมนโฮสต์ในที่ชัดเจนเป็นส่วนหนึ่งของการจับมือ TLS (รู้จักกันในชื่อเซิร์ฟเวอร์) เพื่ออนุญาตให้โฮสต์มากกว่าหนึ่งโดเมนต่อที่อยู่ IP ส่วนเส้นทาง / แบบสอบถามของ URL ได้รับการคุ้มครองโดย TLS ไม่มีความแตกต่างระหว่าง GET และกริยา HTTP อื่น ๆ ในเรื่องนั้น
CodesInChaos

9

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

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

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


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

8

ถ้าฉันสร้างบริการ php ซึ่งแทรกข้อมูลในฐานข้อมูลและส่งผ่านพารามิเตอร์ในสตริงการสืบค้น GET ทำไมจึงเป็นเช่นนั้น

คำตอบที่ง่ายที่สุดคือ "เพราะนั่นไม่ใช่สิ่งที่GETหมายถึง"

การใช้GETเพื่อส่งผ่านข้อมูลสำหรับการอัปเดตก็เหมือนกับการเขียนจดหมายรักและส่งไปในซองจดหมายที่มีเครื่องหมาย "ข้อเสนอพิเศษ - ACT NOW!" ในทั้งสองกรณีที่คุณไม่ควรแปลกใจผู้รับและ / หรือตัวกลางผิดข้อความของคุณ


5

สำหรับการดำเนินงานCRUDของคุณในแอปพลิเคชันที่ใช้ฐานข้อมูลเป็นศูนย์กลางให้ใช้สคีมาต่อไปนี้:

ใช้ HTTP GET สำหรับการดำเนินการอ่าน (SQL SELECT)

ใช้ HTTP PUT สำหรับการดำเนินการอัพเดท (SQL UPDATE)

ใช้ HTTP POST สำหรับสร้างการปฏิบัติการ (SQL INSERT)

ใช้ HTTP DELETE เพื่อลบการดำเนินการ (SQL DELETE)


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

HTTP PUT ไม่เหมือนกับ SQL DELETE และ INSERT มากกว่า UPDATE ใช่ไหม นอกจากนี้ SQL UPDATE ยังสามารถอัพเดตหลาย ๆ ระเบียนพร้อมกัน แต่ HTTP PUT จะอัปเดตสิ่งเดียวเท่านั้น
Backwards_Dave

0

GET ไม่ควรเปลี่ยนแปลงข้อมูลบนเซิร์ฟเวอร์ - ใช้คำขอ POST สำหรับสิ่งนั้น

คำแนะนำนั้นและคำตอบทั้งหมดที่นี่ผิด เห็นได้ชัดว่าฉันเป็นละครมากเกินไปคำตอบอื่น ๆ ที่ยอดเยี่ยม แต่ฉันเชื่อว่าคำแนะนำที่แน่นอนควรได้รับเป็น:

ได้รับควรจะไม่ค่อยเปลี่ยนแปลงข้อมูลบนเซิร์ฟเวอร์ใช้คำขอ POST ที่

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

เห็นได้ชัดว่าเป็นกรณีที่มีขอบ แต่ก็น่าสังเกตอย่างแน่นอน


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

@CodesInChaos - จุดที่ยอดเยี่ยม! ฉันเห็นด้วยกับคุณ. ฉันได้ลบตัวอย่างการยกเลิกการสมัครและทำให้การยืนยันอีเมลเป็นเพียงตัวอย่างเดียว อาจมีคนอื่นนอกเหนือจากการยืนยันอีเมลที่ GET ใช้งานได้ แต่ฉันไม่สามารถนึกถึงอะไรได้ในขณะนี้
TTT

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

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