[英]How to check for incomplete POST request in PHP
當連接緩慢的遠程Web客戶端無法發送包含multipart/form-data
內容的完整POST請求,但PHP仍使用部分接收的數據填充$_POST
數組時,我遇到了一個問題。 結果, $_POST
數組中的一個值可能不完整,並且可能缺少更多值。 我試圖首先在Apache列表中問同樣的問題,並得到一個答案 ,即Apache不緩沖請求主體,並將其作為巨大的Blob傳遞給PHP模塊。
這是我的示例POST請求:
POST /test.php HTTP/1.0
Connection: close
Content-Length: 10000
Content-Type: multipart/form-data; boundary=ABCDEF
--ABCDEF
Content-Disposition: form-data; name="a"
A
--ABCDEF
您可以看到Content-Length
是10000
個字節,但是我只發送了一個var a=A
PHP腳本是:
<?php print_r($_REQUEST); ?>
Web服務器在剩余的請求中等待大約10秒(但是我什么也不發送),然后返回以下響應:
HTTP/1.1 200 OK
Date: Wed, 27 Nov 2013 19:42:20 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.4-14+deb7u3
Vary: Accept-Encoding
Content-Length: 23
Connection: close
Content-Type: text/html
Array
(
[a] => A
)
所以這是我的問題:如何在PHP中驗證是否完全收到了發帖請求? $_SERVER['CONTENT_LENGTH']
將在請求標頭中顯示10000
,但是有沒有辦法檢查收到的實際內容長度?
我猜想遠程客戶端實際上是帶有HTML頁面的瀏覽器。 否則,請告訴我,我將嘗試調整我的解決方案。
您可以添加字段<input type="hidden" name="complete">
(例如)作為最后一個參數。 在PHP
首先檢查此參數是否從客戶端發送。 如果發送了此參數-您可以確定已獲取全部數據。
現在,我不確定是否必須根據RFC(包括HTML和HTTP)保留參數的順序。 但是我嘗試了一些變化,發現順序確實保持不變。
更好的解決方案是計算(在客戶端)參數的哈希並將其作為另一個參數發送。 因此,您可以絕對確定獲得了所有數據。 但這聽起來很復雜...
據我所知,在將multipart/form-data
用作Content-Type
,無法檢查接收到的內容的大小是否與Content-Length
標頭的值匹配,因為您無法獲取原始內容。
1)如果您可以更改Content-Type
(例如,更改為application/x-www-form-urlencoded
),則可以閱讀php://input
,其中將包含請求的原始內容。 的大小php://input
應該匹配Content-Length
(假設的值Content-Length
是正確的)。 如果存在匹配項,您仍然可以使用$_POST
來獲取已處理的內容(常規發布數據)。 在此處閱讀有關php://input
信息 。
2)或者,您可以在客戶端上序列化數據並將其作為text/plain
發送。 服務器可以按照與上述相同的方式檢查大小。 服務器將需要反序列化接收到的內容才能使用它。 並且,如果客戶端生成序列化數據的哈希值並將其發送到標頭(例如X-Content-Hash
)中,則服務器還可以生成哈希值並檢查其是否與標頭中的哈希值匹配。 您無需檢查哈希,可以100%確保內容正確。
3)如果您無法更改Content-Type
,則需要大小不同的內容來驗證內容。 客戶端可以使用額外的標頭(如X-Form-Data-Fields
)來匯總要發送的內容的字段/鍵/名稱。 然后,服務器可以檢查標題中提到的所有字段是否都存在於內容中。
4)另一個解決方案是讓客戶端將預定義的鍵/值作為內容中的最后一個條目。 就像是:
--boundary
Content-Disposition: form-data; name="_final_field_"
TRUE
--boundary--
服務器可以檢查該字段是否存在於內容中,如果是,則內容必須完整。
更新
當您需要傳遞二進制數據時,您不能使用選項1,但仍然可以使用選項2:
客戶端可以對二進制條目進行base64
編碼,(使用您喜歡的任何技術)序列化數據,生成序列化數據的哈希,將哈希作為標頭發送並將數據作為正文發送。 服務器可以生成接收到的內容的哈希值,檢查標頭中的哈希值(並報告不匹配項),反序列化內容, base64
解碼二進制條目。
與僅使用multipart/form-data
,這需要更多的工作,但是服務器可以100%保證內容與客戶端發送的內容相同。
如果可以將enctype更改為
multipart/form-data-alternate
你可以檢查
strlen(file_get_contents('php://input'))
與
$_SERVER['CONTENT_LENGTH']
在Apache或PHP中,它們可能會受到限制。 我相信Apache對此也有一個配置變量。
這是PHP設置;
php.ini
post_max_size=20M
upload_max_filesize=20M
.htaccess
php_value post_max_size 20M
php_value upload_max_filesize 20M
對於由於連接問題而完全丟失的表單值,您只需檢查是否已設置:
if(isset($_POST['key']){
//value is set
}else{
//connection was interrupted
}
對於大型數據(例如圖像上傳),您可以使用來檢查接收到的文件的大小
$_FILES['key']['size']
一個簡單的解決方案可能是使用JavaScript計算客戶端的文件大小,然后將該值作為表單提交時的隱藏輸入附加到表單中。 您可以使用以下方法在JS中獲取文件大小
var filesize = input.files[0].size;
然后在文件上載時,如果隱藏表單輸入的值與上載的文件大小匹配,則該請求不會被網絡連接問題中斷。
也許您可以檢查一個有效的變量,但不能檢查長度,例如:
// client
$clientVars = array('var1' => 'val1', 'otherVar' => 'some value');
ksort($clientVars); // dictionary sorted
$validVar = md5(implode('', $clientVars));
$values = 'var1=val1&otherVar=some value&validVar=' . $validVar;
httpRequest($url, values);
// server
$validVar = $_POST['validVar'];
unset($_POST['validVar']);
ksort($_POST); // dictionary sorted
if (md5(implode('', $_POST)) == $validVar) {
// completed POST, do something
} else {
// not completed POST, log error and do something
}
我還建議使用hidden
值或像MeNa提到的哈希。 (問題在於某些算法在平台上的實現方式不同,因此js中的CRC32與PHP中的CRC32可能有所不同。但是通過一些測試,您應該能夠找到兼容的算法)
我將建議使用對稱加密,因為它是一個選擇。 (我不認為它比哈希運算要快)。 加密除了提供保密性外,還提供完整性,即。 這是收到的消息嗎?
盡管流密碼非常快,但像AES這樣的分組密碼也可以非常快,但這取決於您的系統,所使用的語言等。
如果您無法解密該消息(或者混亂),則表明該消息不完整。
但是認真地 ,使用哈希。 在客戶端上對POST進行哈希處理,首先在服務器上檢查哈希的長度。 (有些?)哈希是固定長度的,所以如果長度不匹配,那就錯了。 然后對接收到的POST進行哈希處理,並與POST哈希進行比較。 如果在完整的POST上執行操作,則以指定的順序執行(因此,所有重新排序都將被取消),因此開銷最小。
所有這一切,假設您只是無法檢查發布消息以查看是否缺少字段並且is_set == True,長度> 0,!empty()...
我認為您正在尋找的是$ HTTP_RAW_POST_DATA ,這將為您提供實際的 POST長度,然后您可以將其與$ _SERVER ['CONTENT_LENGTH']進行比較。
我認為無法從$ _REQUEST超全局性計算原始內容大小,至少對於多部分/表單數據請求而言。
我將使用所有parameter = value哈希將自定義標頭添加到您的http請求中,以檢查服務器端。 標頭肯定會到達,因此您的哈希標頭始終存在。 確保以相同順序連接參數,否則哈希將不同。 也要注意編碼,客戶端和服務器上必須相同。
如果可以配置Apache,則可以添加一個具有mod_proxy的虛擬主機,該虛擬主機配置為在同一服務器上的另一個虛擬主機上進行代理。 這應該過濾未完成的請求。 請注意,這樣會浪費每個請求2個套接字,因此,如果您考慮采用這種方式,請留意資源使用情況。
如果計算內容長度不合理,則可能需要對客戶端發送的數據進行簽名。
使用javascript,在提交之前以合理合理的方式將表單數據序列化為json字符串或等效形式(即,根據需要對其進行排序)。 使用一個或兩個相當快速的算法(例如crc32,md5,sha1)對該字符串進行哈希處理,並將此額外的哈希數據添加到將作為簽名發送的內容中。
在服務器上,從$ _POST請求中剝離這些多余的哈希數據,然后在PHP中重做相同的工作。 相應地比較哈希值:如果哈希值匹配,則翻譯不會丟失任何內容。 (如果您想消除產生誤報的微小風險,請使用兩個散列。)
我敢打賭,有一種合理的方法可以對文件執行類似的操作,例如,在JS中獲取文件的名稱和大小,並將其他信息添加到要簽名的數據中。
這與某些PHP框架為避免篡改會話數據(在會話數據得到管理並存儲在客戶端Cookie中)所做的工作有關,因此您可能會發現一些易於使用的代碼可以在后者的上下文中執行此操作。
原始答案:
就我所知,發送GET或POST請求或多或少發送的數量之間的區別如下:
GET /script.php?var1=foo&var2=bar
headers
與發送類似的內容:
POST /script.php
headers
var1=foo&var2=bar <— content length is the length of this chunk
因此,對於每個部分,您都可以計算長度,並檢查它與content-length標頭廣告的長度之比。
$_FILES
條目具有一個方便的大小字段,您可以直接使用。 $_POST
數據,重建發送的查詢字符串並計算其長度。 注意點:
var[]=foo&var[]=baz
vs var[0]=foo&var[1]=baz
進一步閱讀:
其他可能有用的解決方案...如果另一端的連接速度很慢,則只需刪除執行該帖子的限制即可。
set_time_limit(0);
而且,您將確保將發送孔桿數據。
這是PHP中的一個已知錯誤,需要在此處進行修復-https: //bugs.php.net/bug.php?id=61471
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.