如何寫出安全的PHP網站
2013/12/01 14:19網站安全那些大小事
如果你正在學習使用PHP(其實任何語言都通用)開發網站... 請務必要注意到安全性的問題 特別是有開放留言、註冊功能之類的網站!
有留言板,就有人想 <script>
...
有登入系統,就有人想 'or''='
...
可以寫<script>
的留言板,黑客就可以偷走你的cookie
甚至偷走你的密碼,進入後台搞破壞
萬一你的密碼是萬用密碼,拿到之後每個網站都可以登入
那你就損失慘重了!
至於可以寫單引號的系統呢? 如果你寫了...
$query = mysql_query("DELETE FROM `messages` WHERE " .
"`mid` = '{$_POST['id']}' AND " .
"`del_code` = '{$_POST['code']}'");
像這樣具有刪除留言功能的留言板
那麼我只要搞破壞,在code欄位輸入了 ' or '' = '
...
整個 SQL 命令就會變成
DELETE FROM `messages` WHERE `mid` = '' AND `del_code` = '' or '' = ''
看清楚了嗎?
or '' = ''
然後你整張資料表的內容就...
萬一這是使用者刪除帳號的功能呢?
只要把握一個原則,就可以寫出很安全的網頁
Never Trust User Input, Forever.
就算是在後台也一樣
不管是前台還是後台,都要要檢查使用者輸入資料格式、範圍是否正確 並且正確的 escape 掉所有的 SQL 和 HTML 如此一來,你的網站安全就能夠大大的提升了
好了,我已經講完了
「等等,這不是有講跟沒講一樣嗎?」
摁,所以來講些實作上的東西
Never Trust User Input Data
- 取得使用者IP?
- HTTP_X_FORWARDED_FORHTTP_CLIENT_IPREMOTE_ADDR請全部記下來...
- 如果我要限制使用者IP呢?
- 只能相信
REMOTE_ADDR
,因為其他的通通都能輕易偽造
- 只能相信
登入系統絕對不能只靠cookie判斷
if ($_COOKIE['user_login'] == "admin") { echo "歡迎回來,管理員大大"; }
javascript: document.cookie = "user_login=admin"; // Hi, I'm cracker.
- SESSION是你的好夥伴
- SESSION ID請使用 http-only 的cookie來存,確保JS讀不到
- SESSION內存放登入有效期限,超過就強制登出
組合 SQL 前,檢查並且escape所有放進去的參數
- mysqli_real_escape_string // mysql_* 要廢棄了,全面改用mysqli
- mysqli::prepare
- PDO
- ORM framework
- IP位址也要escape...
- HTTP_X_FORWARDED_FOR 也能注
- 如果你要把資料從DB query出來,再存進去,記得也要再次escape
- 延伸閱讀:2nd sql injection
- 絕對要使用正確的方式做escape
絕對不要 認為 htmlentities
可以保護你的資料庫
- But... XSS還是要靠他
輸出前記得要escape html擋XSS
- htmlentities
- 使用 ENT_IGNORE時請小心
- 可以利用文字編碼繞過 escape
- 統一入資料庫前或者輸出前escape一次就好
我遇過被資料escape兩次的,然後編輯資料我就要手動unescape一次 ... ~~幹....~~
CAPTCHA 的內容絕對不要放 cookie 或者img的alt...
<img src="captcha.php?code=dr5gh" alt="請輸入:dr5gh" id="img_captcha" />
var captcha = document.querySelector("#captcha").alt.substr(4);
- 乖乖用SESSION啦
重要的操作請勿使用GET方法
<img src="http://your.website.com/user/delete_my_account.php?confirm=yes" />
- 你看你又悲劇了
- POST + one time token + check referer
<input type="hidden" name="token" value="blablablabla_store_in_session" />
避免不安全的使用eval()
Server儲存密碼的時候請使用hash處理過
$pwd = sha1(sha1($_POST['password']) + SALT);
- 表單送出密碼時,先用javascript處理過
- input_pwd.value = sha1(sha1(input_pwd.value) + salt)
- 萬一你的user被監聽了,至少不會被拿到原始密碼
if (!DEBUG) error_reporting(0);
- error噴出來的資料可能會洩漏一些資訊
- Ex: username, path...
- 為了安全,不顯示錯誤
就先寫到這裡吧,最後來個留言板Demo... http://pastebin.com/z77Tx3ra