close

我們是使用 PHP 開發、以 CheckMarx 進行源碼檢測。在這篇持續補充各種 CheckMarx 修補心得~


弱點:

  • File Inclusion
  • Inappropriate Encoding for Output Context
  • Parameter Tampering
  • Reflected XSS All Clients
  • Remote File Inclusion
  • Stored XSS
  • Client DOM Stored XSS
  • Client Potential XSS
  • File Manipulation
  • CSRF

程式完成後要進 CheckMarx 掃描,確認原始碼沒有弱點。但是 CheckMarx 非常敏感,看到以下系統變數,就會判斷可能成為注入點:

  • $_SERVER
  • $_FILE
  • $_POST
  • $_GET

我原本很賣力的寫正規表示式 (regular expression),用 preg_match 來過濾傳入的參數,但是 CheckMarx 還是判斷我沒有處理,我猜可能是因為 CheckMarx 無力判斷 regex 是否寫得夠嚴謹。

上述的變數常常會 CheckMarx 被判斷為以下弱點:

  • Reflected XSS All Clients
  • Remote File Inclusion
  • File Inclusion

參考《Checkmarx的Reflected XSS All Clients如何修補?》,看到苦主們集體哀嚎,其中有人說「代理商回應修改過的解法是ok的,建議客戶可以加入例外清單」,但是不是每個人家裡的資安都會接受這種「看到弱點卻沒有解決它」的修正方式。

另外有個人建議的做法是使用 PHP 內建的過濾函式 filter_var_array() 或是 filter_input_array()。這兩個函式內的可用參數可參考 PHP 官方的文件 "預設常數說明"(簡體中文版,上方亦可切換語系),或是 w3school 的簡體中文版也有整理:《PHP filter 函數》

 

 

另外,雖然我們原本用 PDO 的參數化查詢寫法,但是某版本的 CheckMarx 口味特殊,只接受在 PDO execute 時用陣列的方式餵已命名參數,所以原本是這樣寫:

<?php
  $sql = "select * from test_table from id= ?";
  $sth = $dbh->prepare($sql);
  $sth->bindValue(1, $testID, PDO::PARAM_INT);
?>

得改成這樣寫:

<?php
  $sql = "select * from test_table from id=:testID";
  $sth = $dbh->prepare($sql);
  $sth->execute([ "testID" => $testID ])
?>

否則照樣會吃一記 SQL Injection 弱點。

 


弱點:Missing HSTS Header

按照報告中的建議,在 PHP 裡加入以下 header 即可:

<?php header("Strict-Transport-Security: max-age=31536000; includeSubDomains"); ?>

 


弱點:Second Order SQL Injection

原本我使用 PDO 將資料庫裡的資料撈出來後,透過 fetch() 放到 $row 裡,想說可以比照前面過濾參數的方式,把 $row 先用 filter_var_array() 濾過就拿去用,但一直跳出中風險弱點 "Second Order SQL Injection"。

後來在同事的指點下發現,再多包一層字串取代 str_replace(),將內容的單引號消掉,就沒事了。

<?php $test = str_replace("'", "", $row["TEST_COL"]); ?>

 


弱點:

  • Cross Site History Manipulation
  • Insecure Randomness

"Cross Site History Manipulation" 這個我看不懂為什麼是定格在 if 條件式下,原本以為是對裡面的條件有疑義,後來看了《CheckMarx Cross Site History Manipulation 解决方案》才發現,我的 else 裡會執行轉址,轉到顧定頁面,這個行為被判斷成有攻擊的可能。但其實不會啊,我就是想要使用者在條件不成立時一律轉到固定頁面。

如果硬是要修掉,就是在轉址時加個不重複的隨機參數。我原本加 time(),但被判斷成還不夠安全;只加 rand(),還出現新的弱點 "Insecure Randomness",加入亂數的 seed 也沒用,用 mt_srand() 處理也不行:

<?php  header("Location: test.php?l=".$lang."&t=".mt_srand(); ?>

使用 openssl_random_pseudo_bytes 後,雖然不會出現 "Insecure Randomness",但是還是會出現 "Cross Site History Manipulation" 。後來我嘗試把轉址的內容抽出來變成 function,忽然就好了。XDrz

有天再搜尋一下查到 "How insecure are PHP's rand functions?",有人提到:

"There are now cryptographically secure pseudo-random generators in PHP 7, including random_int and random_bytes. Might be worth checking those out."(PHP 7 有安全的加密器 random_int()random_bytes(),也許值得一試。)

我用了 random_int() 取代 rand(),貌似有消除弱點 "Insecure Randomness"。


弱點:Session Fixation

目的是要避免攻擊者利用未結束的 session 來偽冒受害者,解決方式是在 session_start() 之前,先使用 session_destroy()

 


弱點:Client DOM Stored XSS

如果 JavaScript 有帶 HTML 格式的值要塞到頁面元素上,會很容易觸發 Checkmarx 的 Client DOM Stored XSS。解決辦法可以利用 jQuery 的 parseHTML:

function filterHtml(html){
    var parsed = $.parseHTML(html);
    var filteredHtml = $('<div/>').append(parsed).html();
    return filteredHtml;
}

如果沒有一定要用 $.html(),也可以使用以下兩個方法來處理:

  1. 使用 jQuery 的 text() 來取代 html(),設定元素的內容。
  2. 使用 jQuery 的 append() 來取代 html(),在指定節點增加元素內容。

弱點: Client Potential XSS

如果有值直接取出後使用,會觸發 Client Potential XSS,技巧是把角括號取代掉:

function filterXss(str) {
    str = str.replace(">", "");
    str = str.replace("<", "");
    return str;
}

 


弱點:File Manipulation

如果整串路徑是直接從使用者有能力干預的參數裡取得(例如:$_FILES['user_upload']['tmp_name']),會有被變更的風險。

需要使用 basename() 取得檔名,再轉到指定的目錄。

例如:

$tmpfile = sys_get_temp_dir().basename($_FILES['user_upload']['tmp_name']);

弱點:CSRF

需要以 token 確保資料沒有被異動。

 

以上,給各位苦情的同業們參考。

arrow
arrow
    創作者介紹
    創作者 小攻城師 的頭像
    小攻城師

    小攻城師的戰場筆記

    小攻城師 發表在 痞客邦 留言(0) 人氣()