top
Loading...
小符號大問題(perl和php下文件結束符的處理)
本人利用Perl編寫了一個論壇CGI程序,并用這個CGI程序在自己的主頁上構筑了一個論壇。程序全部采用結構性文本文件保存資料,如用戶名、密碼、帖子內容文件、帖子目錄文件(文件名main.txt,存放帖子編號、標題、發言者名字、發表日期、回復數及點擊數等資料,用于生成論壇界面中的帖子點擊鏈接)。程序一直運行良好,但是在5月11日,卻發現論壇界面中5月10日前用戶所發的帖子全部不見了,這令我大吃一驚。直覺告訴我這肯定是有人搗蛋。于是馬上FTP到我的主頁,一看帖子文件都完好無缺,但是main.txt的字節數卻不對頭,只剩下幾百字節了。我將main.txt拉了回來,打開一看,發現了問題所在:約在5月10日零時,一個別有用心的人(注冊名為hacker)用一個特殊的符號作為帖子標題發了一個帖子,致使main.txt文件中5月10日前的數據被刪除掉。這令我對這個符號產生了極大的興趣,于是在恢復了main.txt的數據后,對這個符號研究了一翻,發覺它對用Perl編寫的、用文本文件存放用戶發送的資料的CGI程序(如很多論壇或聊天室)有很大的危害性,造成數據丟失及程序運行異常。為了讓大家在編寫CGI時注意到這個問題,本人特意寫了這篇文章,算是拋磚引玉,愿和大家共同探討CGI安全方面的問題。


各位,看了上面的內容也許你會說:這太可笑了吧,一個符號就把你的論壇黑了?這真讓我感到慚愧,不過你可千萬別小看這個符號,說它特殊,是因為它是一個可見的、可復制粘貼的、Perl把它當成文件結束符的一個字符。實話說剛開始我也把問題想得很簡單:在程序里加入幾行命令,讓程序自動刪除帖子標題中的這個特殊結束符不就行了?可是,我馬上發現這根本行不通。要這樣做必須將這個符號放在程序中,這樣程序才能對用戶輸入的資料進行匹配以便鑒定這些資料是否存在這個字符。但是由于這個字符是一個文件結束符,使得程序總是運行出錯(程序運行前,系統首先要讀取程序文件,但是讀取過程卻在程序文件中的這個特殊字符處結束了,所以程序沒有完整地被讀取,程序運行當然會出錯,即使在這個字符前加上反斜杠或#作為注釋也不行),看來這算是Perl的一個BUG。現在我回頭說一下這個字符是如何造成main.txt文件中數據丟失的。為了便于后面的說明,我先對main.txt的結構作點說明并將程序中處理main.txt的部份簡化后寫出來:


main.txt的結構是一個帖子記錄占用兩行,如下:


D-1001-這是一個示范的帖子-過河卒-2000/05/15 12:02:01(2)〖32〗

<!--end: 100--></ul>


其中第一行內容順序是:帖子類型、編號、標題、發言者姓名、時間、回復數、點擊數,第二行是表示帖子資料結束。每新增一個帖子,新帖子的資料插入文件頭部。


程序添加新增帖子資料到main.txt的部份如下:


open (MAIN,″main.txt″); # 以讀取方式打開main.txt

@main = <MAIN>;

# 將main.txt中的內容賦予數組@main

close(MAIN);

# 關閉main.txt

open (MAIN,″>main.txt″;

# 以寫入方式打開main.txt

print MAIN @post;

# 寫入用戶發送的資料@post

print MAIN @main;

# 寫入main.txt原來的數據

close(MAIN);

# 關閉main.txt


當用這個特殊字符作為帖子標題發了一個帖子后(假設帖子編號為100),main.txt中就有了這個符號(在編號100帖子的資料中),此時main.txt還保持完整,即數據還沒有發生丟失。但是,如果接著別的用戶又發了一個帖子(帖子編號為101),則程序要先讀取main.txt(命令行:open (MAIN,″main.txt″); @main = <MAIN>; close(MAIN); ),接著以寫入方式再打開main.txt(命令行:open (MAIN,″>main.txt″;),在main.txt文件的頭部添加編號101帖子的資料后(命令行:print MAIN @post;),還要將main.txt文件原來的數據寫入到這個帖子資料的后面(命令行:print MAIN @main;),由于編號100的帖子完成發送之后,main.txt已經含有這個特殊的字符,程序在讀取main.txt時,并沒有完整地讀取所有的數據,正如前面所說的,因為程序把這個字符當成了文件結束符,所以程序僅僅讀取了這個符號之前的數據,造成數組@main只含有編號100的帖子的部份資料,而不是原來main.txt完整的數據,因此在print MAIN @main;這一步驟中,main.txt被破壞,只寫入了101帖子的資料及帖子100的部份資料,之前的帖子資料全部丟失。


如果把main.txt的結構改為每新增一個帖子,新帖子的資料便插入main.txt尾部,則這個字符雖然不會造成main.txt被破壞,但是之后新添加的帖子無法顯示。


為了進一步證實這個字符的危害性,我用這個字符對國外一個很著名的論壇程序Ultimate Bulletin Board(簡稱UBB,Perl編寫,國內有不少論壇使用這個程序的漢化版)的5.37版本進行了測試。UBB用文本文件存放用戶資料,新注冊的用戶資料放在文件尾部。測試中發現,如果某一個用戶注冊時名字含有這個字符的話,以后的注冊用戶都不能發言,因為程序在讀取用戶資料文件時便在這個字符處結束了,后面的用戶沒有被讀入,所以程序會說"你不是注冊用戶"。同樣地,對其它幾個論壇和聊天室也作了測試,發現這個符號都導致它們出現與UBB相似的問題。


這個字符真是太厲害了,不過后來我想出了一招來對付它,這一招就是“以其人之道還治其人之身”,具體是將用戶所輸入的資料先寫入一個臨時文件,然再從這個臨時文件讀取出這些資料,既然這個字符是一個結束符,那么如果用戶輸入的資料中含有這個字符的話,則讀取過程在這個字符處結束,讀取出來的資料并不包含這個字符,下面是示例源碼,對帖子的標題(|Subject)和發言者名字(|Sname)進行鑒別:


|Sretval = rand(1000000);

#生成一個隨機浮點數

|Sretval = int(|Sretval);

#舍去小數部份,得到一個整數

open (TMP,″>|Sretval.tmp″);

#以這個整數為文件名建立一個臨時文件

print TMP ″|Ssubject
)″;

#寫入帖子標題

print TMP ″|Sname″;

#寫入用戶名字

close(TMP);

#寫入臨時文件結束,關閉它

open (TMP,″|Sretval.tmp″);

#打開臨時文件

@tmp = <TMP>;

#將臨時文件的內容賦予數組@tmp

close(TMP);

|Ssubject =|Stmp[0];

|Sname = |Stmp[1];

|Ssubject =~ s/
//g;

if (|Ssubject eq ″″) {

&Head(“錯誤”,“沒有標題或標題中含有非法字符!”);

exit;

}

|Sname =~ s/
//g;

if (|Sname eq ″″) {

&Head(“錯誤”,“沒有標題或標題中含有非法字符!”);

exit;

}

unlink(″|Sretval.tmp″);

#刪除臨時文件

北斗有巢氏 有巢氏北斗