背景
咸魚君所在公司的安全部門, 掃描出項目的生產(chǎn)服務(wù)安全漏洞,并且很負(fù)責(zé)任的提供了兩份掃描文檔,
一份針對nginx(1.1.6),
一份針對php(7.1.3),
并限期一周內(nèi)改善完!
咸魚君粗略一掃,全都是服務(wù)軟件的漏洞,
于是乎, 兩個選擇擺在眼前:
1.根據(jù)文檔,按圖索驥, 一個補丁一個補丁的“勞模打補丁”;
2.直接升級軟件到最新版, 當(dāng)然, 得確保服務(wù)的代碼必須要適配!
于是咸魚君“認(rèn)真”考慮了一下(實在是懶,補丁太多,當(dāng)然,也是覺得升級了對應(yīng)用代碼影響不大), 決定升級服務(wù)軟件來解決漏洞!
下面,詳細(xì)介紹咸魚君的升級過程~
評估升級風(fēng)險!
有相似需求的朋友一定要認(rèn)真評估“直接升級服務(wù)軟件版本”方案是否適合自己的業(yè)務(wù), 因為服務(wù)軟件的版本每次升級都會有些變化, 比如php就會舍棄一些函數(shù), 萬一項目代碼用到了, 結(jié)果你直接升級了php
, 最后導(dǎo)致生產(chǎn)的服務(wù)代碼跑不起來,那就悲劇了!!
評估升級風(fēng)險的方式,咸魚君采用的是本地升級服務(wù)軟件,測試項目代碼是否正常~
有條件的也可以搞臺克隆生產(chǎn)環(huán)境的服務(wù)器, 而后測試升級操作~
備份備份備份!
先找到php 的安裝目錄
whereis php
查看當(dāng)前版本(7.1.3)
/usr/local/php/bin/php -v
查看當(dāng)前configure編譯參數(shù).這個要保存下,編譯新php時要用!
php -i | grep configure
這里列出來的數(shù)據(jù)格式不對,需要處理一下,建議小伙伴保存在txt中,然后將單引號“ ’ ”替換為空值即可
./configure
--prefix=/usr/local/php
--with-config-file-
path=/usr/local/php/etc
--enable-fpm
--with-mcrypt
--enable-mbstring
--enable-pdo
--with-curl
--disable-debug
--disable-rpath
--enable-inline-optimization
--with-bz2
--with-zlib
--enable-sockets
--enable-sysvsem
--enable-sysvshm
--enable-pcntl
--enable-mbregex
--with-mhash
--enable-zip
--with-pcre-regex
--with-mysqli
--with-gd
--with-jpeg-dir
--with-freetype-dir
--enable-calendar
--with-openssl
--with-png-dir
--with-zlib-dir
--enable-gd-native-ttf
--with-xsl
--with-gettext
--with-pdo-mysql
備份舊版的php文件夾
sudo cp -r /usr/local/php /usr/local/php_7.1_bac
php升級
接下來我們?nèi)?https://www.php.net/downloads.php找到合適的穩(wěn)定的php版本來下載, 記得一定要詳細(xì)看change log,來決定要不要升級! 否則生產(chǎn)的服務(wù)代碼因為版本問題掛了,那就悲劇了!! 咸魚君考察后,選擇了 php7.4.4穩(wěn)定版,粗略一看,應(yīng)該沒問題(事實上后面發(fā)現(xiàn)了個小問題!險些悲劇!)
下載
//切換會用戶目錄(下載文件時先切換自己的目錄,這是個好習(xí)慣!)
cd ~
wget https://www.php.net/distributions/php-7.4.4.tar.gz
解壓
tar -zxvf php-7.4.4.tar.gz
配置編譯參數(shù) (注意, 這里有很多問題, 為了流程的連貫性, 建議先往下看,讀完全文, 最后再執(zhí)行, 咸魚君實力踩坑!)
cd php-7.4.4
//這里的***代表之前保存的編譯參數(shù)
//但是php7.4.4和之前的有些許不同,所以需要大家先轉(zhuǎn)換!!!!
//務(wù)必先不要執(zhí)行,看完全文再執(zhí)行,免得踩坑~
./configure --prefix=***
編譯
sudo make
安裝
sudo make install
恢復(fù)配置
cd /usr/local/php_7.1_bac/
sudo cp etc/php.ini /usr/local/php/etc/php.ini
sudo cp etc/php-fpm.conf /usr/local/php/etc/php-fpm.conf
sudo cp etc/php-fpm.d/www.conf /usr/local/php/etc/php-fpm.d/
切換新版php
//平滑重啟
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
//當(dāng)然,有時候急于等待結(jié)果的話,可以殺死再重啟(不建議)
//咸魚君為了快速驗證,所以采用殺死再重啟的方式,小伙伴們視實際情況而定
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
最后,確認(rèn)服務(wù)正常,去訪問下生產(chǎn)的服務(wù),是不是正常,如果發(fā)現(xiàn)不對,立刻執(zhí)行以下命令恢復(fù)舊php
cd /usr/local
sudo killall php-fpm
sudo rm -r php
sudo cp -r php_7.1_bac/ php
sudo /php/sbin/php-fpm
如果真像上面羅列的升級過程一樣簡單就好了,事實上,咸魚君升級php7.4.4的過程中碰到了很多的,并且有些問題是網(wǎng)上找不到明確解答的!, 接下來聽咸魚君細(xì)細(xì)道來~
php編譯期間缺包問題
No package '*******' found
碰到這問題很好解決,缺啥裝啥即可!
這里咸魚君碰到了如下的包缺失問題, 要是你碰到的和我不一樣,沒關(guān)系, 網(wǎng)上這類的問題解決貼一大把,主要因系統(tǒng)環(huán)境而異!
No package 'libjpeg' found
sudo yum install libjpeg libjpeg-devel
No package 'libxml-2.0' found
sudo yum -y install libxml2
sudo yum -y install libxml2-devel
No package 'sqlite3' found
sudo yum install sqlite-devel
No package 'oniguruma' found
sudo yum install oniguruma oniguruma-devel
No package 'libpng' found
sudo yum install libpng-devel
以上包都很好解決,下面這個包有點“意思”,
No package 'libzip' found
或
Please reinstall the libzip distributio 或 configure: error: system libzip must be upgraded to version >= 0.11
這個包比較特別, 不知道是不是php7.4.4的適配問題, 咸魚君發(fā)現(xiàn) yum install 安裝的版本比較低,并且下載的1.2.1這個包安裝上還是識別不了,只有1.2.0可以用!!!
yum remove libzip libzip-devel //卸載yum安裝的低版本
wget https://nih.at/libzip/libzip-1.2.0.tar.gz
tar -xvf libzip-1.2.0.tar.gz
cd libzip-1.2.0
./configure
make
////這句一定要執(zhí)行,不然編譯安裝php還是報錯找不到!!
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"
sudo ldconfig
sudo make install
php執(zhí)行時加載“.so”擴展失敗問題
咸魚君安裝完各種包,興沖沖的執(zhí)行
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
時,發(fā)現(xiàn),報錯
PHP Warning: PHP Startup: Unable to load dynamic library “******.so”
好吧, 折騰唄!
根本原因就是, 原來編譯安裝的擴展, 比如“redis”這類的,只適用于當(dāng)時的php, 所以我們要想繼續(xù)使用, 則必須得用新的php重新編譯這些擴展,重新安裝!
這里注意的時,有些擴展在php的安裝包里就有,所以不用額外去下載~
Unable to load dynamic library “ bcmath.so”
cd php-7.4.4/ext/bcmath
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
sudo make
sudo make install
Unable to load dynamic library “redis.so”
wget https://pecl.php.net/get/redis-5.2.1.tgz
tar -axvf redis-5.2.1.tgz
cd redis-5.2.1
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
sudo make
sudo make install
咸魚君就碰到這兩個, 安裝完后
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
好吧,再次報錯, 原來是新的擴展打上后, 擴展路徑和以前不一樣了,所以需要修改php.ini, 注意, 安裝擴展時,有返回一個安裝路徑, extension_dir這填充這個路徑即可
sudo vim /usr/local/php/etc/php.ini
修改
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20190902/"
最后
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
好的,好像沒問題了!!(劇透, 接下來有兩個大坑!!!)
檢查服務(wù)正常階段
看似nginx和php都升級成功了,也不曾有報錯了!
接下來我們來到最后階段, 訪問下項目, 看下是不是正常運行!!
大坑一: 驗證碼不顯示!
咸魚君項目采用的是Laravel5.6 + captcha做的驗證碼展示, 結(jié)果升級后發(fā)現(xiàn),驗證碼顯示不出來, 圖片請求全是500錯誤, 打開Laravel Debug模式,看到報錯
Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined function Intervention\Image\Gd\imagettfbbox()
一看, php的gd擴展有問題啊!!!
趕緊去ssh生產(chǎn),查看gd擴展是不是正常安裝的
php -m | grep gd
發(fā)現(xiàn), gd擴展是有的!
這就奇怪了, 隨后網(wǎng)上搜索下, 發(fā)現(xiàn)了各種“大佬”帖子的“解決方案”
大致方案如下
- php重新編譯gd擴展
- 獨立下載gd擴展編譯安裝
………………
好吧, 咸魚君嘗試了, 并沒什么用!
這里就不發(fā)出這類的“解決方案”了,免得小伙伴們和我一樣走“歪路”!
下面是重點了, 解決方案如下
問題原因
其實這個問題的確和 gd 擴展有關(guān)系, 主要是和gd擴展的子擴展freetype有關(guān)系!
你需要先判斷生產(chǎn)環(huán)境是不是安裝了freetype
//查看freetype安裝目錄
find / -name freetype
要是安裝了, 則編寫一個小php腳本來判斷有沒有正確加載
gdtest.php
<?php
if(extension_loaded(gd)){
echo "can use gd<br>";
foreach(gd_info() as $cate=>$value)
echo "$cate: $value<br>";
}else
echo "can not use gd";
?>
執(zhí)行
php test.php
結(jié)果:
can use gd
GD Version: bundled (2.0.34 compatible)
FreeType Support: #這里冒號后面沒有值,說明gd不支持freetype
T1Lib Support:
GIF Read Support: 1
GIF Create Support: 1
JPEG Support: 1
PNG Support: 1
WBMP Support: 1
XPM Support:
XBM Support: 1
JIS-mapped Japanese Font Support:
若“FreeType Support: ”后為空不為“1”則說明,gd庫沒有freetype支持!
問題似乎陷入了循環(huán),明明安裝了freetype, 但是gd擴展卻顯示不支持??!!!! 搜遍互聯(lián)網(wǎng),咸魚君得到的全都是“重裝freetype”,“重裝gd擴展”,甚至“重裝php”的“解決方案”!
最后,咸魚君無疑中看到一篇帖子,關(guān)于介紹php7.4.4的configure編譯配置參數(shù)的變化的! 如下
--with-gd 改為 --enable-gd
--with-freetype-dir 改為 --with-freetype
--with-jpeg-dir 改為 --with-jpeg
--with-webp-dir 改為 --with-webp
--with-xpm-dir 改為 --with-xpm
--with-libxml-dir 改為 --with-libxml
--enable-zip 改為 --with-zip
--enable-zlib-dir 改為 --with-zlib
大家看到
--with-freetype-dir 改為 --with-freetype
有沒有眼前一亮?!! 是的,真相如此簡單, 還記得我們之前
php -i | grep configure
查出的那一堆配置參數(shù)么, 我們只需要按圖索驥, 把對應(yīng)的舊版參數(shù)替換成新版參數(shù), 然后重新
./configure --prefix=***
make
sudo make install
即可!!!
真是坑爹!
另外,咸魚君羅列的可能不全, 大家可以自行查看新版的參數(shù)
//**代表你需要的擴展參數(shù)的關(guān)鍵字,來查看對應(yīng)的配置方式
./configure -h | grep **
最后編譯配置php7.4.4的命令為
./configure
--prefix=/usr/local/php
--with-config-file-path=/usr/local/php/etc
--with-php-config=/usr/local/php/bin/php-configure
--enable-fpm
--enable-bcmath
--with-mcrypt
--enable-mbstring
--enable-pdo
--with-curl
--disable-debug
--disable-rpath
--enable-inline-optimization
--with-bz2
--with-zlib
--enable-sockets
--enable-sysvsem
--enable-sysvshm
--enable-pcntl
--enable-mbregex
--with-mhash
--with-zip
--with-pcre-regex
--with-mysqli
--enable-gd
--with-jpeg
--with-freetype
--enable-calendar
--with-openssl
--with-png
--with-zlib
--with-gd-native-ttf
--with-xsl
--with-gettext
--with-pdo-mysql
大坑二: php7.4.4數(shù)組嚴(yán)格驗證
滿心關(guān)系的解決完驗證碼問題, 登入了項目web后臺,卻突然發(fā)現(xiàn)報錯
Trying to access array offset on value of type null
當(dāng)時, 嚇得咸魚君一身冷汗, 迅速ssh上生產(chǎn), 先恢復(fù)了舊php!
之后,排查得到報錯原因
原來php7.4.4移除了很多notice報錯,也就是可能老版的php只是warning,notice級別的報錯,在新版里全部以error拋出!
Trying to access array offset on value of type null
就是其中的一種, php7.4.4不允許對值為null的數(shù)組不進(jìn)行判空操作直接使用!
舉個例子
$arr = //api返回;
$name = $arr['name'];
這樣的操作就會拋出異常,所以我們需要先判斷
if(isset($arr['name'])){
$name = $arr['name'];
}
可以看出,php也在進(jìn)行一些類型的嚴(yán)格規(guī)范了!
修改了這個問題, php就不拋錯了, 咸魚君再次上線了新版php~~
至此, 順利從php7.1.3升級至7.4.4!!
希望咸魚君的升級筆記能夠幫到需要的小伙伴~