文章轉自: https://learnku.com/php/t/24930
更多文章: https://learnku.com/laravel/c...
相對於其餘幾種語言來講, PHP 在 web 建站方面有更大的優點,即便是新手,也能很容易搭建一個網站出來。但這種優點也容易帶來一些負面影響,由於不少的 PHP 教程沒有涉及到安全方面的知識。php
此帖子分爲幾部分,每部分會涵蓋不一樣的安全威脅和應對策略。可是,這並非說你作到這幾點之後,就必定能避免你的網站出現任何問題。若是你想提升你的網站安全性的話,你應該繼續經過閱讀書籍或者文章,來研究如何提升你的網站安全性html
出於演示須要,代碼可能不是很完美。平常開發過程當中,不少代碼都包含在了框架跟各類庫裏面。做爲一個後臺開發,你不只要熟練基本的CURD,更要知道如何保護你的數據。html5
我賭一包辣條,你確定會看到這裏。 SQL 注入是對您網站最大的威脅之一,若是您的數據庫受到別人的 SQL 注入的攻擊的話,別人能夠轉出你的數據庫,也許還會產生更嚴重的後果。laravel
網站要從數據庫中獲取動態數據,就必須執行 SQL 語句,舉例以下:git
<?php $username = $_GET['username']; $query = "SELECT * FROM users WHERE username = '$username'";
攻擊者控制經過 GET 和 POST 發送的查詢(或者例如 UA 的一些其餘查詢)。通常狀況下,你但願查詢戶名爲「 peter 」的用戶產生的 SQL 語句以下:github
SELECT * FROM users WHERE username = 'peter'
可是,攻擊者發送了特定的用戶名參數,例如:' OR '1'='1web
這就會致使 SQL 語句變成這樣:算法
SELECT * FROM users WHERE username = 'peter' OR '1' = '1'
這樣,他就能在不須要密碼的狀況下導出你的整個用戶表的數據了。sql
那麼,咱們如何防止這類事故的發生呢?主流的解決方法有兩種。轉義用戶輸入的數據或者使用封裝好的語句。轉義的方法是封裝好一個函數,用來對用戶提交的數據進行過濾,去掉有害的標籤。可是,我不太推薦使用這個方法,由於比較容易忘記在每一個地方都作此處理。shell
下面,我來介紹如何使用 PDO 執行封裝好的語句( mysqi 也同樣):
$username = $_GET['username']; $query = $pdo->prepare('SELECT * FROM users WHERE username = :username'); $query->execute(['username' => $username]); $data = $query->fetch();
動態數據的每一個部分都以:作前綴。而後將全部參數做爲數組傳遞給執行函數,看起來就像 PDO 爲你轉義了有害數據同樣。
幾乎全部的數據庫驅動程序都支持封裝好的語句,沒有理由不使用它們!養成使用他們的習慣,之後就不會忘記了。
你也能夠參考 phpdelusions 中的一篇關於動態構建 SQL 查詢時處理安全問題的文章。連接: https://phpdelusions.net/pdo/... 。
XSS 又叫 CSS (Cross Site Script) ,跨站腳本攻擊。它指的是惡意攻擊者往 Web 頁面裏插入惡意 html 代碼,當用戶瀏覽該頁之時,嵌入其中 Web 裏面的 html 代碼會被執行,從而達到惡意攻擊用戶的特殊目的。
下面以一個搜索頁面爲例子:
<body> <?php $searchQuery = $_GET['q']; /* some search magic here */ ?> <h1>You searched for: <?php echo $searchQuery; ?></h1> <p>We found: Absolutely nothing because this is a demo</p> </body>
由於咱們把用戶的內容直接打印出來,不通過任何過濾,非法用戶能夠拼接 URL:
search.php?q=%3Cscript%3Ealert(1)%3B%3C%2Fscript%3E
PHP 渲染出來的內容以下,能夠看到 Javascript 代碼會被直接執行:
<body> <h1>You searched for: <script>alert(1);</script></h1> <p>We found: Absolutely nothing because this is a demo</p> </body>
問:JS 代碼被執行有什麼大不了的?
Javascript 能夠:
問:如何防範此問題呢?
好消息是比較先進的瀏覽器如今已經具有了一些基礎的 XSS 防範功能,不過請不要依賴與此。
正確的作法是堅定不要相信用戶的任何輸入,並過濾掉輸入中的全部特殊字符。這樣就能消滅絕大部分的 XSS 攻擊:
<?php $searchQuery = htmlentities($searchQuery, ENT_QUOTES);
或者你可使用模板引擎 Twig ,通常的模板引擎都會默認爲輸出加上 htmlentities
防範。
若是你保持了用戶的輸入內容,在輸出時也要特別注意,在如下的例子中,咱們容許用戶填寫本身的博客連接:
<body> <a href="<?php echo $homepageUrl; ?>">Visit Users homepage</a> </body>
以上代碼可能第一眼看不出來有問題,可是假設用戶填入如下內容:
#" onclick="alert(1)
會被渲染爲:
<body> <a href="#" onclick="alert(1)">Visit Users homepage</a> </body>
永遠永遠不要相信用戶輸入的數據,或者,永遠都假設用戶的內容是有攻擊性的,態度端正了,而後當心地處理好每一次的用戶輸入和輸出。
另外一個控制 XSS 攻擊的方法是提供一個 CSP Meta 標籤,或者標頭信息,更多詳情請見: https://www.html5rocks.com/en...
另外種 Cookie 時,若是無需 JS 讀取的話,請必須設置爲 "HTTP ONLY"。這個設置能夠令 JavaScript 沒法讀取 PHP 端種的 Cookie。
CSRF 是跨站請求僞造的縮寫,它是攻擊者經過一些技術手段欺騙用戶去訪問曾經認證過的網站並運行一些操做。
雖然此處展現的例子是 GET 請求,但只是相較於 POST 更容易理解,並不是防禦手段,二者都不是私密的 Cookies 或者多步表單。
假如你有一個容許用戶刪除帳戶的頁面,以下所示:
<?php //delete-account.php $confirm = $_GET['confirm']; if($confirm === 'yes') { //goodbye }
攻擊者能夠在他的站點上構建一個觸發這個 URL 的表單(一樣適用於 POST 的表單),或者將 URL 加載爲圖片誘惑用戶點擊:
<img src="https://example.com/delete-account.php?confirm=yes" />
用戶一旦觸發,就會執行刪除帳戶的指令,眨眼你的帳戶就消失了。
防護這樣的攻擊比防護 XSS 與 SQL 注入更復雜一些。
最經常使用的防護方法是生成一個 CSRF 令牌加密安全字符串,通常稱其爲 Token,並將 Token 存儲於 Cookie 或者 Session 中。
每次你在網頁構造表單時,將 Token 令牌放在表單中的隱藏字段,表單請求服務器之後會根據用戶的 Cookie 或者 Session 裏的 Token 令牌比對,校驗成功纔給予經過。
因爲攻擊者沒法知道 Token 令牌的內容(每一個表單的 Token 令牌都是隨機的),所以沒法冒充用戶。
<?php /* 你嵌入表單的頁面 */ ?> <form action="/delete-account.php" method="post"> <input type="hidden" name="csrf" value="<?php echo $_SESSION['csrf']; ?>"> <input type="hidden" name="confirm" value="yes" /> <input type="submit" value="Delete my account" /> </form> ## <?php //delete-account.php $confirm = $_POST['confirm']; $csrf = $_POST['csrf']; $knownGoodToken = $_SESSION['csrf']; if($csrf !== $knownGoodToken) { die('Invalid request'); } if($confirm === 'yes') { //goodbye }
請注意,這是個很是簡單的示例,你能夠加入更多的代碼。若是你使用的是像 Symfony 這樣的 PHP 框架,那麼自帶了 CSRF 令牌的功能。
你還能夠查看關於 OWASP 更詳細的問題和更多防護機制的文章: https://github.com/OWASP/CheatS....
LFI (本地文件包含) 是一個用戶未經驗證從磁盤讀取文件的漏洞。
我常常遇到編程不規範的路由代碼示例,它們不驗證過濾用戶的輸入。咱們用如下文件爲例,將它要渲染的模板文件用 GET 請求加載。
<body> <?php $page = $_GET['page']; if(!$page) { $page = 'main.php'; } include($page); ?> </body>
因爲 Include 能夠加載任何文件,不只僅是PHP,攻擊者能夠將系統上的任何文件做爲包含目標傳遞。
index.php?page=../../etc/passwd
這將致使 /etc/passwd 文件被讀取並展現在瀏覽器上。
要防護此類攻擊,你必須仔細考慮容許用戶輸入的類型,並刪除可能有害的字符,如輸入字符中的「.」 「/」 「」。
若是你真的想使用像這樣的路由系統(我不建議以任何方式),你能夠自動附加 PHP 擴展,刪除任何非 [a-zA-Z0-9-_] 的字符,並指定從專用的模板文件夾中加載,以避免被包含任何非模板文件。
我在不一樣的開發文檔中,屢次看到形成此類漏洞的 PHP 代碼。從一開始就要有清晰的設計思路,容許所須要包含的文件類型,並刪除掉多餘的內容。你還能夠構造要讀取文件的絕對路徑,並驗證文件是否存在來做爲保護,而不是任何位置都給予讀取。
大部分的 Web 應用須要保存用戶的認證信息。若是密碼哈希作的足夠好,在你的網站被攻破時,便可保護用戶的密碼不被非法讀取。
首先,最不該該作的事情,就是把用戶密碼明文儲存起來。大部分的用戶會在多個網站上使用同一個密碼,這是不可改變的事實。當你的網站被攻破,意味着用戶的其餘網站的帳號也被攻破了。
其次,你不該該使用簡單的哈希算法,事實上全部沒有專門爲密碼哈希優化的算法都不該使用。哈希算法如 MD5 或者 SHA 設計初衷就是執行起來很是快。這不是你須要的,密碼哈希的終極目標就是讓黑客花費無窮盡的時間和精力都沒法破解出來密碼。
另一個比較重要的點是你應該爲密碼哈希加鹽(Salt),加鹽處理避免了兩個一樣的密碼會產生一樣哈希的問題。
如下使用 MD5 來作例子,因此請千萬不要使用 MD5 來哈希你的密碼, MD5 是不安全的。
假如咱們的用戶 user1
和 user315
都有相同的密碼 ilovecats123
,這個密碼雖然看起來是強密碼,有字母有數字,可是在數據庫裏,兩個用戶的密碼哈希數據將會是相同的:5e2b4d823db9d044ecd5e084b6d33ea5
。
若是一個若是黑客拿下了你的網站,獲取到了這些哈希數據,他將不須要去暴力破解用戶 user315
的密碼。咱們要儘可能讓他花大精力來破解你的密碼,因此咱們對數據進行加鹽處理:
<?php //warning: !!這是一個很不安全的密碼哈希例子,請不要使用!! $password = 'cat123'; $salt = random_bytes(20); $hash = md5($password . $salt);
最後在保存你的惟一密碼哈希數據時,請不要忘記連 $salt
也已經保存,不然你將沒法驗證用戶。
在當下,最好的密碼哈希選項是 bcrypt
,這是專門爲哈希密碼而設計的哈希算法,同時這套哈希算法裏還容許你配置一些參數來加大破解的難度。
新版的 PHP 中也自帶了安全的密碼哈希函數 password_hash ,此函數已經包含了加鹽處理。對應的密碼驗證函數爲 password_verify 用來檢測密碼是否正確。password_verify
還可有效防止 時序攻擊.
如下是使用的例子:
<?php //user signup $password = $_POST['password']; $hashedPassword = password_hash($password, PASSWORD_DEFAULT); //login $password = $_POST['password']; $hash = '1234'; //load this value from your db if(password_verify($password, $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; }
須要澄清的一點是:密碼哈希並非密碼加密。哈希(Hash)是將目標文本轉換成具備相同長度的、不可逆的雜湊字符串(或叫作消息摘要),而加密(Encrypt)是將目標文本轉換成具備不一樣長度的、可逆的密文。顯然他們之間最大的區別是可逆性,在儲存密碼時,咱們要的就是哈希這種不可逆的屬性。
MITM (中間人) 攻擊不是針對服務器直接攻擊,而是針對用戶進行,攻擊者做爲中間人欺騙服務器他是用戶,欺騙用戶他是服務器,從而來攔截用戶與網站的流量,並從中注入惡意內容或者讀取私密信息,一般發生在公共 WiFi 網絡中,也有可能發生在其餘流量經過的地方,例如ISP運營商。
對此的惟一防護是使用 HTTPS,使用 HTTPS 能夠將你的鏈接加密,而且沒法讀取或者篡改流量。你能夠從 Let's Encrypt 獲取免費的 SSL 證書,或從其餘供應商處購買,這裏不詳細介紹如何正確配置 WEB 服務器,由於這與應用程序安全性無關,且在很大程度上取決於你的設置。
你還能夠採起一些措施使 HTTPS 更安全,在 WEB 服務器配置加上 Strict-Transport-Security 標示頭,此頭部信息告訴瀏覽器,你的網站始終經過 HTTPS 訪問,若是未經過 HTTPS 將返回錯誤報告提示瀏覽器不該顯示該頁面。
然而,這裏有個明顯的問題,若是瀏覽器以前從未訪問過你的網站,則沒法知道你使用此標示頭,這時候就須要用到 Hstspreload。
能夠在此註冊你的網站: https://hstspreload.org/
你在此處提交的全部網站都將被標記爲僅 HTTPS,並硬編碼到 Google Chrome、FireFox、Opera、Safari、IE11 和 Edge 的源代碼中。
你還能夠在 DNS 配置中添加 Certification Authority Authorization (CAA) record ,能夠僅容許一個證書頒發機構(例如: Let's encrypt)發佈你的域名證書,這進一步提升了用戶的安全性。
這多是服務器遇到的最嚴重的攻擊,命令注入的目標是欺騙服務器執行任意 Shell 命令
你若是使用 shell_exec 或是 exec 函數。讓咱們作一個小例子,容許用戶簡單的從服務器 Ping 不一樣的主機。
<?php $targetIp = $_GET['ip']; $output = shell_exec("ping -c 5 $targetIp");
輸出將包括對目標主機 Ping 5次。除非採用 sh 命令執行 Shell 腳本,不然攻擊者能夠執行想要的任何操做。
ping.php?ip=8.8.8.8;ls -l /etc
Shell 將執行 Ping 和由攻擊者拼接的第二個命令,這顯然是很是危險的。
感謝 PHP 提供了一個函數來轉義 Shell 參數。
escapeshellarg 轉義用戶的輸入並將其封裝成單引號。
<?php $targetIp = escapeshellarg($_GET['ip']); $output = shell_exec("ping -c 5 $targetIp");
如今你的命令應該是至關安全的,就我的而言,我仍然避免使用 PHP 調用外部命令,但這徹底取決於你本身的喜愛。
另外,我建議進一步驗證用戶輸入是否符合你指望的形式。
XXE (XML 外部實體) 是一種應用程序使用配置不正確的 XML 解析器解析外部 XML 時,致使的本地文件包含攻擊,甚至能夠遠程代碼執行。
XML 有一個不爲人知的特性,它容許文檔做者將遠程和本地文件做爲實體包含在其 XML 文件中。
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY passwd SYSTEM "file:///etc/passwd" >]> <foo>&passwd;</foo>
就像這樣, /etc/passwd 文件內容被轉儲到 XML 文件中。
若是你使用 libxml 能夠調用 libxml_disable_entity_loader 來保護本身免受此類攻擊。使用前請仔細檢查 XML 庫的默認配置,以確保配置成功。
[](https://secure.php.net/manual...,可能會在生產環境中由於不正確的錯誤報告泄露了敏感信息,例如:文件夾結構、數據庫結構、鏈接信息與用戶信息。
你是不但願用戶看到這個的吧?
通常根據你使用的框架或者 CMS ,配置方法會有不一樣的變化。一般框架具備容許你將站點更改成某種生產環境的設置。這樣會將全部用戶可見的錯誤消息重定向到日誌文件中,並向用戶顯示非描述性的 500 錯誤,同時容許你根據錯誤代碼檢查。
可是你應該根據你的 PHP 環境設置: error_reporting 與 display_errors.
像登陸這樣的敏感表單應該有一個嚴格的速率限制,以防止暴力攻擊。保存每一個用戶在過去幾分鐘內失敗的登陸嘗試次數,若是該速率超過你定義的閾值,則拒絕進一步登陸嘗試,直到冷卻期結束。還可經過電子郵件通知用戶登陸失敗,以便他們知道本身的帳戶被成爲目標。
我不是一個安全專家,恐沒法作到事無鉅細。儘管編寫安全軟件是一個很是痛苦的過程,但仍是能夠經過遵循一些基本規則,編寫合理安全的應用程序。其實,不少框架在這方面也幫咱們作了不少工做。
在問題發生以前,安全性問題並不像語法錯誤等能夠在開發階段追蹤到。所以,在編寫代碼的過程當中,應該時刻有規避安全風險的意識。若是你迫於業務需求的壓力而不得不暫時忽略一些安全防範的工做,我想你有必要事先告知你們這樣作的潛在風險。
若是你從這篇文章有所收益,也請把它分享給你的朋友們把,讓咱們共建安全網站。
文章轉自: https://learnku.com/php/t/24930
更多文章: https://learnku.com/laravel/c...