Ueditor在上傳圖片時,服務器返回502 bad gateway的填坑記

故事發生在2017.10.26下午,環宇同學跟我反應說在測試青羊雙創項目的後臺管理,發現在編輯園區信息時,想要上傳一堆圖片,用編輯器嘗試上傳圖片時,卻報了“上傳失敗,請重試”的錯,類似下圖:

image.png

納尼?我...... 多麼正常的編輯器,我本地跑的好好的,我趕緊去試了一下,果然報錯,WTF......

但是:雖然報錯,當點擊“在線管理”(就是選擇服務器上已存在的文件)的時候,卻發現之前那張圖是傳上去了的,有些個尷尬,馬上和環宇說其實已傳上去了,可以直接在“在線管理”裏先用着...爲了要給客戶弄好內容,他沒有多言,應聲了下來,但很明顯,環宇雖然口頭不說,但心裏肯定在罵“我*”,我想bug還是得改,於是:

解bug第一式:用Chrome瀏覽器,按下F12,打開Network,去上傳一波,然後我就看到這樣的畫面:

image.png

大家都是明白人,其他就不多說了,找到了對應的後端代碼,也看到了返回值,那趕緊找到此這個文件看看代碼再說。

好吧,路徑是個絕對路徑,就是Ueditor的上傳文件的後臺代碼實現(不過是PHP版而已),目錄結構是這樣的:

image.png

此處略去N個字,contoller.php調用了action_upload.php,再調用了Uploader.class.php。

解bug第二式:打斷點,打了尼瑪好多,終於找到出問題的代碼是在這裏:

image.png

也就是說是iconv這個函數引起了報錯,一看上下文,這也沒什麼特別的用處,就是把一句提示信息轉一下碼而已,只要註釋掉這句就可以了,程序照常運行,也不會報錯,先回復環宇說bug已解,可以正常使用。

<?php

/**
* Created by JetBrains PhpStorm.
* User: taoqili
* Date: 12-7-18
* Time: 上午11: 32
* UEditor編輯器通用上傳類
*/
class Uploader
{
private $fileField; //文件域名
private $file; //文件上傳對象
private $base64; //文件上傳對象
private $config; //配置信息
private $oriName; //原始文件名
private $fileName; //新文件名
private $fullName; //完整文件名,即從當前配置目錄開始的URL
private $filePath; //完整文件名,即從當前配置目錄開始的URL
private $fileSize; //文件大小
private $fileType; //文件類型
private $stateInfo; //上傳狀態信息,
private $stateMap = array( //上傳狀態映射表,國際化用戶需考慮此處數據的國際化
"SUCCESS", //上傳成功標記,在UEditor中內不可改變,否則flash判斷會出錯
"文件大小超出 upload_max_filesize 限制",
"文件大小超出 MAX_FILE_SIZE 限制",
"文件未被完整上傳",
"沒有文件被上傳",
"上傳文件爲空",
"ERROR_TMP_FILE" => "臨時文件錯誤",
"ERROR_TMP_FILE_NOT_FOUND" => "找不到臨時文件",
"ERROR_SIZE_EXCEED" => "文件大小超出網站限制",
"ERROR_TYPE_NOT_ALLOWED" => "文件類型不允許",
"ERROR_CREATE_DIR" => "目錄創建失敗",
"ERROR_DIR_NOT_WRITEABLE" => "目錄沒有寫權限",
"ERROR_FILE_MOVE" => "文件保存時出錯",
"ERROR_FILE_NOT_FOUND" => "找不到上傳文件",
"ERROR_WRITE_CONTENT" => "寫入文件內容錯誤",
"ERROR_UNKNOWN" => "未知錯誤",
"ERROR_DEAD_LINK" => "鏈接不可用",
"ERROR_HTTP_LINK" => "鏈接不是http鏈接",
"ERROR_HTTP_CONTENTTYPE" => "鏈接contentType不正確",
"INVALID_URL" => "非法 URL",
"INVALID_IP" => "非法 IP"
);

/**
* 構造函數
* @param string $fileField 表單名稱
* @param array $config 配置項
* @param bool $base64 是否解析base64編碼,可省略。若開啓,則$fileField代表的是base64編碼的字符串表單名
*/
public function __construct($fileField, $config, $type = "upload")
{
    $this->fileField = $fileField;
    $this->config = $config;
    $this->type = $type;
    if ($type == "remote") {
        $this->saveRemote();
    } else if($type == "base64") {
        $this->upBase64();
    } else {
        $this->upFile();
    }

    $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']);
}


回頭來看這句 $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']); 是個什麼鬼,我的疑問有三點:

1.爲毛這裏要轉這一句提示信息,而不轉其他的?

2.iconv是什麼函數?

3.就算要轉,爲毛寫死了unicode到utf-8,爲毛不是gb2312或gbk?

是的,我來解答這幾個問題,首先第一個....不 我先說第二個:

iconv是一個從PHP4以後有的函數,功能是把字符串從一種編碼轉換成另一種編碼,詳情在這裏可以查看:http://php.net/manual/en/function.iconv.php

那編碼又是什麼意思呢,大概就是計算機存儲信息時最終是二進制,然全世界有很多國家和語言,慢慢變成同樣的二進制數字表示的字符不一樣,通常聽到的UTF-8,GB2312等,就是不同的編碼方式的意思,而ANSI,Unicode等就是不同的字符集的意思,詳情可以查看:http://blog.jobbole.com/84903/ ,或者 http://blog.csdn.net/wabil/article/details/50807240 。這裏不多講。

那爲什麼要寫死了了unicode到utf-8呢?那我就不知道了,一臉懵逼的時候,看到頭部:

/**
* Created by JetBrains PhpStorm.
* User: taoqili
* Date: 12-7-18
* Time: 上午11: 32
* UEditor編輯器通用上傳類
*/

好吧,解bug第三式:百度一下taoqili,第一條便是https://my.oschina.net/taoqili/ ,嗯,看起來是其作者本人的主頁,因爲這個主頁進去第一篇文章依然是5年前說Ueditor上線的文章 ....

image.png

於是進去此文章 https://www.oschina.net/news/37279/ueditor-1-2-5 ,無非就是說新版本上線了,文章 最後附了Ueditor的鏈接:

image.png

是鏈接到 http://ueditor.baidu.com/website/download.html#ueditor ,好吧,官網的頁面....,然後引人注目的是這句話:

image.png

再次懵逼的我,又打開了這個鏈接 https://github.com/fex-team/ueditor/blob/dev-1.5.0/php/Uploader.class.php ,不錯,這個文件內容是替換了的(不死心的我還下載了此頁面下方一些其他版本,看到Uploader.class.php的內容是和本地一樣) ,然回到此頁面的上一級 https://github.com/fex-team/ueditor/tree/dev-1.5.0/php ,如圖:

image.png

就是說其他文件在4年前就搬到git,這個文件是最近修改過了(PS:https://github.com/fex-team 是百度前端的git主頁),下載此文件,做了對比,也沒啥特別的,重要的是:

image.png

直接把上面有bug的給換了個函數,我汗,那這個mb_convert_encoding又是做嘛的?http://www.php.net/manual/en/function.mb-convert-encoding.php ,和iconv類似,也是轉編碼用的,但爲啥換成這個了? 其大概意思是iconv在中文(或者說非英文)時會有一些亂碼問題,比如有增加後綴 //TRANSLIT 和 //IGNORE 的用法,並且需要指定從啥轉換成啥的兩個”啥“,而mb_convert_encoding可以不用指定從啥到啥,因爲有個萬能的”auto“參數,具體的大家自己百度有一堆文章在講這兩個函數,這裏不多講,最重要的結論就是:mb_convert_encoding兼容好一些,但效率差一些...

好吧,我就認了,跳過...但是上上圖中

image.png

提到的是修復了***f的安全漏洞....可剛纔替換iconv的函數和***f好像沒半毛錢關係(這就是所謂的掩人耳目嗎),好吧,繼續對比兩份文件,在後面一個saveRemote 方法裏(這個方法是編輯器允許引用一張在網絡上已存在的真實的圖片地址,一般是指絕對路徑的URL),新版的代碼多了幾行:

 

//檢查文件內容是否真的是圖片
if (substr(mime_content_type($this->filePath), 0, 5) != 'image') {
    $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
    return;
}

bingo 就是他了,那麼問題來了,什麼是***f?好吧

***F(Server-Side Request Forgery ,服務器端請求僞造)是一種由***者構造形成由服務器發起請求的一個安全漏洞,***F的主要***目標爲外網無法訪問的內部系統
原因?
***F形成的原因是服務端提供了從其他服務器應用獲取數據的功能,在用戶可控的情況下,未對目標地址進行過濾與限制,導致此漏洞的產生。
比如從指定URL地址獲取網頁文本內容,加載指定地址的圖片等,都是***F容易出現的點。詳情依然請大家自行百度。

簡單來講就是服務器上會訪問其信任的第三方URL,如下面一個實例:

http://www.douban.com/***/service?image=http://www.baidu.com/img/bd_logo1.png

所以理論上要信任這個URL,是需要做一些限制和判斷的,很多網頁的分享功能,就有類似的原理,而上面的:檢查文件內容是否真的圖片,就是對其文件格式做了判斷,避免非圖片的URL被訪問到了。

回頭再倒個序:當我發現是那iconv引起的bug時,我突然想起前幾日嚴旭同學和航航寫公告功能的時候,加了Ueditor的,所以跑去庫裏追他的代碼,發現他其實自己給前端寫了一個上傳文件的接口,走我們mhi常用的common/upload去了,然再跟發現,他最終實現上傳的代碼,依然是拷的Ueditor的Uploader.class.php來實現的,並且,他把那句iconv函數的註釋了,然後我就問他爲啥要註釋呢,他說因爲那句不註釋就要報錯~~,我倆就尷尬的對視着,5秒之後我說好吧我知道了,然再5秒之後他問我:那你知道爲啥要報錯嗎,sorry,又是尷尬的對視,我說:我也不知道,畫面大家自行腦補。。。


好吧,說了那麼多,繞的挻遠的,回頭總結一下我本身想記的坑,也是本文的小結:

1.多語言造成的字符集和字符編碼的問題,比如常用的Unicode和ANSI字符集,UTF-8和GBK編碼,導致在windows和Linux、中文和英文操作系統裏、中文編碼的代碼文件和英文編碼的數據庫間,需要用字符編碼轉換函數進行轉換來解決亂碼的問題,特別是開發環境是windows,生產環境是Linux時要注意;

2.iconv和mb_convert_encoding都是php中轉換編碼的函數,大概區別在於:1.iconv效率高,但容易出錯,2.mb_convert_encoding兼容性好,但效率偏低,3兩個函數是依賴於不同的php擴展;

3.***f漏洞是指:當服務器允許訪問第三方URL,並且這個第三方URL是由用戶決定,那如果對這個URL未做嚴格的控制,可能會有漏洞;

4.Ueditor官網上的代碼,是5年前的了,要繼續用這個編輯器的童鞋,請到百度前端git頁去下載 https://github.com/fex-team/ueditor


Well,如果你很偏執,你可能會問我上面提的三個問題中第一個問題我好像沒回答,那我現在回答你:我沒找到答案......

所以,晚了,躺下就睡吧。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章