แก้ไข superglobals โดยตรง


20

ฉันเคยเห็นผู้คน (ซึ่งมักเขียนโค้ดที่ดี) แก้ไข$_POSTอาเรย์ด้วยรหัสดังนี้:

// Add some value that wasn't actually posted
$_POST['last_activity'] = time();

// Alter an existing post value
$_POST['name'] = trim($_POST['name']);

// Our pretend function
// Pass the entire $_POST array as data to work with in the function
// The function update_record() will read only the values we actually need
update_record($_POST);

// ...That sure was easier than creating a new array 
//  with only the $_POST values we actually need.

มันสมเหตุสมผลที่update_record()ไม่ควรเข้าถึง $ _POST โดยตรงดังนั้นเราจึงสามารถส่งผ่านอาร์เรย์ของข้อมูลอื่น ๆ ได้ แต่แน่นอนว่านี่คือการขี้เกียจการออกแบบที่ไม่ดีหรืออาจผิด อย่างไรก็ตามเรายังคงผ่านอาร์เรย์ที่ถูกต้องไปupdate_record()ทำไมสร้างใหม่

นี่ไม่ใช่ประเด็นของคำถามเป็นเพียงตัวอย่างของการใช้งาน อย่างไรก็ตามฉันได้ยินผู้คนมากมายพูดว่าสิ่งนี้ไม่ควรทำกับ$_REQUESTข้อมูลและเป็นการฝึกฝนที่ไม่ดี แต่ทำไม ดูไม่เป็นอันตรายมากพอ

ตัวอย่าง:

  • การตั้งค่าเริ่มต้น$_GET(หรือโพสต์) ค่าที่ไม่มีอยู่จริง

  • การเพิ่ม$_POSTค่าที่ไม่ได้โพสต์จริงๆหลังจากส่งแบบฟอร์ม

  • ฆ่าเชื้อโดยตรงหรือกรอง$_GETค่าหรือคีย์ของอาร์เรย์ในสคริปต์ (การสุขาภิบาลทางเลือก ... ทำไมไม่?)

  • การตั้ง$_POSTค่าด้วยตนเองก่อนส่งแบบฟอร์มเพื่อใส่ข้อมูลด้วยค่าเริ่มต้น (เมื่ออินพุตอ่าน$_POSTว่าเป็นค่าเริ่มต้นฉันได้ทำสิ่งนี้แล้ว)

  • สร้างค่าของคุณเอง$_SERVER? แน่นอนว่าทำไมไม่

  • แล้วคนอื่น ๆ เป็นอย่างไร$_COOKIEและ$_SESSION? แน่นอนว่าเราต้องแก้ไขสิ่งเหล่านั้นโดยตรงใช่มั้ย ถ้าอย่างนั้นทำไมคนอื่นไม่ล่ะ

ควรนำการเปลี่ยนแปลงของsuperglobals ไม่เคยทำได้หรือมันตกลงที่จะทำในบางกรณี?


ฉันเห็นด้วยกับ # 1, # 2 และ # 3 เพราะเป็นการใช้งานที่ไม่คาดคิด (โดยเฉพาะ # 1 และ # 2)
Kevin Peno

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

1
ฉันใช้เครื่องห่อหุ้มอาร์เรย์อินพุต OO (การกรองโดยนัย) ซึ่งพิมพ์คำเตือนเพิ่มเติมเมื่อตัวแปร $ _GET หรือ $ _POST ได้รับการดัดแปลง มันยังคงเป็นไปได้ แต่ควรถูก จำกัด ให้แคบลง (การส่งสัญญาณข้ามโมดูลแม้ว่าจะมีเพียงตัวแจกจ่าย / ตัวควบคุมด้านหน้าเท่านั้นที่จำเป็น)
mario

@mario: ฉันชอบที่จะได้ยินเพิ่มเติมเกี่ยวกับวิธีการที่คุณประสบความสำเร็จว่าถ้าคุณสามารถดูคำถามนี้: stackoverflow.com/questions/12954656
Wesley Murch

คำตอบ:


16

เนื่องจาก PHP นั้นตั้งค่า superglobals เหล่านั้นอยู่แล้วฉันไม่คิดว่ามันจะเลวร้ายถ้าจะแก้ไขมัน ในบางกรณีอาจเป็นวิธีที่ดีที่สุดในการแก้ปัญหา ... โดยเฉพาะอย่างยิ่งเมื่อต้องรับมือกับรหัสบุคคลที่สามที่คุณไม่สามารถแก้ไขได้อย่างง่ายดาย (พวกเขาอาจใช้$_GETโดยตรงหรือสมมติว่ามีบางคีย์อยู่$_SERVERเป็นต้น)

อย่างไรก็ตามโดยทั่วไปฉันคิดว่ามันเป็นการปฏิบัติที่ไม่ดีเมื่อคุณเขียนโค้ดของคุณเอง การแก้ไข$_REQUESTข้อมูลด้วยตัวกรองเบื้องหลังที่ทำงานบนทุกหน้าโดยอัตโนมัติน่าจะมีผลข้างเคียง (ดูปัญหาทั้งหมดที่เกิดจาก "คำพูดวิเศษ" เพื่อพิสูจน์)

ดังนั้นหากคุณจะไม่ทำเช่นนั้น (กรองซูเปอร์บล็อกส์โดยอัตโนมัติ) ดังนั้นสิ่งต่อไปนี้จะไม่ให้ประโยชน์ใด ๆ แก่คุณ:

$_POST['foo'] = filter($_POST['foo']);

เมื่อคุณสามารถทำได้อย่างง่ายดายเพียง:

$foo = filter($_POST['foo']);

ฉันคิดว่ามันชัดเจนกว่าที่จะทำให้ความแตกต่างทั่วทั้งไซต์นั้น$_POSTและ$_GETมีการกรองข้อมูลที่ไม่น่าเชื่อถือและไม่ถูกกรองอยู่เสมอและพวกเขาไม่ควรใช้อย่างที่เคยเป็น

ด้วยการคัดลอกค่าตัวกรองไปยังตัวแปรอื่นคุณกำลังอ้างสิทธิ์ว่า "ฉันเข้าใจสิ่งที่ฉันกำลังทำ ... ฉันได้กรองอินพุตนี้แล้วและปลอดภัยต่อการใช้งาน"


ขอบคุณสำหรับการป้อนข้อมูลอินเทอร์เน็ตของฉันออกมาเกือบ 2 วันแล้วดังนั้นฉันจึงไม่ได้มีโอกาสตอบใคร ในตัวอย่างฉันปรับเปลี่ยน $ _POST และใช้เป็นอาร์เรย์ของข้อมูลเพื่อส่งไปยังฟังก์ชั่นอัปเดตโดยสมมติว่ามีคีย์ $ _POST อื่น ๆ อีกมากมายที่เราจะอ่านในฟังก์ชั่นนั้น ฉันต้องการสร้างอาร์เรย์ใหม่ แต่ฉันได้เห็นคนทำเช่นนี้ดังนั้นฉันยังไม่แน่ใจว่าจะเรียกมันว่า "รหัสไม่ดี" หรือไม่ แต่ฉันคิดว่าอย่างน้อยก็พิงกัน ฉันคิดว่าทุกครั้งที่คุณรู้สึกว่าจำเป็นต้องทำสิ่งนี้มีวิธีที่ดีกว่าเสมอ
Wesley Murch

1
@ เวสลีย์สาเหตุหลักที่ทำให้ "แย่" ก็คือมันทำให้มีแนวโน้มที่คุณจะลืมข้อมูลผู้ใช้บางส่วน ในตัวอย่างของคุณคุณปรับเปลี่ยนหนึ่งคีย์แล้วส่งผ่านอาร์เรย์ทั้งหมด เกิดอะไรขึ้นถ้าข้อมูลบางอย่างมีการป้อนข้อมูลที่เป็นอันตรายที่ไม่ได้ประมวลผล มันจะดีกว่ามากที่จะสร้างอาร์เรย์ใหม่ขึ้นมาด้วยมือคัดลอกเฉพาะสิ่งที่คุณต้องการจาก$_POSTมันลงไปล้างให้สะอาดตามที่คุณไป และเกี่ยวกับคนอื่นที่ทำสิ่งนี้ ... เอาละหลายคนเขียนโค้ด PHP ที่แย่มาก ๆ แต่นั่นก็ไม่ใช่ข้อแก้ตัวสำหรับคุณเช่นกัน :)
konforce

ฉันคิดว่าฉันควรจะเน้นย้ำถึงแอปพลิเคชั่นอื่น ๆ ของการใช้สิ่งที่ไม่เหมาะสมเป็นพิเศษนอกเหนือจากการฆ่าเชื้อข้อมูล ฉันน่าจะทิ้งมันไว้โดยสิ้นเชิงและเขียนคำถามที่ชัดเจนกว่าคนโดยเฉพาะอย่างยิ่งชอบที่จะหยิบประเด็นด้านความปลอดภัยและมักจะมองข้ามส่วนที่เหลือ เพื่อไม่ให้เนรคุณฉันชอบความคิดเห็น ฉันจะรอวันหนึ่งและทำเครื่องหมายนี้ให้กับคุณเนื่องจากคุณได้รับคะแนนที่ดี แต่พลาดปาร์ตี้โหวตระหว่าง 3 นาทีเมื่อคำถามใหม่ :) ขอบคุณอีกครั้ง!
Wesley Murch

9

โดยทั่วไปฉันจะแนะนำว่าคุณไม่ควรแก้ไข super-globals ที่กำหนดไว้ล่วงหน้าเพื่อให้ชัดเจนว่าข้อมูลใดที่ถูกทำให้สะอาดและข้อมูลดิบ / ไม่น่าเชื่อถือ

คนอื่นอาจแนะนำว่าถ้าคุณล้าง superglobals ในช่วงเริ่มต้นของรอบคำขอคุณไม่ต้องกังวลเกี่ยวกับพวกเขาที่อื่น

ฉันมักจะจับคู่พวกเขาเสมอเมื่อคุณต้องการ:

$id = (int)$_POST['id'];

หรือคล้ายกัน

ในแง่ของตัวแปรอื่น ๆ ก็ปฏิบัติที่ดีที่จะไม่เขียนใด ๆ$_GET, $_POST, $_REQUEST, หรือ$_SERVER อย่างไรก็ตามจะแตกต่างกันเนื่องจากคุณมักจะต้องการเขียนข้อมูลลงในเซสชันที่ยังคงอยู่ในคำขอที่แตกต่างกันในเซสชัน$_COOKIE$_SESSION


2
เหตุผลเพิ่มเติมว่าทำไมรูปทรงกลมเหล่านี้ควรหายไปแทนที่ด้วยวัตถุ / วิธีการที่สามารถเรียกใช้เพื่อให้ได้เมื่อจำเป็น ทำไมไม่setcookieอยู่ แต่เราได้รับคุกกี้ผ่าน$_COOKIE? นอกจากนี้เนื่องจาก$_COOKIEมีการตั้งค่าเฉพาะเมื่อเซสชันปัจจุบันเริ่มต้นและไม่ได้รับการอัปเดตจึงต้องการให้คุณแก้ไข / ตั้งค่าคุกกี้ในทั้งสองด้านเพื่อให้พื้นที่ในภายหลังของรหัสมีข้อมูลที่ทันสมัย
Kevin Peno

ขอบคุณ James ฉันออฟไลน์มาระยะหนึ่งแล้วฉันจึงไม่สามารถตอบสนองได้ เพื่อให้เรื่องสั้นสั้น - ฉันเห็นด้วยกับคุณ มีเสมอเป็นทางออกที่ดีกว่าการเขียนโพสต์ / รับ / ฯลฯ แต่ฉันยังคงไม่แน่ใจว่ามันถือว่าเป็นความคิดที่ดีอย่างเคร่งครัดในขณะที่ " ไม่เคยทำเช่นนี้เคย " ดังนั้นหากฉันเจอรหัสประเภทนี้อีกครั้งคุณคิดว่าฉันมีสิทธิ์ที่จะ "เรียกพวกเขาออก" โดยใช้รหัสเลอะเทอะหรืออาจใช้วิธีนี้ในวิธีที่ฉลาดและปลอดภัยหรือไม่?
Wesley Murch

@Wesley ถ้ามันเป็น "ไม่เคยทำแบบนี้มาก่อน" superglobals น่าจะเป็นแบบอ่านอย่างเดียว - พวกเขาไม่ใช่ ฉันเพิ่งจะเรียกมันว่าการปฏิบัติที่ไม่ดีในการตั้งค่าหรือเขียนทับพวกเขาในรหัสแอปพลิเคชันของคุณ - ด้วยเหตุผลดังกล่าว
Michel Feldheim

3

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

  • คุณจะได้รับสิ่งที่คุณต้องการ / ต้องการและไม่ใช่สิ่งที่อยู่ในนั้น$_POSTด้วย
  • คุณอาจจะได้รับข้อผิดพลาดหากอาร์เรย์ที่สร้างขึ้นใหม่หายไปบางคีย์หรือหายไปเลย

สคริปต์อื่น ๆ เพิ่มเติมอาจคิดว่าอาร์เรย์นั้นไม่มีการแตะต้องและอาจมีปฏิกิริยาแปลก ๆ


2

ฉันไม่เคยชอบความคิดที่จะดัดแปลง superglobal เพราะมันทำให้เข้าใจผิด มันเป็นวิธีที่แฮ็คอย่างรวดเร็วในการทำสิ่งที่มีแนวโน้มว่าจะเป็นวิธีที่ดีกว่าที่จะทำ

$_POSTตัวอย่างเช่นหากคุณเปลี่ยนค่าเป็นคุณจะบอกว่าซอฟต์แวร์ได้รับข้อมูลที่ไม่ได้

ปัญหาที่แท้จริง

มีสถานการณ์ชีวิตจริงที่นี่กลายเป็นปัญหาใหญ่:

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

$_POST['ranking'] = 2; // John has changed ranking from 1 to 2 for whatever reason

จากนั้นคุณก็มีนักพัฒนา Chris คนอื่นที่ต้องการใช้filter_inputเพื่อเข้าถึงข้อมูลที่ได้รับ (เช่น GET, POST, SERVER, COOKIE) เพื่อปกป้องซอฟต์แวร์เมื่อประมวลผลข้อมูลที่ผู้ใช้สามารถแก้ไขได้ rankingในส่วนของเขาของซอฟต์แวร์ที่เขาต้องการที่จะได้รับค่าโพสต์ของ ส่วนหนึ่งของรหัสคือAFTERของ John

$ranking = filter_input(INPUT_POST, 'ranking', FILTER_SANITIZE_NUMBER_INT);
// $ranking = 1

จากตัวอย่างข้างต้นโดยการเปลี่ยน superglobal แสดงว่าคุณใช้งาน PHP ไม่ได้ John ตั้งค่า$_POST['ranking']เป็น 2 ไม่ว่าด้วยเหตุผลใด แต่ตอนนี้ Chris ได้รับค่า 1

เมื่อฉันไม่เห็นวิธีอื่นที่จะทำ:

ฉันทำงานในโครงการที่ใช้ wordpress เป็นบล็อกหลัง AWS load balancer $_SERVER['remote_address']นี้เปลี่ยนค่าของ ในตัวอย่างนี้ผู้พัฒนารายอื่นไม่มีทางเลือกนอกจากต้องทำสิ่งต่อไปนี้:

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $parts[0];
}

ข้อสรุป

มีวิธีที่ดีกว่าในการเปลี่ยน superglobals


1

ฉันคิดว่าคำถามจริงที่นี่คือ "ทำไมคุณควรปรับเปลี่ยนชุดรูปแบบ?" ฉันไม่เห็นเหตุผลที่ถูกต้องที่จะทำเช่นนั้น หากคุณต้องการฆ่าเชื้อ imput คุณอาจต้องการใช้ตัวแปรเฉพาะที่ ...

หากคุณใช้รหัสสั้นพอ (กล่าวคือน้อยกว่า 50 บรรทัด) การแก้ไขซุปเปอร์เปอร์โกลบอลจะทำให้โค้ดของคุณยากขึ้นในการดูแลรักษาและใต้ท้องทะเล

โดยวิธีที่คุณไม่จำเป็นต้องผ่าน $ _POST ไปยังฟังก์ชั่นเนื่องจากมันเป็นอาร์เรย์ superglobal ที่สามารถเข้าถึงได้แม้อยู่ในขอบเขตของฟังก์ชั่น


3
แต่เขาควรผ่านมันไป อื่นมันยากที่จะทดสอบและเป็นไปไม่ได้ที่จะเรียกใช้ฟังก์ชัน / วิธีที่มีค่าอื่น ๆ โดยไม่ต้องมีแฮ็ก (แม้แต่ขี้เหร่)
KingCrunch

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

2
@ โทมัสฉันเห็นด้วยกับกษัตริย์ที่นี่ แม้ว่าพวกเขาจะเป็นสากลคุณไม่ควรใช้สิ่งใดในโลกภายในขอบเขตอื่น ๆ เพราะมันทำให้เกิดการมีเพศสัมพันธ์อย่างแน่นหนา (ซึ่งเป็นเหตุผลที่ฟังก์ชั่นไม่สามารถนำกลับมาใช้) ให้ตัวอย่างของคุณถ้าฟังก์ชั่นคือการฆ่าเชื้อข้อมูลทำไมมันจึงแค่ฆ่าเชื้อ$_POSTข้อมูล? การส่งผ่าน$_POSTทำให้ฟังก์ชั่นฆ่าเชื้อข้อมูลใด ๆ
Kevin Peno

0

หลังจากเริ่มตอบคำถามนี้โดยบอกว่าไม่มีเหตุผลที่จะแก้ไข superglobals ฉันกำลังแก้ไขคำตอบนี้ด้วยตัวอย่างของเวลาที่ฉันตัดสินใจ

ขณะนี้ฉันกำลังทำงานกับตารางฐานข้อมูลเขียน URL ใหม่โดยที่requestคอลัมน์นำผู้ใช้ไปยังtargetคอลัมน์ที่เกี่ยวข้อง

ยกตัวอย่างเช่นrequestอาจจะมีblog/title-hereและอาจจะมีtargetblog.php?id=1

เนื่องจากblog.phpคาดว่าจะมี$_GETตัวแปรและฉันไม่ต้องการเปลี่ยนheader("Location:")ฉันก็เลยทำอะไรแบบนี้:

$uri    = explode('?', $uri_request)[0];
$params = explode('?', $uri_request)[1];
parse_str($params, $_GET);

สิ่งนี้จะสร้าง$_GETอาร์เรย์ที่มีพารามิเตอร์ที่ต้องการผ่านtargetคอลัมน์

ในที่สุดผมจะขอแนะนำให้กับการปรับเปลี่ยน superglobals ถ้าคุณอย่างต้อง

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