想試著來全部回答一下,我本業不是網路安全,所以可能內容會需要被訂正...
: 1. 像是 XSS 是什麼?會帶來什麼危險?用 htmlentities 跟 htmlspecialchars 這兩個函式來防禦有什麼差別?
白話解釋 XSS:「別人在你的網站上塞他的 javascript」
可能的危害
- 所有使用者都會看到嘲笑你的對話方塊
- 你的網站被塞別人的廣告
- 別人可以用 javacsript 控制你的使用者做...任何在他在網站上能做的事情
- 其他很多...
htmlenties 會把「每一個字」都編碼成 html entity,htmlspecialchars 只轉換 & " ' > < 這五個。
在大多數情況下這兩者的防禦效果差不多,差異在於出來的檔案大小會不一樣。
不過我記得有個 UTF-7 什麼碗糕的的鬼可以打穿 htmlspecialchars,只要 charset 用 utf-8 就沒事
剛剛一下 Google 找到都是講 wordpress 爛掉的事情就是了...
(EDIT: 我弄錯了,是 htmlenties 會被 UTF7 攻擊打穿...)
: 2. LFI 是什麼?除了被 access 到 /etc/passwd 之外,有沒有可能讓 source code 洩露?
白話解釋 LFI:「別人可以亂讀你的檔案」
a.php?file=aaa.doc => 吐 aaa.doc 的內容給他
a.php?file=a.php => 把 a.php 的內容(咦,不就自己嗎)吐給他
我看過比較有趣的搞法是讀 .git/ 把整個 repo 拿走,這樣還不用暴力硬掃你有哪些檔案...
: 3. SQL Injection 的原理是?用 PDO 真的可以一勞永逸嗎? mysqli_real_escape_string 真的有助於防止 SQL injection 嗎?
組 SQL 字串的時候塞奇怪東西讓 SQL 的意思跟你以為的不一樣,然後幹奇怪的事情
例如
$id = $_POST['id']; // '123or1=1'
$sql = "select * from user where id = {$id}";
出來會是 select * from user where id = 123or1=1
然後所有的使用者資料就一起噴出來了
而且這個例子用 mysqli_real_escape_string 正好搞不定
理想的做法是不要自己組 SQL 字串,用 prepared statement 然後 bind 參數。背後做的事情大約像這樣
mysql> PREPARE stmt FROM "SELECT * FROM user WHERE uid = ?";
「DB 哥哥,我等下要跑『select * from user where uid where id = ?』,問號我等下再跟你說是什麼」
Statement prepared
「好喔,我準備好了等你的參數」
mysql> SET @param='123or1=1'; EXECUTE stmt USING @param;
「DB 哥哥,參數是 '123or1=1' 喔,麻煩把結果跑給我」
Query OK, 0 rows affected (0.00 sec)
Empty set (0.00 sec)
「喔, id 是『123or1=1』嗎?我看看...一個都沒有喔!」
是說看到上面推文才知道 PDO 的 mysql driver 預設是模擬的,不是真的叫 DB 做 prepared statement。
可以用 PDO::ATTR_EMULATE_PREPARES 這個選項切換...
爬了一下別人的 code
Laravel 內部做 PDO 的時候會強制設定用真的 prepared statement
doctrine 的 dbal 跟 maghead 乍看之下沒有管這件事
希望 PHP(或該說 MySQL Driver )模擬的效果夠好...
: 4. 什麼是 SSRF?會讓系統服務遭受什麼樣的風險?
白話解釋 SSRF:「別人讓你的 server 幫他送 request 給其他機器」
例如抓檔案的 URL 如果可以自己亂填
upload_image.php?url=https://example.com/porn.png
然後你正好知道人家內部的網路狀況,或是剛好靈光一閃,就可以拿來打看看(?
upload_image.php?url=https://dashboard.example.com/
upload_image.php?url=http://127.0.0.1/
等於是讓人家間接鑽進內網幹亂連機器幹事情,而且你會以為是你的內部機器要連所以不一定擋得下來。
SSRF 我不確定有沒有什麼好招可以處理,只能想到黑名單白名單....
: 5. extract 這個函式很好用,但會不會不知不覺間被拿來提權?
extract 把 array 的內容直接拆開變成變數
然後有些人會覺得 extract($_GET) 很好用
於是別人就可以亂打網址 ?var1=aaa&var2=bbb 來亂改你 code 裡面的變數內容,什麼魔法都能變得出來..
我覺得啦,這年頭還是別這樣寫...
: 6. serialize 跟 unserialize 的使用時要注意什麼?
唔...我原本猜你指的是「unserialize 的東西可能是別人家過料的,然後就可以亂改變數內容」
類似 extract 的狀況
不過為防萬一找了一下,發現 PHP 跟 unserialize() 有關的安全漏洞還真不少....
我「猜」對 serialize 字串做數位簽章(例如 HMAC)驗證內容無誤,大概還擋得住。
不過更好的作法應該是...別 unserialize 任何使用者有機會動手腳(不論是網址帶入或 post 進來)的東西。
: 7. Hash 用戶的密碼是用什麼演算法好呢?bcrypt
: ?argon2?它們又有什麼差別?
簡單的答案:
什麼演算法都不要管,PHP(5.5+)都包好了
把密碼這樣存
$hash = password_hash("password", PASSWORD_DEFAULT);
然後這樣比對密碼是否正確(別在 DB 比對)
$passwordOK = password_verify($input, $storedHash);
然後用這裡的範例檢查有沒有需要更新密碼 hash
http://php.net/manual/en/function.password-needs-rehash.php
最後,PHP 升級你盡量跟著升級。
真正的答案:
現代 hash 演算法的敵人主要有幾種
## 從數學,相對容易算出碰撞值
例如數學方法可以快速算出其他值的 hash 跟你密碼的 hash 一樣
對應法:
選沒有已知攻擊的演算法
- md5 陣亡(2004的論文)
- sha1 進棺材了(同一個人寫過 sha1 的論文,Google 做過示範攻擊)
還有,沒事就看一大堆講這種事情的文章....
## 從實作上被攻擊
有些實作有 side channel attack 的問題,例如可以從計算時間推算數值
甚至有些比較誇張的,可以錄你電腦的聲音推斷 CPU 多忙,猜出在算的值是什麼...
對應法:
常常更新系統套件,用 libsodium 或是 PHP 5.5+ 的 password_ 系列 function 來算 hash
還有,沒事就看一大堆講這種事情的文章....
## 預先算好巨大的對應表(所謂 rainbow table)
就是有人會先算好一大堆字串的 hash,做成查詢表方便對照。
所以看到 7c4a8d09ca3762af61e59520943dc26494f8941b 可以直接查出這是 123456 的 sha1
是說,有個網站叫做 CrackStation,線上讓你查 hash 對應表..
對應法:
內容加鹽或是給 iv,每筆資料的鹽或 iv 都要都不一樣,而且要夠長
這會讓對應表很難做
也可以用 PHP 5.5+ 的 password_ 系列算,他預設會隨機產鹽撒進資料來算 hash
## 用巨大的運算力量暴力硬算
通常是雲端出租 GPU,也有靠 ASIC 之類的專門機器。
感謝數位挖礦產業,這方面的技術突飛猛進...
對應法:
選難以平行化運算的,記憶體使用量大的,計算效能差的
然後也許因此不該選 sha256,因為 bitcoin 挖礦就是在算 sha256,有利可圖就有專門產業...
bcrypt 是前幾年的佼佼者,而 argon2 是最近幾年的國際 hash 比賽冠軍。
PHP 預設的密碼 hash PASSWORD_DEFAULT 應該還是 bcrypt
有找到 PHP 的 RFC 提到「暫時不把 PASSWORD_DEFAULT 改成 ARGON2」
https://wiki.php.net/rfc/argon2_password_hash
不過理由我一下沒找到
然後「最好的 hash 演算法」是會隨著時間變化的
: 8. json_encode 跟 json_decode 有什麼潛在問題嗎?有沒有可能造成 DoS?
其實我直覺想到的是「爬 log 的時候用 json_decode 好慢,我都用 jq 先 parse 過」...
不過我猜你指的是可以惡搞 json 的內容,故意製造一大堆 hash collision,導致 hash table 效率低落。
這解釋起來得把 hash table 從頭講一次...簡中連結,不過講得還滿清楚的
http://www.cnblogs.com/yangecnu/p/Introduce-Hashtable.html
: 9. 什麼是 XXE?什麼情況可能會出現?
我我我我我很少用 XML 這個麻煩的小東西所以跟他不熟........
: 10. 什麼是 race codition?在 PHP + MySQL 這樣的經典組合中有沒有可能出現?要如何解決?
雷姆揍我右臉,我會往左邊飛
拉姆揍我左臉,我會往右邊飛
他們揍我的速度夠快,我被揍飛另一個揮拳就會落空
當雷姆跟拉姆幾乎同時出拳揍我,我可能往左邊飛,也可能往右邊飛,難以預測
race condition 就是指這種狀況
PHP 本身原則上是單一 Process 在跑的,所以只有 PHP 不太容易碰到
(我知道有 fork 或 pthreads,但是網頁上還是盡量別這麼幹,跑後端 job 就是另外一件事...)
MySQL 比較常碰到是當 DB 規模大的時候,通常會讀寫分離到不同台機器上。
如果先做 update 然後又馬上 select,有可能 select 不到更新後的資料(還沒同步到所有機器
另外一個狀況是來自系統外部,例如 client 同時發兩個取 token 的 request
然後你不確定 client 會覺得自己取到哪個
race condition 有一些正面解法,例如說設置 lock 確保一次只有一個人通過
不過我的經驗是,非硬解不可的狀況並不多,比較建議的作法是調整工作流程
例如既然都 update 了表示你有資料,那就別再 select 了
或是跪求 client 不要同時發一樣的 request 上來,如果有 re-try 機制要間隔夠久
不讓拉姆跟雷姆有幾乎同時揍我的機會,就不會不知道自己被揍時會飛往何方
: 11. 如何正確取得使用者的 IP?$_SERVER['REMOTE_ADDR']?如果商業邏輯伺服器是放在 LoadBalancer 之後呢?
$_SERVER 裡面有很多欄位跟 client IP 有關
REMOTE_ADDR
這是 server 看到當下 request 的來源 IP
HTTP_X_FORWARDED_FOR
HTTP_FORWARDED_FOR
HTTP_CLIENT_IP
這堆(或是其他類似的欄位)則是 http header 裡面寫的 client IP
如果你的伺服器是放在外面直接讓 client 打,那麼 HTTP_ 系列的欄位全部不可信
因為 http header 很容易偽造,人家可以隨便亂塞 127.0.0.1 之類的鬼東西
但如果機器放在 Load Balancer 後面,那麼送 request 給你的機器是 LB,REMOTE_ADDR 只會拿到 LB 的 IP
通常 LB 會確保 HTTP_X_FORWARDED_FOR 是原始的 client ip ,外部惡搞這個欄位的時候會也把他洗掉
這部分細節請參照 LB 的說明書
所以如何正確取得使用者 IP,要看你的的服務架構
- 機器直接對外,看 $_SERVER['REMOTE_ADDR']
- 機器在 LB 後面,看 $_SERVER['HTTP_X_FORWARDED_FOR'],或是看你的 LB 要你看哪個(些)欄位