精華區 > 酷!學園 精華區
網頁應有那些基礎的安全措施?
fell0206:
請各位大大,小弟想請問一下!
1.當我寫一個網站我應該有那些的安全機制?
2.如果我要測試我寫的網頁能夠一次容納多少人使用,才不會當機,不知有沒有大大知道要如何做?
請各位大大指點指點! Thank you~
Yamaka:
這個命題...好大 :o
我覺得首先應該要先讓 web server 的環境是安全的
然後再來討論網頁(網站)如何安全 :D :D
fell0206:
感謝大大的指點!
若排除SERVER的安全來說的話(因為...那並非自己的PC),應該有那些是基本的安全?
小的是在寫問卷系統,所以...只有一般的填表單及登入的動作而已,如果只是這樣...有什麼方法可以做到比較安全?
請大大指點指點! Thank you~
totouu:
底下是我自己翻譯的,有一些說明不是很順,給你參考看看。
除了php manual上的說明外,還須注意的安全事項。
session fixation----php manul上的session章節有提到
xss攻擊----這個不熟:不過把css,html,javascript語法全部濾掉,就可以避開了。
至於還有沒有其他的,就不知道了。
文章出處
http://www.php.net/manual/en/security.php
翻譯內容
Introduction
PHP是功能強悍的語言及解譯器,不論是當做網頁伺服器的模組或是以CGI的方式執行,
PHP都能在伺服器上存取檔案、執行系統命令或是開啟網路連結。這種特性使得任何在
網頁伺服器上執行的程式,本質上就是不安全的,但PHP經過特別設計,成為比Perl或
C等CGI程式更為安全的程式語言。只要選取正確的編譯及執行時期的設定參數,搭配適當
的程式碼,PHP可以讓你兼顧程式撰寫的自由度及安全性。
運用PHP的方式有很多種,所以有許多的參數可以設定,以便控制PHP如何執行。大量可
以設定的參數可以確保PHP可以運用在各種不同的目的,但也表示這些參數如果設定不當
會導致系統變得不安全。
PHP程式碼的編寫跟設定參數一樣,都有很大的彈性,PHP可以被用來發展完整的伺
服端應用程式,擁有一般shell使用者的所有權力,或者可以在一個嚴密控制的環境下
,得到低風險、應用有限的程式。執行環境的建立與安全性的高低,完全取決於PHP
程式的開發人員。
這個章節會談論一般性的安全議題,解釋不同的設定參數設定組合可以安全應用
的環境,及不同的安全層次上,程式碼撰寫時應考量的重點。
General considerations
完全安全的系統是不存在的,在資安專業領域常見的作法是在使用風險及系統使用便
利性上取得平衡,如果由使用者送出的變數,都要經過二次的生物檢測的方式做驗證
(例如虹膜掃描或指紋),你會有高度安全的系統,但那會花費半個鐘點去填寫複雜的表
單,也會鼓勵使用者找出方法,跳過這些安全認證。
最好的安全性認證,能夠滿足安全性的需求而且又夠簡單明瞭,不會妨礙使用者完成
他們的工作或使用過度複雜的程式碼,加重程式設計人員的負擔,實際上,許多安全
性認證的攻擊,只是簡單探測過度建構安全驗證的環境,因為隨著時間流逝,這些認
證都會慢慢毀壞。
有一句話值得銘記在心:系統安全性取決於整個系統最弱的一環。如果所有的程式執行
都記錄了時間、位址、執行方式等,可是使用者只經過簡單的cookie認證,那麼,使用
者執行時的所記錄的各項數據,可信度就相當低。
測試時,你必須曉得,不可能測試所有的可能狀況,即使是最簡單的網頁也是一樣,你
期待使用者輸入的資料可能和你預期的完全不同,這些資料可能來自心生不滿的使用、一
位有大把時間的駭客、甚至家裡的貓走過你的鍵盤都有可能,這也是為什麼,要從邏輯角
度檢視程式碼,發現可以從哪裡輸入非預期的資料,造成系統入侵,接著,再找尋如
何修正、減低或擴大這些威脅。
網路上有許多人想要成名,他們的方法很多,如:破解你的程式碼、毀損你的網站、張貼
不適宜的內容或是讓你的生活變得有趣。不論網站大小,只要連結網路,你就是他們的
目標,許多駭客程式並不會分辨網站大小,它們只是搜尋一大段網址裡頭的受害者,
不要變成其中的受害者。
Installed as an Apache module
當PHP以Apache的模組方式使用,他繼承了Apache使用者的權限(通常是"nobody",依linux
版本而定),這在安全性及權限管理上產生衝擊,例如,如果你使用PHP存取資料庫,除非
資料庫內建存取控制機制,不然,就必須讓資料庫可以被nobody存取,這意味著,一支
惡意的script,可以存取並修改資料庫,而不需要帳號及密碼,這可能讓網路蜘蛛可以搜尋
到資料庫管理頁面,而清空資料庫,你可以使用許多的方式保護資料庫:Apache的認證模
式 、或是使用LDAP、 .htaccess的模式等等,再含括這些程式碼成為PHP程式碼的一部份。
通常,系統建立時,PHP使用者(Apache的使用者)擁有的權限不足以傷害系統,PHP在這種情
況下無法寫入任何檔案到使用者目錄,甚至無法存取或改變資料庫,這種情況下,寫入一般
檔案或差勁的檔案、進行好的或壞的資料庫交易都是非常安全的。
在安全性上常犯的錯,就是給予Apache root的權限,或是用其他方式擴增Apache的能力。
擴增Apache的權限為root 的權限是非常危險的,會危及整個系統,所以sudo,chroot或其他
以root執行的方式最好不要考慮,除非你是安全專家。
有個簡單的方法,可以控制哪些目錄下的檔案,PHP可以控制。這個參數在php.ini裡頭
參數名稱:open_basedir
參數值:/home/php/
PHP在執行檔案函數前(fopen),會先檢查這個參數的設定值,如果要開啟的檔案或目錄的位置
不在這個參數設定值裡頭,就不會執行。
這個參數,在PHP安裝好後是沒有設定的,PHP可以存取系統上所有的檔案。
參數值的結尾如果不是/,就表示單獨的檔案,如果結尾是/,就表示目錄,如果有多個檔案
或目錄,每個參數值必須以冒號分開(windows系統以分號分開)。
例 :
open_basedir=/home/php/:/home/www/:/etc/config.php
只允許PHP的檔案函數可以存取/home/php/及/home/www/目錄下的檔案及/etc/config.php這個檔案
/home/php/ 及 /home/www/ 這個值後面都有接斜線(/),所以是目錄。
/etc/config.php 這個值後面沒有接斜線(/),所以是檔案。
Filesystem Security
系統上的檔案及目錄權限支配了PHP的安全性,這可以讓你控制系統上哪些可以讀取.你必須特別關注可以讓任何人讀取的檔案,確保這些檔案被讀取時不會危害安全性.
PHP在設計上,允許使用者存取檔案系統,你可以撰寫PHP script,做許多工作,如讀取/etc/passwd檔案,修改乙太網路的設定,送出大量的列印工作等,這對系統有很明顯的衝擊,你必須確認存取的檔案是正確的.
看一下底下的程式碼,這些程式碼讓使用者刪除家目錄裡的檔案,並且假設使用者以正常的方式使用這個介面.
Example#1 Poor variable checking leads to....
<?php
// remove a file from the user's home directory
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "The file has been deleted!";
?>
暨然使用者名稱及檔案名稱是由表單取得,使用者當然可以填入別人的名稱以及其他檔案名稱,然後刪除原本不允許他們刪除的檔案.這種情況下,你必須使用其他方式做認證,想想看,如果送出的變數值是../etc/以及passwd,會發生什麼事,這些程式碼是會發生作用的.
<?php
// removes a file from anywhere on the hard drive that
// the PHP user has access to. If PHP has root access:
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "The file has been deleted!";
?>
有二種方法可以讓你避免這種事情發生
*限制PHP執行者的權限
*檢查所有傳入的變數
下面是範例程式碼
xample#3 More secure file name checking
<?php
// removes a file from the hard drive that
// the PHP user has access to.
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "Deleted $filepath\n";
} else {
$logstring = "Failed to delete $filepath\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $lo gstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
可是上面的程式碼也有缺點,如果認證系統允許使用者填入登入名稱,而使用者填入的名稱是../etc,系統會再次陷入危險當中,所以你會想要有更多的驗證.
Example#4 More secure file name checking
<?php
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Bad username/filename");
}
//etc...
?>
依據作業系統的不同,你所必須留意的檔案就會不同,這些要留意的檔案,包括裝置檔(/dev 或com1),設定檔(/etc/files 及.ini檔),及資料檔(/home,My Document)等,因為安全考量,安全政策通常是禁止所有的檔案存取,只放行你允許的.
Null bytes realted issues
PHP使用底層的C函數進行相關的檔案操作,這些函數控制null位元的方式與我們期望的不同,在C中null位元代表字串的結尾,含有null位元的字串,C只會讀取字串開頭到null的部份,而略過剩下的字串,下面的程式印證了這個問題.
Example#5 Script vulnerable to null bytes
<?php
$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/'.$file.'.php')) {
// file_exists will return true as the file /home/wwwrun/../../etc/passwd exists
include '/home/wwwrun/'.$file.'.php';
// the file /etc/passwd will be included
}
?>
因此,檔案系統使用的字串必須經過適當驗證,底下的程式碼是比較好的寫法.
Example#6 Correctly validating the input
<?php
$file = $_GET['file'];
// Whitelisting possible values
switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/'.$file.'.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>
Database Security
今曰,資料庫是web應用系統的關鍵組件,提供網站豐富的動態內容,許多的敏感及機密資料都放在資料庫裡,因此你必須加倍保護你的資料庫.
為了存取資料,你必須連結資料庫,送出查詢,取得結果再關閉資料庫連結,目前與資料庫互動最常用的話言就是SQL
如同你的預期,PHP本身無法保護資料庫,接下來的部份,會說明如何使用PHP進行簡易的資料庫操作及存取.
你必須謹計一個原則:加強防禦縱深,你在越多地方採取資料庫的保護措施,被駭客破解的機率就越低,好的資料庫及程式設計可以解除你最深沉的恐懼.
Designing Database
建立網站的第一步就是建資料庫,除非你想要使用別人開發的作品,當建立資料庫時,建立資料庫的使用者就是資料庫的擁有者,通常只有資料庫的擁有者(或superuser)可以在資料庫執行任何的操作,別的使用者如果要使用資料庫,就必須被給予適當的權限.
應用程式絕對不能以資料庫擁有者或superuser的身份連結資料庫,因為這些使用者可以在資料庫裡做任何事,包括修改資料庫架構,或清空所有的內容.
你可以為不同應用層面的應用程式建立適當權限的資料庫使用者,如果入侵者取透過應用程式的認證取得資料庫的存取權,他們的影響範圍就是應用程式所能做的.
最好不要將所有的商業邏輯都實做在程式中,相反地,最好在資料庫中的view,trigger,rule中實做,如果系統持續演進,就必須開啟新的資料庫, 你也必須為每個資料庫重新實做這些商業邏輯,此外,trigger可以被用來做自動化的管理,這通常做為應用程式內部的除錯或交易的追蹤.
Connecting to Database
為了增加安全性,你可能會想要在server端及client端使用ssl或ssh進行資料的傳遞, 如果你採用了這些方法,駭客就難以取得資料庫通訊時的資訊.
Encrypted Storage Model
SSL/SSH可以保護在client端及server端傳送的資料,但卻沒辦法保護存在資料庫裡頭的資料.
一旦駭客可以直接存取資料庫,這些敏感資料就有可能就濫用,除非資料庫本身有方法保護這些資料,將資料加密是降低威脅的好方法,但只有少數資料庫提供這種加密方式.
解決這類問題最簡易的方式就是建立你自己的加密套件庫,然後使用在PHP script中,PHP有許多擴充套件可以幫助你建立加密套件庫,例如Mcrypt及Mhash就包含了不同的加密演算法,這樣,程式會在資料加進資料庫時進行加密,耎在取出資料時進行解密,你可以查閱PHP manual得到進一步的資訊.
如果不需要顯示解密後的資料,hash演算法可以列入考慮,hash演算法最有名的例子就是在資料庫中存入以hash方式加密後的資料,可以參考crypt()及md5()函數.
Example#1 Using hashed password field
<?php
// storing password hash
$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
pg_escape_string($username), md5($password));
$result = pg_query($connection, $query);
// querying if user submitted the right password
$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
pg_escape_string($username), md5($password));
$result = pg_query($connection, $query);
if (pg_num_rows($result) > 0) {
echo 'Welcome, $username!';
} else {
echo 'Authentication failed for $username.';
}
?>
SQL Injection
許多web程式開發者並不明白SQL語法是可以被纂改的,並且以為SQL語法是可以被信任的命令,這表示SQL語法可以繞過存取控制,直接通過認證及授權檢查,甚至執行作業系統的命令.
SQL 隱碼攻擊是駭客使用的技巧之一,它被用來建立或改變現有的SQL語法,使你的資料外洩,或更動資料庫裡頭的資料,這種技巧甚至讓駭客可以執行作業系統的命令.這種技巧是透過應用程式將使用者輸入的變數跟程式中的SQL語法結合而產生新的SQL語法的一種技巧,很不幸地,下面所提都是真實案例:
缺乏資料驗證而且以superuser(或能建立使用者的身分)的身分連結資料庫,駭客就可以在資料庫裡頭建立superuser.
Example#1 Splitting the result set into pages ... and making superusers (PostgreSQL)
<?php
$offset = $argv[0]; // beware, no input validation!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
一般的使用者會點選上一頁或下一頁的超連結,程式預期$offset傳入的變數是數字,但是你可以在url中輸入下面的內容打斷程式的執行
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
如果照上面的方式做,會新增一個superuser,輸入0;是為了使$offset成為有效的數字,並且結束這個SQL查詢.
注意:程式設計師常用--符號當做sql註解,使--符號後的文字不被資料庫執行.
實務上,取得密碼比較可行的做法是透過搜尋頁面,駭客唯一要做的就是找找看,SQL語法裡有沒有管理不當的變數,這些放在表單裡的篩選變數,通常會放在 select語句中的where,order by,limit,offset後面,如果你的資料片支援union語法,駭客可以在原來的sql語法後面再加上一個完整的sql語法,這樣就可以列出任何一個資料表裡頭的密碼,所以強烈建議使用加密過的密碼欄位.
Example#2 Listing out articles ... and some passwords (any database server)
<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = odbc_exec($conn, $query);
?>
The static part of the query can be combined with another SELECT statement which reveals all passwords:
'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--
If this query (playing with the ' and --) were assigned to one of the variables used in $query, the query beast awakened.
SQL的update語法也會受到攻擊,這些SQL語句有可能被截斷,再加入新的語法而使主機受到攻擊,但駭客要纂改set語句,他必須知道資料庫架構的一些資訊,這些資訊可以由變數名稱或使用暴力法破解來取得,帳號及密碼欄位並沒有那麼多的命名慣例.
Example#3 From resetting a password ... to gaining more privileges (any database server)
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
But a malicious user sumbits the value ' or uid like'%admin%'; -- to $uid to change the admin's password, or simply sets $pwd to "hehehe', admin='yes', trusted=100 " (with a trailing space) to gain more privileges. Then, the query will be twisted:
<?php
// $uid == ' or uid like'%admin%'; --
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";
// $pwd == "hehehe', admin='yes', trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE
...;";
?>
一個可怕的狀況是作業系統的命令可以在資料庫上執行
Example#4 Attacking the database hosts operating system (MSSQL Server)
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
If attacker submits the value a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- to $prod, then the $query will be:
<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
?>
MSSQL Server executes the SQL statements in the batch including a command to add a new user to the local accounts database. If this application were running as sa and the MSSQLSERVER service is running with sufficient privileges, the attacker would now have an account with which to access this machine.
Note: Some of the examples above is tied to a specific database server. This does not mean that a similar attack is impossible against other products. Your database server may be similarly vulnerable in another manner.
Avoiding techniques
你可能會辯解駭客必須擁有資料庫的資訊才能進行各類sql隱碼的攻擊,這是對的,可是你永遠不會知道這些資訊會如何被駭客取得,如果這些事情發生了,你的資料庫就曝露在風險當中,如果你使用開放原始碼或公開的資料庫套件,駭客很容易取得程式碼的複本,如果程式設計有疏漏,很容易形成安全性上的漏泂.
這些攻擊主要是找尋沒有遵循安全規範撰寫的程式碼,永遠不要相信使用者輸入的資料,就算是select box,hidden input field,cookie的資料,也都不能信任,第一個例子就印證了,這可能造成災難.
*絕對不能以superuser或資料庫擁有者的身份與資料庫進行連接,而是永遠以權限受限的使用者連接資料庫
*檢查輸入的資料型態是否符合我們的預期,PHP有很多的驗證函數以及正規表示式的支援.
*如果應用程式期待的輸入資料型態是數字,可以使用is_numeric(),settype(),sprintf()函數做判斷或轉換及呈現.
Example#5 A more secure way to compose a query for paging
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string, using %s would be meaningless
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
$offset);
?>
*所有使用者提供的資料,如果不是數字型態,就必須使用資料庫提供的跳脫函數進行處理 (mysql_escape_string(),sql_escape_string()),如果資料庫提供的函數沒辦法處理sql語法裡的特殊字元,就必須使用addslashes()及str_replace()函數,第一個實例已經說明,只在sql語法中加入引號是不夠的,那是非常容易被解的.
*不要顯示任何的資料庫資訊,尤其是資料庫架構,你可以查詢下面二個主題的資訊: Error Reporting and Error Handling and Logging Functions.
*你可以使用預儲程序或prviously defined cursor進行資料庫的抽象性存取,這樣你就不會直接存取資料表或view,但這種解決方案會有其他衝擊.
此外,最好能夠對程式的運作進行log,雖然這樣沒辦法防止系統被入侵,但卻可以追蹤哪些程式的防護被破解了,log本身是沒有意義的,裡頭的資訊才有價值,多一點訊息總比資訊貧乏的狀況好.
Error Reporting
對PHP來說,錯誤報告對安全性有正反兩面的影響,錯誤報告能夠增強安全性,也可能危害系統安全。
標準的攻擊策略通常會先送出不正確的資料給系統,然後檢查這些錯誤訊息的種類及內容,以便探
測整個系統,這可以讓駭客得到伺服器的資訊,找到可能的弱點,例如,一位駭客得到關於一個
頁面的少數資訊是來自於之前提交的另一個頁面,他可以修改這些送出的變數名稱或值:
Example#1 Attacking Variables with a custom HTML page
PHP的錯誤報告對程式設計師所非常有幫助,他可以告訴程式設計師哪些函數或檔案有錯誤,哪
幾行程式碼有問題,這些錯誤報告所能透露的也就是這些了,但如果使用show_source()、
highlight_string()、highlight_file()等函數做為除錯的方法就不同了,在一個運作中的網站,這樣
做會暴露隱藏的變數、未經檢查的程式語法及其他危害系統的資訊,特別危險的是,如果程式
碼內含有內建的除錯裝置或公開的除錯技巧,而駭客知道你使用的除錯技巧,他們就可以
藉著送出不同的除錯字串,使用暴力法破解你的頁面。
Example#2 Exploiting common debugging variables
如果不考慮錯誤管理機制,提供系統的錯誤訊息會讓駭客得到更多的資訊。
例如,典型的PHP錯誤報告可以告訴我們系統是建構在PHP上,如果駭客找到一個.html的頁面,
而且想要知道後端的系統類型,只要送出錯誤的資料,就可以知道系統是以PHP建構的。
函數的錯誤訊息可以告訴我們系統是不是正在執行某個資料庫引擎,或是給我們頁面及程式如何
設計的線索,這可以讓我們知道資料庫開啟的連接埠,或找到頁面的弱點,例如,送出錯誤的
資料,駭客就可以知道認證的順序,或發掘暴露的程式碼。
檔案系統或一般的PHP錯誤訊息可以告訴我們Web伺服器擁有的權限以及Web伺服器上的檔案
架構,程式設計所撰寫的錯式碼如果有錯,會惡化這個問題,使以前隱藏的資訊輕易曝光。
有三種方式可以解決這些問題,第一種方式是檢視所有的函數,修正所有的錯誤,第二種
方式是關閉系統的錯誤報告功能,第三種方式是使用PHP的錯誤報告函數建立你自己的錯誤
處理機制,依據你的安全政策的不同,你可以在你的系統上應用以上三種方式。
提前找到這類問題的方法就是使用error_reporting()函數,他可以讓你的程式碼變得更加安全,
並且發現會造成危害的變數設定,在程式碼開始佈署前,使用error_reporting()函數中的E_ALL
參數可以幫你快速找到會毒害系統或被其他方式纂改的變數內容,一旦開始佈署程式碼,你應該
關閉錯誤報告的功能(error_reporting(0)),或是將php.ini中顯示錯誤訊息的功能關閉(diaplay_errors=off)
,避免程式碼的內容被破解,如果你要晚一點才做這些設定,你應該設定log檔的路徑,並開啟
log的功能(error_log,log_errors=on)
Example#3 Finding dangerous variables with E_ALL
if ($username) { // Not initialized or checked before usage
$good_login = 1;
}
if ($good_login == 1) { // If above test fails, not initialized or checked before usage
readfile ("/highly/sensitive/data/index.html");
}
?>
Using Register Globals
注意:最好不要使用這個功能,PHP 6.0已經移除這個功能。
PHP改變時引起最大爭論的可能是將register_globals的預設值由on改成off(php 4.2.0之後),依照
這個設定來運作的程式相當普遍,許多人還不知道有這個設定的存在,以為這是php運作的模式
,這裡將要解釋如何使用這個設定撰寫不安全的程式,但你要知道,這個設定本身並非不安全
,除非你誤用他。
當register_globals的設定值為on,你的程式碼中可能會被插入所有種類的變數,如從html表單傳
過來的request變數,因為PHP不須要初始化變數,當register_globals設定值為on時,會更容易撰
寫不安全的程式碼。當PHP社群決定將register_globals的預設值改為off時,這是非常困難的決定
,當設為on時,程式設計師不確定變數從哪邊取得,程式內部設定的變數會與使用者送出的變
數混淆,可是設為off時,就會改變這種狀況,讓我們舉一個register_globals誤用的例子做說明:
Example#1 Example misuse with register_globals = on
// define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
// Because we didn't first initialize $authorized as false, this might be
// defined through register_globals, like from GET auth.php?authorized=1
// So, anyone can be seen as authenticated!
if ($authorized) {
include "/highly/sensitive/data.php";
}
?>
當register_globals設為on時,上面的程式邏輯就會有安全風險,當設為off時,$authorized不會經
由request取得變數,所以不會有安全風險,但養成初始化變數的習慣是一個比較好的寫作風格,
例如:上面的例子中,我們可以將$authorized的初始值設為false($authorized=false),這樣做,
可以確保程式碼不論register_globals的設定為on或off,都可以確定使用者在預設上都是未獲授權的。
另一個例子是session,當register_globals設為on時,我們可以在下面的例子中使用$username變數,
但這個變數也可能以其他方式傳入,例如經由url的get方式:
Example#2 Example use of sessions with register_globals on or off
// We wouldn't know where $username came from but do know $_SESSION is
// for session data
if (isset($_SESSION['username'])) {
echo "Hello {$_SESSION['username']}";
} else {
echo "Hello Guest
";
echo "Would you like to login?";
}
?>
當變數來源造成混淆時,我們可以採取預防方法,如果你事先知道變數來源時,你可以檢驗這些
變數是不是用不正當的管道傳送進來,這不保證資料不會被混雜在一起,但這會使駭客必須猜測
資料混用的正確方式。如果你不在乎資料request的來源,你可以使用$_REQUEST變數,它保含了
GET,POST,COOKIE的所有資料,你可以參考variables from outside of PHP.手冊的內容。
Example#3 Detecting simple variable poisoning
if (isset($_COOKIE['MAGIC_COOKIE'])) {
// MAGIC_COOKIE comes from a cookie.
// Be sure to validate the cookie data!
} elseif (isset($_GET['MAGIC_COOKIE']) || isset($_POST['MAGIC_COOKIE'])) {
mail("admin@example.com", "Possible breakin attempt", $_SERVER['REMOTE_ADDR']);
echo "Security violation, admin has been alerted.";
exit;
} else {
// MAGIC_COOKIE isn't set through this REQUEST
}
?>
當然,將register_globals設為off,並不保證程式碼是安全的,必須以其他的方式檢驗,無論如何,
都必須檢驗使用者送來的資料並初始化你的所有變數,如果要檢查未經初始化的變數,你可以
使用error_reporting()函數,讓這個函數顯示E_NOTICE的錯誤訊息。
如果想要獲得更多有關register_globals為on或off時的訊息,可以參考faq。
Note: Superglobals: availability note Since PHP 4.1.0, superglobal arrays such
as $_GET , $_POST, and $_SERVER, etc. have been available. For more information,
read the manual section on superglobals
User Submitted Data
PHP程式的最大弱點不是來自程式語言本身的特質,而是來自於不關心安全性的程式碼,因此,
你必須花時間檢驗程式碼,確定非預期的變數傳入時所造成的各種危害。
Example#1 Dangerous Variable Usage
// remove a file from the user's home directory... or maybe
// somebody else's?
unlink ($evil_var);
// Write logging of their access... or maybe an /etc/passwd entry?
fwrite ($fp, $evil_var);
// Execute something trivial.. or rm -rf *?
system ($evil_var);
exec ($evil_var);
?>
你必須檢驗程式碼,確認所有由瀏覽器傳入的變數都有經過檢驗,並且問問自己下面這些問題:
*程式是否只影響預期的檔案
*非預期的資料是否會發生作用
*這個程式是否可以用非預期的方式使用
*這個程式與其他程式共同運作時,是否會有負面結果
*任何程式運作的結果是否被適當地log
撰寫程式時必須提出這些問題,而非之後,如果你必須增強程式安全性時,就不須重新撰寫程式碼
,當你以這些問題當做出發點,你沒辦法保證你的系統的安全性,但你可以改善它。
你可能想要關閉register_globals,magic_quotes等方便性的設定,因為這些設定會造成變數來源、變數
合法性或變數值的混淆,使用error_reporting可以讓你在變數驗證前或初始化前就獲得警告,這
樣你就可以防止未使用的變數發生作用了。
Magic Quotes
注意:不鼓勵使用這個功能,php6.0.0已移除這個功能。
Magic Quotes可以讓你在PHP script中進行字串地自動跳脫,但最好是關閉這個功能,而是在程式
執行時進行這些跳脫動作。
What are Magic Quotes
當設為on時,單引號(')、進引號(")、倒斜線(\)及NULL字元會自動加入倒斜線(\),addslashes()函
數也會做同樣的事。
magic quotes有三個設定參數
magic_quotes_gpc會影響HTTP request的資料(get,post,cookie),不能在執行時設定,預設為on,
相關函數:get_magic_quotes_gpc()。
magic_quotes_runtime如果設定為on,所有從外部傳入的資料,包含資料庫及檔案,都會進行字串跳脫
,可以在執行時設定,預設為off,相關函數:set_magic_quotes_runtime(),get_magic_quotes_runtime()。
magic_quotes_sybase如果設為on,會以單引號當做跳脫字元,且只會針對單引號進行跳脫 ,雙引號、
倒斜線、NULL字元不會進行跳脫,如果原來的magic_quotes_gpc設為on,這個設定會失效,
相關函數:get_ini()。
Why user Magic Quotes
這對初學者很有用處,可以讓程式碼免除一些危險性,雖然在使用magic_quotes時,仍有可能遭
到sql隱碼的攻擊,但這風險已經大幅降低了。
Why not to use Magic Quotes
可攜性:不論設為on或off,都會影響可攜性,可以使用get_magic_quotes_gpc()檢查這個設定,
並根據傳回值,撰寫程式碼。
效能:並非所有經過跳脫處理的資料都要存入資料庫,進行所有資料的跳脫就會造成效能的折損,
在程式執行時才進行跳脫(addslashes())才是比較有效率的作法,雖然預設是開啟的,但建議你最
好將他關閉,這是基於提高效能的原因。
不方便:因為不是所有的資料都須要跳脫,當看到不須跳脫的資料含有跳脫字元時,是很惹人厭
的,例如,從表單寄送電子郵件,並且看到一串的跳脫(\)字元,要修正這個問題,你必須大量
使用stripslashes()函數。
Disabling Magic Quotes
magic_quotes_gpc只能在系統層級關閉,無法在執行時關閉,也就是說,無法使用ini_set()函數。
Example#1 Disabling magic quotes server side
An example that sets the value of these directives to Off in php.ini. For additional details, read the manual section titled How to change configuration settings.
; Magic quotes
;
; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off
; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off
; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = Off
If access to the server configuration is unavailable, use of .htaccess is also an option. For example:
php_flag magic_quotes_gpc Off
如果要撰寫高可攜性的程式碼,底下的範例程式碼可以在程式執行時關閉magic_quotes_gpc,不
過這個方法很沒效率,最好地方式是直接設定php.ini的參數或是在.htaccess中進行設定。
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value)
{
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
return $value;
}
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}
?>
Hiding PHP
通常,隱藏資訊是最薄弱的保護方式,但在一些案例中,任何增加一點點安全性的方法都是
被極度渴望的。
一個簡單地技巧可以幫助你隱藏PHP資訊,減慢駭客探查系系統弱點的速度,將php.ini中的
expose_php設為off,可以減少透露出去的資訊。
另一個策略就是在apahce的設定檔中或是.htaccess中設定php處理的檔案類型,設定誤導駭客的
副檔名:
Example#1 Hiding PHP as another language
# Make PHP code look like other code types
AddType application/x-httpd-php .asp .py .pl
Or obscure it completely:
Example#2 Using unknown types for PHP extensions
# Make PHP code look like unknown types
AddType application/x-httpd-php .bop .foo .133t
Or hide it as HTML code, which has a slight performance hit because all HTML will be parsed through the PHP engine:
Example#3 Using HTML types for PHP extensions
# Make all PHP code look like HTML
AddType application/x-httpd-php .htm .html
為了讓這些設定可以正常運作,你必須重新命名PHP程式的副檔名,這個方式,能夠提供的保
護很少,但缺點也很少。
Keeping Current
PHP就像其他的大型系統一樣,都在被持續觀察及改進當中,每個新版本通常都會包含各種大大
小小的改變,以便加強安全性,修正系統弱點以及設定上的錯誤,這些都會加強系統的穩定度
及安全性。
就如同其他的程式語言一樣,最好的方法就是時常更新,並且清楚明白新版本的特點及變動。
http://totou-phpmysql.blogspot.com/
fell0206:
感謝大大的指點!
這篇文章之前就看到過,不過英文差所以看不太懂,經大大的翻譯,著實了解不少!
小的在問一個問題,在判斷權限時,若我設定SESSION中的資料有一定的格式(例:XXX,Y),然後我用eregi去判斷是否符合,這樣應該會比只check SESSION是否為NULL來的好吧?(不知大大覺得有沒有必要)請大大在指點指點~~ Thank you!
導覽
[0] 文章列表
[#] 下頁
前往完整版本