首頁 php教程 PHP开发 getimagesize 函數不是完全可靠的

getimagesize 函數不是完全可靠的

Dec 28, 2016 pm 01:04 PM

getimagesize 函數並不屬於 GD 擴充功能的部分,標準安裝的 PHP 都可以使用這個函數。可以先看看這個函數的文檔說明:http://php.net/manual/zh/function.getimagesize.php

如果指定的檔案如果不是有效的映像,會傳回false,回傳資料中也有表示文檔類型的字段。如果不用來獲取文件的大小而是使用它來判斷上傳文件是否是圖片文件,看起來似乎是個很不錯的方案,當然這需要屏蔽掉可能產生的警告,比如代碼這樣寫:

<?php
$filesize = @getimagesize(&#39;/path/to/image.png&#39;);
if ($filesize) {
    do_upload();
}
# 另外需要注意的是,你不可以像下面这样写:
# if ($filesize[2] == 0)
# 因为 $filesize[2] 可能是 1 到 16 之间的整数,但却绝对不对是0。
登入後複製

但是如果你只是做了這樣的驗證,那麼很不幸,你成功的在程式碼裡種下了一個webshel​​l 的隱患。

要分析這個問題,我們先來看一下這個函數的原型:

static void php_getimagesize_from_stream(php_stream *stream, zval **info, INTERNAL_FUNCTION_PARAMETERS)
{
    ...
    itype = php_getimagetype(stream, NULL TSRMLS_CC);
    switch( itype) {
        ...
    }
    ...
}

static void php_getimagesize_from_any(INTERNAL_FUNCTION_PARAMETERS, int mode) {
    ...
    php_getimagesize_from_stream(stream, info, INTERNAL_FUNCTION_PARAM_PASSTHRU);
    php_stream_close(stream);
}

PHP_FUNCTION(getimagesize)
{
    php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_PATH);
}
登入後複製

限於篇幅上面隱藏了一些細節,現在從上面的程式碼中我們知道兩件事情就夠了:

最終處理的函數是php_getimagesize_from_stream

負責判斷文件類型的函數是 php_getimagetype

接下來看一下 php_getimagetype 的實作:

PHPAPI int php_getimagetype(php_stream * stream, char *filetype TSRMLS_DC)
{
    ...
    if (!memcmp(filetype, php_sig_gif, 3)) {
        return IMAGE_FILETYPE_GIF;
    } else if (!memcmp(filetype, php_sig_jpg, 3)) {
        return IMAGE_FILETYPE_JPEG;
    } else if (!memcmp(filetype, php_sig_png, 3)) {
        ...
    }
}
登入後複製

去掉了一些細節,php_sig_mm 文件流的前幾個位元組(文件頭)來判斷的。那麼既然如此,我們可不可以建構一個特殊的 PHP 檔案來繞過這個判斷呢?不如來嘗試一下。

找一個十六進位編輯器來寫一個的PHP 語句,例如:

PHPAPI const char php_sig_gif[3] = {&#39;G&#39;, &#39;I&#39;, &#39;F&#39;};
...
PHPAPI const char php_sig_png[8] = {(char) 0x89, (char) 0x50, (char) 0x4e, (char) 0x47,
                                    (char) 0x0d, (char) 0x0a, (char) 0x1a, (char) 0x0a};
登入後複製

這幾個字符的十六進位編碼(UTF-8)是這樣的:

<?php phpinfo(); ?>
登入後複製

我們構造一下,把PNG檔案的頭字節加在前面變成這樣的:

3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E
登入後複製

最後保存成 .php 後綴的檔案(注意上面是檔案的十六進位值),例如test.php。執行 php test.php 你會發現完全可以執行成功。那麼能用 getimagesize 讀取它的文件資訊嗎?新建一個文件寫入程式碼試試看:

8950 4E47 0D0A 1A0A 3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E
登入後複製

執行結果:

<?php
print_r(getimagesize(&#39;test.php&#39;));
登入後複製

成功讀取出來,並且文件也被正常識別為 PNG 文件,雖然寬和高的值都大的有點離譜。

現在你應該明白為什麼上文說這裡留下了一個 webshel​​l 的隱患的吧。如果這裡只有這樣的上傳判斷,而且上傳之後的檔案是可以存取的,就可以透過這個入口注入任意程式碼執行了。

那為什麼上面的檔案可以 PHP 是可以正常執行的呢?用 token_get_all 函數來看這個檔案:

Array
(
    [0] => 1885957734
    [1] => 1864902971
    [2] => 3
    [3] => width="1885957734" height="1864902971"
    [bits] => 32
    [mime] => image/png
)
登入後複製

如果顯示正常的話你能看到輸出陣列的第一個元素的解析器代號是312,透過 token_name 取得到的名稱會是T_INLINE_HTML,也就是說檔案頭部的資訊被當成正常的內嵌的HTML 程式碼被忽略掉了。

至於為什麼會有一個大的離譜的寬和高,看一下 php_handle_png 函數的實作就能知道,這些資訊也是透過讀取特定的檔案頭的位元來取得的。

所以,對於正常的圖片文件,getimagesize 完全可以勝任,但是對於一些有心構造的文件結構卻不行。

在處理使用者上傳的檔案時,先簡單粗暴的判斷檔案副檔名並對檔案名稱做一下處理,保證伺服器上不是 php 檔案都不能直接執行也是一種有效的方式。然後可以使用 getimagesize 做一些輔助處理。

 以上就是getimagesize 函數不是完全可靠的的內容,更多相關內容請關注PHP中文網(www.php.cn)!


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1322
25
PHP教程
1270
29
C# 教程
1250
24