Trực tiếp sửa đổi các siêu sao


20

Tôi đã thấy những người (thường viết mã tốt) trực tiếp thay đổi $_POSTmảng bằng mã như thế này:

// 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.

Điều này có nghĩa là update_record()không nên truy cập trực tiếp $ _POST, vì vậy chúng ta có thể chuyển các mảng dữ liệu khác cho nó, nhưng chắc chắn đây là sự lười biếng, thiết kế tồi hoặc có thể chỉ là sai? Tuy nhiên, chúng tôi vẫn đang truyền một mảng hợp lệ cho update_record(), vậy tại sao tạo một mảng mới?

Đây không phải là điểm của câu hỏi, chỉ là một ví dụ về việc sử dụng. Tuy nhiên, tôi đã nghe nhiều người nói rằng điều này không nên được thực hiện với $_REQUESTdữ liệu và đó là thực tế tồi. Nhưng tại sao? Trông đủ vô hại.

Ví dụ:

  • Đặt $_GETgiá trị mặc định (hoặc bài đăng) không thực sự tồn tại

  • Thêm $_POSTcác giá trị không thực sự được đăng sau khi gửi biểu mẫu

  • Vệ sinh trực tiếp hoặc lọc các $_GETgiá trị mảng hoặc khóa từ rất sớm trong tập lệnh (vệ sinh dự phòng ... tại sao không?)

  • Đặt $_POSTgiá trị theo cách thủ công trước khi gửi biểu mẫu để nhập dữ liệu đầu vào với giá trị mặc định (khi đầu vào đọc $_POSTgiá trị mặc định; tôi đã thực hiện việc này)

  • Tạo nên $_SERVERgiá trị của riêng bạn ? Chắc chắn, tại sao không?

  • Còn những người khác, thích $_COOKIE$_SESSIONthì sao? Tất nhiên chúng ta phải sửa đổi trực tiếp phải không? Vậy thì tại sao không phải là những người khác?

Nên chỉ đạo sửa đổi superglobals không bao giờ được thực hiện, hoặc là nó OK để làm trong một số trường hợp?


Tôi đồng ý với # 1, # 2 và # 3 vì đây là cách sử dụng không mong muốn (đặc biệt là # 1 và # 2).
Kevin Peno

Câu hỏi hay. Sửa đổi mảng toàn cầu là sai theo cùng một cách sử dụng các giá trị toàn cầu là sai. Ngoài ra các mảng này có mục đích của chúng (truyền tham số từ bên ngoài) làm cho việc thay đổi chúng trở thành một cách đơn giản để gây rối trong mã. Nhưng, tôi tin rằng một số mảng này có thể được khử trùng khi bắt đầu tập lệnh, chỉ để không gây ra vấn đề trong mã.
Tadeck

1
Tôi đang sử dụng các trình bao bọc mảng đầu vào OO (lọc ngầm), in một thông báo bổ sung khi các biến $ _GET hoặc $ _POST bị giả mạo. Vẫn có thể, nhưng nên hạn chế trong các tình huống hẹp. (Tín hiệu mô-đun chéo, mặc dù chỉ có bộ điều phối / bộ điều khiển phía trước mới cần.)
mario

@mario: Tôi rất muốn nghe thêm về cách bạn đã hoàn thành điều đó nếu bạn có thể xem câu hỏi này: stackoverflow.com/questions/12954656
Wesley Murch

Câu trả lời:


16

Vì PHP đã thiết lập các siêu sao đó, tôi không nghĩ việc sửa đổi chúng là xấu . Trong một số trường hợp, đó có thể là cách tốt nhất để giải quyết vấn đề ... đặc biệt khi xử lý mã bên thứ ba mà bạn không thể dễ dàng sửa đổi. (Họ có thể sử dụng $_GETtrực tiếp hoặc giả sử một số khóa tồn tại $_SERVER, v.v.)

Tuy nhiên, nói chung, tôi nghĩ rằng đó là một thực tế xấu khi bạn đang viết mã của riêng bạn. Sửa đổi $_REQUESTdữ liệu với một số bộ lọc hậu trường chạy tự động trên mỗi trang có khả năng giới thiệu các tác dụng phụ. (Xem tất cả các vấn đề mà "trích dẫn ma thuật" gây ra để chứng minh.)

Vì vậy, nếu bạn sẽ không làm điều đó (tự động lọc các siêu lớp), thì những điều sau đây không mang lại cho bạn bất kỳ lợi ích nào:

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

khi bạn có thể dễ dàng làm:

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

Tôi nghĩ rõ ràng hơn nhiều để phân biệt toàn bộ trang web $_POST$_GETđược luôn không lọc, dữ liệu không tin cậy, và họ nên không bao giờ được sử dụng như nó vốn có.

Bằng cách sao chép giá trị được lọc sang một biến khác, bạn đang đưa ra tuyên bố rằng: "Tôi hiểu những gì tôi đang làm ... Tôi đã lọc đầu vào này và nó an toàn để sử dụng."


Cảm ơn về đầu vào, internet của tôi đã ra được gần 2 ngày nên tôi không có cơ hội trả lời bất cứ ai. Trong ví dụ này, tôi đã sửa đổi $ _POST và sử dụng nó như một mảng dữ liệu để chuyển đến một chức năng cập nhật, giả sử có một số khóa $ _POST khác mà chúng ta sẽ đọc trong chức năng đó. Tôi muốn tạo ra một mảng mới nhưng tôi đã thấy mọi người thay vào đó, vì vậy tôi vẫn không chắc chắn có nên gọi nó là "mã xấu" hay không, nhưng tôi nghĩ rằng ít nhất là nó nghiêng về phía đó. Tôi nghĩ bất cứ lúc nào bạn sẽ cảm thấy cần phải làm điều này, luôn có một cách tốt hơn.
Wesley Murch

1
@Wesley, lý do chính khiến nó "xấu" là vì nó khiến bạn quên đi việc vệ sinh một số dữ liệu người dùng. Trong ví dụ của bạn, bạn sửa đổi một khóa sau đó chuyển toàn bộ mảng. Điều gì xảy ra nếu một số dữ liệu đó chứa đầu vào độc hại không được xử lý? Tốt hơn nhiều là xây dựng mảng mới đó bằng tay, chỉ sao chép những thứ bạn cần từ $_POSTđó, vệ sinh khi bạn đi cùng. Và liên quan đến những người khác làm điều này ... tốt, rất nhiều người viết mã PHP rất tệ, nhưng đó cũng không phải là lý do để bạn làm điều đó. :)
konforce

Tôi nghĩ rằng tôi nên nhấn mạnh các ứng dụng khác của lạm dụng siêu lớp ngoài việc vệ sinh dữ liệu. Tôi có lẽ nên bỏ nó hoàn toàn và viết một câu hỏi rõ ràng hơn, mọi người đặc biệt thích chọn các khía cạnh bảo mật và thường bỏ qua phần còn lại. Không phải là vô duyên, tôi thực sự đánh giá cao thông tin phản hồi. Tôi sẽ đợi một ngày và đánh dấu kiểm này, vì bạn đã giải quyết một số điểm tốt nhưng đã bỏ lỡ bữa tiệc bỏ phiếu trong 3 phút khi câu hỏi mới :) Cảm ơn một lần nữa!
Wesley Murch

9

Nói chung, tôi khuyên bạn không nên sửa đổi các siêu toàn cầu được xác định trước để nó rõ ràng dữ liệu được khử trùng là gì và dữ liệu thô / không tin cậy là gì.

Những người khác có thể đề nghị rằng nếu bạn dọn sạch các siêu sao khi bắt đầu chu kỳ yêu cầu thì bạn không cần phải lo lắng về chúng ở nơi khác.

Tôi sẽ luôn kết hợp chúng khi bạn cần:

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

hoặc tương tự.

Trong điều kiện của các biến khác đó là thực hành tốt để không viết bất kỳ $_GET, $_POST, $_REQUEST, $_SERVERhoặc $_COOKIE. $_SESSIONtuy nhiên thì khác bởi vì bạn thường muốn ghi dữ liệu vào phiên mà sau đó vẫn tồn tại trên các yêu cầu khác nhau trong phiên.


2
Thêm lý do tại sao các loại hình cầu này sẽ biến mất bằng các đối tượng / phương thức có thể được gọi để lấy chúng khi cần thiết. Tại sao setcookietồn tại, nhưng chúng tôi nhận được cookie thông qua $_COOKIE? Ngoài ra, vì $_COOKIEchỉ được đặt khi phiên hiện tại bắt đầu và không bao giờ được cập nhật, nên yêu cầu bạn thay đổi / đặt cookie ở cả hai khu vực để các khu vực sau của mã có thông tin cập nhật.
Kevin Peno

Cảm ơn James, tôi đã ngoại tuyến được một lúc nên tôi không thể phản hồi. Để làm cho một câu chuyện dài ngắn - tôi đồng ý với bạn. Luôn có một giải pháp tốt hơn là viết để đăng / nhận / v.v., nhưng tôi vẫn không chắc liệu đó có phải là một ý tưởng tồi tệ hay không, như trong " không bao giờ làm điều này bao giờ ". Vì vậy, nếu tôi gặp lại loại mã này một lần nữa, bạn có nghĩ rằng tôi có quyền "gọi chúng ra" bằng mã cẩu thả, hoặc đôi khi có thể sử dụng mã này một cách thông minh, an toàn không?
Wesley Murch

@Wesley Nếu nó là "không bao giờ làm điều này bao giờ" thì các siêu sao có lẽ chỉ được đọc một cách nghiêm ngặt - chúng không phải vậy. Tôi chỉ gọi đó là thực tiễn xấu để đặt hoặc ghi đè chúng trong mã ứng dụng của bạn - vì những lý do đã nói.
Michel Feldheim

3

Bạn nên tránh nó. Có thể đôi khi bạn quên vệ sinh một cái gì đó, sau đó bạn có thể lấy dữ liệu nguy hiểm. Nếu bạn sao chép dữ liệu vào cấu trúc mới trong khi vệ sinh

  • Bạn chỉ nhận được, những gì bạn muốn / cần chứ không phải những gì trong $_POST quá
  • Bạn có thể sẽ gặp lỗi, nếu mảng vừa tạo bị thiếu một số khóa hoặc hoàn toàn không có

Các tập lệnh khác có thể giả định rằng mảng không bị ảnh hưởng và có thể gây phản ứng tò mò.


2

Tôi chưa bao giờ thích ý tưởng sửa đổi superglobal vì nó sai lệch. Đó là một cách nhanh chóng để làm một cái gì đó mà gần như sẽ có một cách tốt hơn để làm.

Nếu bạn thay đổi giá trị của $_POST, chẳng hạn, thì bạn đang nói rằng phần mềm đã nhận được dữ liệu mà nó không có.

VẤN ĐỀ THỰC SỰ

Có một tình huống thực tế trong cuộc sống khi điều này trở thành một vấn đề lớn:

Hãy tưởng tượng bạn đang làm việc trong một nhóm. Trong một thế giới lý tưởng, mọi người đều sử dụng cùng một cú pháp, nhưng chúng ta không sống trong một thế giới lý tưởng. Một nhà phát triển, John, thích truy cập dữ liệu được đăng bằng cách sử dụng $_POST. Anh ấy thay đổi một cái gì đó trong bài vars:

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

Sau đó, bạn có một nhà phát triển khác, Chris, người thích sử dụng filter_inputđể truy cập dữ liệu đã nhập (tức là GET, POST, SERVER, COOKIE) để bảo vệ phần mềm khi xử lý dữ liệu mà người dùng có thể giả mạo. Trong phần mềm của mình, anh ta cần lấy giá trị bài đăng ranking. Phần mã của anh ta là SAU John's.

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

Từ ví dụ trên, bằng cách thay đổi một siêu lớp, bạn đã phá vỡ PHP. John đã đặt giá trị là $_POST['ranking']2 vì bất kỳ lý do gì, nhưng bây giờ Chris đã nhận được giá trị là 1

Khi tôi thấy không có cách nào khác để làm điều đó:

Tôi đã làm việc trong một dự án sử dụng wordpress làm blog của nó đằng sau một bộ cân bằng tải AWS. Điều này thay đổi giá trị của $_SERVER['remote_address']. Trong trường hợp này, nhà phát triển khác không có lựa chọn nào khác ngoài việc thực hiện như sau:

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

Phần kết luận

Gần như chắc chắn là một cách tốt hơn là thay đổi các siêu sao


1

Tôi nghĩ rằng câu hỏi thực sự ở đây là tại sao bạn nên sửa đổi chủ đề? Tôi không thấy bất kỳ lý do hợp lệ để làm như vậy. Nếu bạn cần vệ sinh một mục tiêu, bạn có thể muốn sử dụng một biến cục bộ

Trừ khi mã của bạn đủ ngắn (giả sử, dài dưới 50 dòng), sửa đổi những siêu toàn cầu đó sẽ chỉ làm cho mã của bạn khó duy trì hơn và bị thiếu.

Bằng cách này, bạn không cần chuyển $ _POST cho hàm, vì đó là một mảng siêu lớp có thể được truy cập ngay cả trong phạm vi cục bộ của hàm.


3
Nhưng anh nên vượt qua nó. Khác rất khó kiểm tra và không thể gọi hàm / phương thức với các giá trị khác mà không có bất kỳ hack nào (thậm chí xấu hơn)
KingCrunch

Vâng, nó phụ thuộc vào những gì phương pháp của mình làm. Nếu nó được thiết kế chỉ để phân tích bất cứ thứ gì có trong mảng $ _POST, anh ta không cần phải vượt qua nó. Tất nhiên, nếu nó phục vụ một mục đích chung / trừu tượng hơn, bạn đúng.

2
@Thomas, tôi đồng ý với King ở đây. Ngay cả khi chúng là toàn cầu, bạn không nên sử dụng bất kỳ thứ gì trong phạm vi khác vì nó gây ra sự khớp nối chặt chẽ (đó là lý do tại sao chức năng này không thể được sử dụng lại). Cho ví dụ của bạn, nếu chức năng là vệ sinh dữ liệu, tại sao nó chỉ vệ sinh $_POSTdữ liệu? Truyền $_POSTvào làm cho chức năng vệ sinh bất kỳ dữ liệu.
Kevin Peno

0

Sau khi ban đầu trả lời câu hỏi này bằng cách nói rằng sẽ không có lý do gì để sửa đổi các siêu sao, tôi đang chỉnh sửa câu trả lời này bằng một ví dụ về thời điểm tôi quyết định.

Tôi hiện đang làm việc trên một bảng cơ sở dữ liệu viết lại URL, theo đó requestcột hướng người dùng đến targetcột tương ứng .

Ví dụ, một requestcó thể blog/title-herevà nó targetcó thể làblog.php?id=1 .

blog.phpđang mong đợi $_GETcác biến và tôi không muốn thay đổi header("Location:"), nên tôi sẽ làm một cái gì đó như thế này:

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

Điều này tạo ra một $_GETmảng chứa các tham số dự định được truyền bởitarget cột.

Cuối cùng, tôi sẽ khuyên bạn không nên sửa đổi các siêu sao trừ khi bạn thực sự phải làm .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.