摘要:瀏覽器的同源策略并不能夠避免來自于惡意站點的提交。為了保證輸入數(shù)據(jù)的完整性,服務(wù)器端務(wù)必要進(jìn)行數(shù)據(jù)校驗。輸入驗證即是保證實際輸入與應(yīng)用預(yù)期的輸入的一致性。因此驗證輸入時保證系統(tǒng)安全性與防衛(wèi)危險的第一道防線。
Github Repo:https://github.com/wxyyxc1992/infosecurity-handbook/blob/master/Reinforce/WebSecurity/basics-of-web-application-security.md
原文:The Basics of Web Application Security
現(xiàn)代的軟件開發(fā)者已經(jīng)有點像瑞士軍刀了,首先,你需要來保證完成用戶的功能或者業(yè)務(wù)需求,并且要保證又快又好地完成。其次,你希望你的代碼能夠擁有充分的可理解性或者可擴展性:能夠隨著IT需求的快速變遷而有著充分的擴展空間,與此同時還需要穩(wěn)定與可用。開發(fā)者必須列舉出有用的接口,優(yōu)化數(shù)據(jù)庫,以及頻繁地建立或者維護(hù)一個交付渠道。不過,當(dāng)我們審視這長長的需求列表的時候,在快速、低成本以及靈活可擴展之下的,即是安全性?;蛟S直到一些東西出了問題,或者你構(gòu)建的系統(tǒng)被攻擊了之后才能深刻感受到安全才是最重要的。安全這個概念有點像性能,是個泛化的跨越了多個領(lǐng)域的概念。所以一個開發(fā)者怎么才能在模糊的安全需求與未知的風(fēng)險面前選擇合適的開發(fā)規(guī)劃呢?當(dāng)然如果能夠明確這些安全需求與定位到威脅的話毫無疑問非常值得推薦,但是這個準(zhǔn)備本身就需要耗費大量的時間與金錢。
Trust(信賴)首先,在討論具體的輸入輸出之前,我們需要來強調(diào)下自認(rèn)為安全中最重要也是最根本的原則:Trust。作為一個開發(fā)者,也需要不斷地問自己,我們相信來自于用戶瀏覽器的請求嗎?我們相信上游系統(tǒng)正常工作來保證了我們數(shù)據(jù)的干凈與安全嗎?我們相信服務(wù)器與瀏覽器之間的信道就不會被監(jiān)聽或者偽造嗎?我們相信我們系統(tǒng)本身依賴的服務(wù)或者數(shù)據(jù)存儲嗎?呵呵,都不可信。
當(dāng)然,就像安全一樣,Trust也不是一個雙選題,非黑即白。我們需要明白系統(tǒng)的風(fēng)險忍受力與數(shù)據(jù)的安全邊界。為了能夠正確的、基于某個統(tǒng)一規(guī)則的預(yù)估,我們需要審視威脅與風(fēng)險,這個評估方法與標(biāo)準(zhǔn)會在另一篇文章中講解。
Reject Unexpected Form Input(拒絕未知的表單輸入)HTML表單本身就可能帶來些好像很安全的錯覺,表單的構(gòu)建者肯定覺得他們限制了輸入類型、做了數(shù)據(jù)校驗,這樣整個表單輸入就是安全的。但確信無疑的是,這只是個錯覺,盡管客戶端地JavaScript腳本可以從安全地角度來說提供完整的校驗。
Untrusted Input無論我們是否在客戶端提供了表單驗證或者是否使用了HTTPs連接,我們能夠信賴來自用戶瀏覽器的連接的比例都是0。用戶可以輕易地在發(fā)送之前修改標(biāo)記,或者使用類似于curl這樣的命令行來提交沒有經(jīng)過校驗的數(shù)據(jù)。乃至于一個不明所以的用戶可能在一個懷有惡意的網(wǎng)站莫名其妙地添了些內(nèi)容。瀏覽器的同源策略并不能夠避免來自于惡意站點的提交。為了保證輸入數(shù)據(jù)的完整性,服務(wù)器端務(wù)必要進(jìn)行數(shù)據(jù)校驗。
不過估計有人有疑問了,為啥說這個畸形的數(shù)據(jù)就會導(dǎo)致安全問題呢?這往往取決于你的應(yīng)用業(yè)務(wù)邏輯與輸出的編碼,為了避免不可預(yù)知的行為、數(shù)據(jù)泄露與潛在攻擊,需要在輸入的數(shù)據(jù)與可執(zhí)行代碼之間架構(gòu)一個過濾層。譬如,我們的表單里有一個選擇的按鈕來允許用戶選擇合適的通信類型,我們的業(yè)務(wù)邏輯代碼可能是這樣的:
final String communicationType = req.getParameter("communicationType"); if ("email".equals(communicationType)) { sendByEmail(); } else if ("text".equals(communicationType)) { sendByText(); } else { sendError(resp, format("Can"t send by type %s", communicationType)); }
上面代碼危不危險取決于sendError這個方法是怎么定義的,而我們肯定無法確定下游的代碼就一定是安全的。最好的選擇就是我們在控制流中移除這個危險,而使用的方法就是輸入驗證。
Input Validation輸入驗證即是保證實際輸入與應(yīng)用預(yù)期的輸入的一致性。超出預(yù)期的輸入數(shù)據(jù)會導(dǎo)致我們系統(tǒng)拋出未知的結(jié)果,譬如邏輯崩壞、觸發(fā)錯誤乃至于允許攻擊者控制系統(tǒng)的一部分。其中像數(shù)據(jù)庫查詢這樣的能夠在服務(wù)端作為可執(zhí)行代碼的輸入與JavaScript這樣在客戶端能夠被執(zhí)行的代碼更是特別的危險。因此驗證輸入時保證系統(tǒng)安全性與防衛(wèi)危險的第一道防線。
開發(fā)者們在構(gòu)建應(yīng)用系統(tǒng)的過程中會進(jìn)行一些基本的驗證,譬如判斷值是否為空或者是否為正數(shù)。而從安全的角度考慮,我們需要將輸入限定到系統(tǒng)允許的最小集合中,譬如數(shù)值型值可以被限定在某個特定的范圍內(nèi)。譬如,系統(tǒng)不會允許用戶將一個負(fù)值添加到購物車中。這種限制性的驗證手段就是所謂的positive validation或者whitelisting。一個白名單可以用于限定某個具體的URL或者yyyy/mm/dd這樣的時間日期。它可以限制輸入的長度、單個字符的編碼規(guī)范或者上面例子中的只有給定值可以被接受。
另外一種考慮輸入驗證的思維角度就是把它當(dāng)做服務(wù)端與消費者之間簽訂的一種協(xié)議,任何違背了這個協(xié)議的請求都是無效的并且被拒絕。你的這個協(xié)議越嚴(yán)格,你的系統(tǒng)在未知情況下遭受的風(fēng)險就會越小。而當(dāng)對于某個輸入驗證失敗之后,開發(fā)者也要好好考慮應(yīng)該如何反饋。最嚴(yán)格,也是最有爭議的辦法就是全部拒絕,并且沒有任何反饋,不過要注意將這個事情通過日志或者監(jiān)控記錄下來。不過為啥一點反饋都沒有呢?我們需要提供給用戶哪些信息是無效的嗎?這一點還是要取決于你的約定。在上面的例子中,如果你接收到了除了email或者text之外的內(nèi)容,那你有可能被攻擊了。不過如果你進(jìn)行了反饋,可能正中全套。譬如如果開發(fā)者直接返回:俺們并不認(rèn)識你傳入的communicationType,可能這個還無傷大雅,但是如果是這樣的呢:
這種情況下你就會面臨一個用來盜取你的Cookies的XSS攻擊代碼,如果你一定要給用戶反饋,你必須保證不會把不受信任的用戶內(nèi)容直接返回,而應(yīng)該使用固定的提示信息。如果你不可避免地要把用戶的輸入反饋回去,你要保證它是被編碼的。
In Practice實踐中,我們經(jīng)常要通過過濾標(biāo)簽來避免一些攻擊,過濾掉這些包含著危險值的輸入的方法就是所謂的negative validation 或者blacklisting。不過這種方法的麻煩之處在于潛在的危險輸入是相當(dāng)多的,很多時候不能勝數(shù)。維持一個龐大的危險輸入的列表可能是耗費較大的操作,這需要進(jìn)行頻繁地更新。如果你真的需要一個黑名單,這就需要覆蓋你所有的測試用例,編寫高質(zhì)量的測試代碼,遵循OWASP的XSS_Filter_Evasion_Cheat_Sheet原則。
對于危險輸入的過濾,我們常常稱之為sanitization,即是在輸入中移除那些黑名單中的元素而不是直接拒絕。不過這種黑名單機制,很難保證完全正確,往往也會給攻擊者更多地漏洞機會。譬如,在上面的例子中,我們是選擇移除標(biāo)簽,而一個攻擊者可以通過輸入下面這樣的字符串來逃避檢查:
ipt>
在現(xiàn)代的Web應(yīng)用程序開發(fā)中,已經(jīng)有很多現(xiàn)成的框架提供了基礎(chǔ)的過濾功能。內(nèi)建的對于郵件地址、信用卡號碼等等過濾還是灰常好用的,使用這些網(wǎng)絡(luò)框架提供的驗證機制可以有效避免一些嚴(yán)重的錯誤,譬如:
Framework | Approaches |
---|---|
Java | Hibernate (Bean Validation) |
ESAPI | |
Spring | Built-in type safe params in Controller |
Built-in Validator interface (Bean Validation) | |
Ruby on Rails | Built-in Active Record Validators |
ASP.NET | Built-in Validation (see BaseValidator) |
Play | Built-in Validator |
Generic JavaScript | xss-filters |
NodeJS | validator-js |
General | Regex-based validation on application inputs |
盡可能地使用白名單
在不能用白名單的時候用黑名單
盡可能地使用嚴(yán)格約定
確保警示潛在的攻擊
避免直接地輸入反饋
盡可能地在不可信數(shù)據(jù)深入系統(tǒng)邏輯之前進(jìn)行處理,或者直接使用你的框架的白名單機制
Encode HTML Output:HTML輸出內(nèi)容編碼除了上述所說的對于輸入的過濾與限制之外,Web應(yīng)用的開發(fā)者還需要關(guān)注返回的數(shù)據(jù)。一個現(xiàn)代的Web應(yīng)用往往會用HTML標(biāo)記來構(gòu)建文檔結(jié)構(gòu),用CSS來構(gòu)建文檔樣式,JavaScript來構(gòu)建應(yīng)用邏輯。一個HTML文檔通常使用像或者這樣的標(biāo)記來進(jìn)行切割。用戶可能在沒料到的地方使用尖括號,特別是在一個可執(zhí)行的上下文中附著一些特定內(nèi)容,譬如HTML與JavaScript都會包含URL,只不過它們各有各的處理方法。
Output RisksHTML本身是一個非常非常寬松自由的格式,瀏覽器會盡可能地來渲染內(nèi)容,即使發(fā)現(xiàn)了些格式錯誤。這一點對于開發(fā)者非常友好,不過這也是一個很大的漏洞的源泉。攻擊者可能在向你的頁面中注入一些內(nèi)容來打破可執(zhí)行的上下文,甚至不需要考慮整個頁面是否有效。處理輸出內(nèi)容并不一定是安全地考慮,應(yīng)用從數(shù)據(jù)庫與上游服務(wù)中獲取的渲染數(shù)據(jù)務(wù)必保證不會影響到整個應(yīng)用,不過從不可信的數(shù)據(jù)源得到的渲染內(nèi)容還是會存在很大的風(fēng)險。就如上文所說的,開發(fā)者應(yīng)該拒絕那些超出約定的輸入,不過有時候我們不可避免的需要接收像尖括號那樣的可能更改我們的代碼的內(nèi)容,這也就是下面所說的output encoding所需要起作用的地方。
Output Encoding輸出編碼即是將輸出的數(shù)據(jù)流轉(zhuǎn)化為最終的輸出的格式,輸出編碼的難點或者復(fù)雜的地方在于需要根據(jù)輸出數(shù)據(jù)流向的不同選定不同的編碼方式。如果沒有合適的編碼方式,應(yīng)用可能會給客戶端提供一個錯誤格式的數(shù)據(jù),導(dǎo)致輸出數(shù)據(jù)不可用乃至于存在一定風(fēng)險。攻擊者往往會利用錯誤的編碼方式中的漏洞使得整個輸出數(shù)據(jù)的結(jié)構(gòu)失控。譬如在我們的電商系統(tǒng)中有個用戶叫做Sandra Day O"Connor,系統(tǒng)會在該用戶登錄的時候輸出一個歡迎辭,那么當(dāng)她的名字渲染到HTML頁面上的時候,效果大概是這樣的:
The Honorable Justice Sandra Day O"Connor
The Honorable Justice Sandra Day O"Connor
開發(fā)者期望的正是這樣渲染得出的界面,不過如果我們是基于MVC架構(gòu)開發(fā)出了一個動態(tài)網(wǎng)頁,名字會通過JS腳本動態(tài)地插入到頁面中,示例代碼如下:
document.getElementById("name").innerText = "Sandra Day O"Connor" //<--unescaped string
而這樣的代碼正是攻擊者們孜孜不倦尋找的漏洞點來執(zhí)行他們的自定義代碼,如果另一個用戶Chief Justice這樣的輸入他的名字:
Sandra Day O";window.location="http://evil.martinfowler.com/";
那么所有看到他名字頁面的用戶都會被重定向到一個危險的站點,而如果我們應(yīng)用正確地在這個JS上下文中進(jìn)行了編碼,整個編碼之后的文本如下所示:
"Sandra Day O";window.location="http://evil.martinfowler.com/";"
那么整個文本雖然看上去有點雜亂,但是已經(jīng)變成了沒有任何危害的不可執(zhí)行的代碼。一般來說我們有很多種方式可以來對JS進(jìn)行編碼,最常見的就是使用轉(zhuǎn)義字符。
好消息是絕大部分現(xiàn)代Web框架都提供了將內(nèi)容安全編碼與過濾保留字符的功能。不過大部分開發(fā)者會忽略乃至于主動關(guān)閉這種過濾編碼功能從而去執(zhí)行他們自認(rèn)為的安全的可執(zhí)行代碼。
Cautions and Caveats關(guān)于輸出編碼這部分還有幾個需要了解的地方,重要的事情多強調(diào)幾遍,一定要選擇一個自帶編碼功能的框架。另外還需要注意的是,盡管一個框架可以安全地渲染HTML,也不代表他可以安全地渲染PDF或者JavaScript。
另一個我們在應(yīng)用開發(fā)過程中經(jīng)常遇到的情況,就是很多開發(fā)者習(xí)慣在獲取用戶的原始輸入后進(jìn)行編碼然后存入數(shù)據(jù)庫中。譬如如果你在將數(shù)據(jù)入庫之前就把純文本格式的數(shù)據(jù)編碼成HTML格式,然后在需要渲染成其他格式的地方還需要先把HTML反編碼然后再編譯成其他格式。這無端會增加很多復(fù)雜性和額外的工作,因此最好直接以原始數(shù)據(jù)格式存入到數(shù)據(jù)庫中,然后在渲染時在將它們進(jìn)行編碼。
In Summary以合適的編碼手段對所有從應(yīng)用中吐出的數(shù)據(jù)進(jìn)行編碼
盡可能地使用框架提供的輸出編碼功能
盡量避免嵌入式渲染上下文
以原始格式存放數(shù)據(jù),在渲染時進(jìn)行編碼
避免使用不安全的框架與規(guī)避了編碼的JS調(diào)用
Bind Parameters for Database Queries不管你是用SQL在一個關(guān)系型數(shù)據(jù)庫中進(jìn)行查詢,還是用ORM框架,或者直接使用一個NoSQL數(shù)據(jù)庫,你都需要考慮到怎么把用戶輸入的數(shù)據(jù)集成進(jìn)你的查詢語句中。數(shù)據(jù)庫可能是一個Web應(yīng)用程序中最關(guān)鍵與緊要的部分,因為它存放了大量的無法重現(xiàn)的狀態(tài)數(shù)據(jù)。譬如一個數(shù)據(jù)庫中可能存放著大量的關(guān)鍵的與敏感的客戶信息,也正是這些數(shù)據(jù)驅(qū)動著整個應(yīng)用與邏輯的運行。因此你肯定希望開發(fā)者在和數(shù)據(jù)庫打交道的時候要慎之又慎,不過數(shù)據(jù)庫注入攻擊還是很盛行啊。
Little Bobby Tables很多關(guān)于數(shù)據(jù)庫中參數(shù)綁定的討論都會包含著名的2007年的Little Bobby Tables事件,這個事件可以用一個漫畫描述如下:
為了便于分析這個漫畫所要表達(dá)的含義,我們假設(shè)這個成績追蹤系統(tǒng)有一個用于增加新的學(xué)生信息的函數(shù):
void addStudent(String lastName, String firstName) { String query = "INSERT INTO students (last_name, first_name) VALUES ("" + lastName + "", "" + firstName + "")"; getConnection().createStatement().execute(query); }
如果輸入的參數(shù)是"Fowler"與"Martin",那么最終構(gòu)造出的SQL語句為:
INSERT INTO students (last_name, first_name) VALUES ("Fowler", "Martin")
不過如果輸入的是上面那娃的名字,那么整個待執(zhí)行的SQL語句就變成了:
INSERT INTO students (last_name, first_name) VALUES ("XKCD", "Robert’); DROP TABLE Students;-- ")
實際上,這個SQL語句一共執(zhí)行了兩個操作:
INSERT INTO students (last_name, first_name) VALUES ("XKCD", "Robert") DROP TABLE Students
最后的--注釋是為了屏蔽余下的內(nèi)容,保證整個SQL語句能夠穩(wěn)定執(zhí)行。類似于這樣的攻擊載荷能夠執(zhí)行任意的SQL語句,換言之,攻擊者能夠在數(shù)據(jù)庫內(nèi)像這個應(yīng)用系統(tǒng)一樣做任何事情。
采用參數(shù)綁定來解決這個問題對于上文描述的這種場景,如果只是依賴于簡單的清洗過濾,肯定無法應(yīng)付所有的攻擊載荷,這也不是一個正道?;旧夏軌虿扇〉姆椒ň褪撬^的參數(shù)綁定,譬如JDBC中提供的PreparedStatement.setXXX()方法,參數(shù)綁定可以將像SQL這樣的可執(zhí)行代碼與需要進(jìn)行編碼、過濾的內(nèi)容區(qū)分開來:
void addStudent(String lastName, String firstName) { PreparedStatement stmt = getConnection().prepareStatement("INSERT INTO students (last_name, first_name) VALUES (?, ?)"); stmt.setString(1, lastName); stmt.setString(2, firstName); stmt.execute(); }
一般來說,一個功能比較全面地數(shù)據(jù)訪問層都會提供這種參數(shù)綁定的功能,開發(fā)者在開發(fā)的時候就要注意將所有的不受信任的輸入通過參數(shù)綁定生成SQL語句。
Clean and Safe Code有時候我們開發(fā)時會遇到一個兩難的問題,即是好的安全性與干凈整潔的代碼之間的沖突。為了保證安全性往往需要我們增加些額外的代碼,不過在上面的例子中我們還是同時達(dá)成了較高的安全性與好的代碼設(shè)計。使用綁定的參數(shù)不僅能使應(yīng)用系統(tǒng)免于注入攻擊,還能通過在代碼與內(nèi)容之間構(gòu)建清晰的邊界來增加整個代碼的可讀性,并且與手動拼接相比還能大大簡化構(gòu)造可用的SQL的過程。當(dāng)你用參數(shù)綁定來代替原本的格式化字符串或者字符串拼接來構(gòu)造SQL的時候,你會發(fā)現(xiàn)還能用全局的綁定方程來完成這一工作,這又會大大增加整個代碼的整潔度與安全性。
Common Misconceptions有一個常見的錯誤思維就是覺得存儲過程能夠避免SQL注入攻擊,但是這個只有在你是通過參數(shù)綁定的方式傳入?yún)?shù)的情況下。如果存儲過程本身也是用的字符串連接的方式,那么同樣存在SQL注入攻擊的風(fēng)險。類似的,像ActiveRecord、Hibernate或者.Net Entity這樣的框架,也是只有在用參數(shù)綁定來構(gòu)造SQL的情況下才會進(jìn)行SQL注入清洗。
最后,還有一個常見的錯覺就是NoSQL數(shù)據(jù)庫不會被SQL注入攻擊影響。這肯定是不對的,所有的查詢語言,無論是不是SQL都需要在可執(zhí)行代碼與輸入的內(nèi)容之間劃定明晰的邊界來防止參數(shù)混淆可執(zhí)行的命令。攻擊者會不停尋找能夠在運行時打破這種邊界隔離的方法從而進(jìn)行潛在的攻擊。即使是Mongodb,采用了二進(jìn)制的協(xié)議與多種語言特定的API都會存在被注入的風(fēng)險,譬如$where這個操作符。
Parameter Binding FunctionsFramework | Encoded | Dangerous |
---|---|---|
Raw JDBC | Connection.prepareStatement() used with setXXX() methods and bound parameters for all input. | Any query or update method called with string concatenation rather than binding. |
PHP / MySQLi | prepare() used with bind_param for all input. | Any query or update method called with string concatenation rather than binding. |
MongoDB | Basic CRUD operations such as find(), insert(), with BSON document field names controlled by application. | Operations, including find, when field names are allowed to be determined by untrusted data or use of Mongo operations such as "$where" that allow arbitrary JavaScript conditions. |
Cassandra | Session.prepare used with BoundStatement and bound parameters for all input. | Any query or update method called with string concatenation rather than binding. |
Hibernate / JPA | Use SQL or JPQL/OQL with bound parameters via setParameter | Any query or update method called with string concatenation rather than binding. |
ActiveRecord | Condition functions (find_by, where) if used with hashes or bound parameters, eg: where (foo: bar)where ("foo = ?", bar) | Condition functions used with string concatenation or interpolation: where("foo = "#{bar}"")where("foo = "" + bar + """) |
避免直接從用戶的輸入中構(gòu)建出SQL或者等價的NoSQL查詢語句
在查詢語句與存儲過程中都使用參數(shù)綁定
盡可能使用框架提供好的原生的綁定方法而不是用你自己的編碼方法
不要覺得存儲過程或者ORM框架可以幫到你,你還是需要手動調(diào)用存儲過程
NoSQL 也存在著注入的危險
Protect Data in Transit當(dāng)我們著眼于系統(tǒng)的輸入輸出的時候,還有另一個重要的店需要考慮進(jìn)去,就是傳輸過程中數(shù)據(jù)的保密性與完整性。在使用原始的HTTP連接的時候,因為服務(wù)器與用戶之間是直接進(jìn)行的明文傳輸,導(dǎo)致了用戶面臨著很多的風(fēng)險與威脅。攻擊者可以用中間人攻擊來輕易的截獲或者篡改傳輸?shù)臄?shù)據(jù)。攻擊者想要做些什么并沒有任何的限制,包括竊取用戶的Session信息、注入有害的代碼等,乃至于修改用戶傳送至服務(wù)器的數(shù)據(jù)。
我們并不能替用戶選擇所使用的網(wǎng)絡(luò),他們很有可能使用一個開放的,任何人都可以竊聽的網(wǎng)絡(luò),譬如一個咖啡館或者機場里面的開放WiFi網(wǎng)絡(luò)。普通的用戶很有可能被欺騙地隨便連上一個叫免費熱點的網(wǎng)絡(luò),或者使用一個可以隨便被插入廣告的網(wǎng)路當(dāng)中。如果攻擊者會竊聽或者篡改網(wǎng)路中的數(shù)據(jù),那么用戶與服務(wù)器交換的數(shù)據(jù)就好不可信了,幸好我們還可以使用HTTPS來保證傳輸?shù)陌踩浴?/p> HTTPS and Transport Layer Security
HTTPS最早主要用于類似于經(jīng)融這樣的安全要求較高的敏感網(wǎng)絡(luò),不過現(xiàn)在日漸被各種各樣的網(wǎng)站鎖使用,譬如我們常用的社交網(wǎng)絡(luò)或者搜索引擎。HTTPS協(xié)議使用的是TLS協(xié)議,一個優(yōu)于SSL協(xié)議的標(biāo)準(zhǔn)來保障通信安全。只要配置與使用得當(dāng),就能有效抵御竊聽與篡改,從而有效保護(hù)我們將要去訪問的網(wǎng)站。用更加技術(shù)化的方式說,HTTPS能夠有效保障數(shù)據(jù)機密性與完整性,并且能夠完成用戶端與客戶端的雙重驗證。
隨著面臨的風(fēng)險日漸增多,我們應(yīng)該將所有的網(wǎng)絡(luò)數(shù)據(jù)當(dāng)做敏感數(shù)據(jù)并且進(jìn)行加密傳輸。已經(jīng)有很多的瀏覽器廠商宣稱要廢棄所有的非HTTPS的請求,乃至于當(dāng)用戶訪問非HTTPS的網(wǎng)站的時候給出明確的提示。很多基于HTTP/2的實現(xiàn)都只支持基于TLS的通信,所以我們現(xiàn)在更應(yīng)當(dāng)在全部地方使用HTTPS。
目前如果要大范圍推廣使用HTTPS還是有一些障礙的,在一個很長的時間范圍內(nèi)使用HTTPS會被認(rèn)為造成很多的計算資源的浪費,不過隨著現(xiàn)代硬件與瀏覽器的發(fā)展,這點計算資源已經(jīng)不足為道。早期的SSL協(xié)議與TLS協(xié)議只支持一個IP地址分配一個整數(shù),不過現(xiàn)在這種限制所謂的SNI的協(xié)議擴展來解決。另外,從一個證書認(rèn)證機構(gòu)獲取證書也會打消一些用戶使用HTTPS的念頭,不過下面我們介紹的像Let"s Encrypt這樣的免費的服務(wù)就可以打破這種障礙。
Get a Server Certificate對于網(wǎng)站的安全認(rèn)證依賴于TLS的底層的支持,如果客戶端只是根據(jù)網(wǎng)站說它自己是誰就是誰,那么攻擊者可以輕易的使用中間人攻擊來模擬站點,從而繞過所有協(xié)議提供的安全機制。在使用了TLS協(xié)議之后,一個網(wǎng)站可以用它的公鑰證書來證明它自己是誰。在某些系統(tǒng)中客戶端也需要用公鑰證書證明自己是誰,不過大部分情況下受限于為用戶管理證書的復(fù)雜性,這個并沒有廣泛使用。除非一個網(wǎng)站證書的真實性已經(jīng)經(jīng)過了驗證,不然客戶端在收到一個證書的時候也要通過一定的手段來驗證證書的真實性。而在Web瀏覽器或者其他的應(yīng)用中,往往是通過一個第三方的稱作CA的機構(gòu)來管理證書并且提供驗證功能,包括驗證這個證書與證書所屬網(wǎng)站的真實性。
如果我們通過其他渠道已經(jīng)能夠提前得知某個證書是否可信,那也就沒必要再經(jīng)過第三方機構(gòu)進(jìn)行仲裁。譬如一個移動APP或者其他應(yīng)用在分發(fā)的時候就會內(nèi)置一些證書從而在使用時來驗證站點是否真實可信。大部分關(guān)于HTTPS是否可信的指示會在瀏覽器訪問某個HTTPS的站點的時候顯示出來,如果沒有的話瀏覽器會顯示一個告警信息來警告用戶不要訪問不可信的站點。
在測試的時候我們可以自己創(chuàng)建配置一個證書用于HTTPS認(rèn)證,不過如果你要提供服務(wù)給普通用戶使用,那么還是需要從可信的第三方CA機構(gòu)來獲取可信的證書。對于很多開發(fā)者而言,一個免費的CA證書是個不錯的選擇。當(dāng)你搜索CA的時候,你可能會遇到幾個不同等級的證書。最常見的就是Domain Validation(DV),用于認(rèn)證一個域名的所有者。再往上就是所謂的Organization Validation(OV)與Extended Validation(EV),包括了驗證這些證書的請求機構(gòu)的信息。雖然高級別的證書需要額外的消耗,不過還是很值得的。
Configure Your Server當(dāng)你申請到了證書之后,你就可以開始配置你的服務(wù)器支持HTTPS了。雖然HTTPS啊,包括TLS/SSL的原理好像要個密碼學(xué)的PHD學(xué)位才能理解,但是要把他們配置著用起來還是很容易的呦。不同的加密算法與站點使用的協(xié)議的版本差異會大大影響到它能夠提供的通信的安全級別。所以咋才能一方面保證我們站點的安全性另一方面又保證那些使用老版本的瀏覽器的用戶也能正常使用網(wǎng)站服務(wù)呢?這里要推薦下Mozilla提供的Security/Server Side TLS工具,可以協(xié)助來自動創(chuàng)建適用的Web服務(wù)器的配置。
Use HTTPS for Everything現(xiàn)在我們經(jīng)常碰到一些網(wǎng)站僅僅只用HTTPS來保護(hù)部分資源,有些情況下只會保護(hù)一些對于敏感資源的提交操作。另一些情況下,部分網(wǎng)站只會將HTTPS用于自認(rèn)為敏感的資源上,譬如一個用戶登錄之后才能見到的東西往往是HTTPS加密的。而現(xiàn)在的麻煩事還有很多站點并未使用HTTPS來保護(hù)自己,導(dǎo)致整個網(wǎng)站還處于被中間人攻擊的危險之下。筆者很是建議這類網(wǎng)站應(yīng)該直接關(guān)閉掉HTTP端口從而強制性讓用戶轉(zhuǎn)到HTTPS,雖然這并不是一個理想的解決方案,不過估計是最好的解決方法。
如果是在Web瀏覽器中呈現(xiàn)的資源,那可以添加一個HTTP請求轉(zhuǎn)發(fā)的配置,來將所有的HTTP請求轉(zhuǎn)發(fā)到HTTPS端口上,譬如:
# Redirect requests to /content to use HTTPS (mod_rewrite is required) RewriteEngine On RewriteCond %{HTTPS} != on [NC] RewriteCond %{REQUEST_URI} ^/content(/.*)? RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R,L]Use HSTS
讓用戶從HTTP遷移到HTTPS可以來避免使用原始的HTTP請求帶來的風(fēng)險。為了幫助站點把用戶從HTTP遷移到HTTPS,現(xiàn)代的瀏覽器支持一個非常強力的安全特征叫做HSTS(HTTP Strict Transport Security),來告訴瀏覽器我這個站點只會接收來自于HTTPS的請求。這個特性最早來自于2009年的Moxie Marlinspike"s提出的一個用于演示基于HTTP的潛在危險的SSL剝離攻擊??梢杂萌缦碌脑O(shè)置來啟用這個特性:
Strict-Transport-Security: max-age=15768000
上述的設(shè)置會告訴瀏覽器只和使用HTTPS的站點進(jìn)行交互,HSTS是一個非常重要的強制使用HTTPS的特性。一旦開啟之后,瀏覽器會自動把不安全的HTTP請求切換到HTTPS,盡管用戶沒有顯式的輸入"https://"。而在瀏覽器端開啟HSTS特性只需要添加如下的一行代碼:
... # HSTS (mod_headers is required) (15768000 seconds = 6 months) Header always set Strict-Transport-Security "max-age=15768000"
不過現(xiàn)在并不是所有的瀏覽器都支持HSTS特性,你可以通過訪問 Can I use. 來看看你面向的用戶常用的瀏覽器能不能使用。
Protect Cookies瀏覽器目前有內(nèi)建的安全機制來避免包含敏感信息的Cookie暴露出來。在Cookie中設(shè)置secure標(biāo)識位能夠強制讓瀏覽器只會用HTTPS來傳遞Cookie,如果你已經(jīng)使用了HSTS也要記得這樣設(shè)置來保護(hù)Cookie。
Other Risks即使你全站都用了HTTPS,也還是有幾個地方可能導(dǎo)致敏感信息的泄露的。譬如如果你直接把敏感數(shù)據(jù)放在URL里面,然后這個敏感的URL又被緩存在了瀏覽器的歷史記錄里。除此之后,如果包含了敏感信息的站點被鏈接到了其他的網(wǎng)站中,那么在用戶點擊鏈接之后整個敏感數(shù)據(jù)就會被放在Referer Header中然后傳送過去,然后就呵呵了。另外,有時候因為大家都懂的原因我們會使用一些代理然后允許他們監(jiān)控HTTPS的流量,也是有危險地,這個時候就要在Header中來關(guān)閉緩存從而降低風(fēng)險。筆者建議你可以參考OWASP Transport Protection Layer Cheat Sheet 來收獲一些有用的建議。
Verify Your Configuration最后一步,你要仔細(xì)驗證你的配置是否有效。有很多的在線工具可以幫你做這件事,譬如SSL Lab的SSL Server Test能夠幫你深度分析你的HTTPS的配置,再看看是不是有啥地方配錯了。這個工具會在發(fā)現(xiàn)了新的攻擊手段與協(xié)議更新之后實時更新,所以多用用它還是個很不錯的事情嗷。
In Summary啥地方都要用HTTPS
采用HSTS來強制使用HTTPS
別忘了從可信的證書機構(gòu)中請求可信證書
不要亂放你的私鑰
用合理的配置工具來生成可靠地HTTPS配置
在Cookie中設(shè)置"secure"標(biāo)識
不要把敏感的數(shù)據(jù)放在URL中
隔一段時間就要好好看看你的HTTPS的配置,表過時了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/11163.html
摘要:本書的這一部分將為隨后的章節(jié)打下基礎(chǔ),會涵蓋模板,模塊化,和依賴注入。本書的小例子中我們會使用未經(jīng)壓縮的,開發(fā)友好的版本,在的上。作用域也可以針對特定的視圖來擴展數(shù)據(jù)和特定的功能。 上一篇:【譯】《精通使用AngularJS開發(fā)Web App》(一) 下一篇:【譯】《精通使用AngularJS開發(fā)Web App》(三) 原版書名:Mastering Web Application D...
摘要:前端日報精選專題之類型判斷下百度生態(tài)構(gòu)建發(fā)布基于的解決方案將全面支持從綁定,看語言發(fā)展和框架設(shè)計掘金譯機器學(xué)習(xí)與一付費問答上線,向你心目中的大牛提問吧產(chǎn)品技術(shù)日志中文第期團(tuán)隊技術(shù)信息流建設(shè)翻譯基于路由的異步組件加載個必備的裝逼 2017-07-06 前端日報 精選 JavaScript專題之類型判斷(下) · Issue #30 · mqyqingfeng/Blog 百度Web生態(tài)構(gòu)...
摘要:微服務(wù)簡介微服務(wù)架構(gòu)是一種架構(gòu)概念,旨在通過將功能分解到各個離散的服務(wù)中以實現(xiàn)對解決方案的解耦。每個微服務(wù)僅關(guān)注于完成一件任務(wù)并很好地完成該任務(wù)。服務(wù)異常自動隔離。微服務(wù)架構(gòu)挑戰(zhàn)服務(wù)規(guī)模大,部署運維管理難度大。 微服務(wù)簡介 微服務(wù)架構(gòu)(Microservice Architecture)是一種架構(gòu)概念,旨在通過將功能分解到各個離散的服務(wù)中以實現(xiàn)對解決方案的解耦。 微服務(wù)是一種架構(gòu)風(fēng)格,...
摘要:面試網(wǎng)絡(luò)了解及網(wǎng)絡(luò)基礎(chǔ)對端傳輸詳解與攻防實戰(zhàn)本文從屬于筆者的信息安全實戰(zhàn)中滲透測試實戰(zhàn)系列文章。建議先閱讀下的網(wǎng)絡(luò)安全基礎(chǔ)。然而,該攻擊方式并不為大家所熟知,很多網(wǎng)站都有的安全漏洞。 面試 -- 網(wǎng)絡(luò) HTTP 現(xiàn)在面試門檻越來越高,很多開發(fā)者對于網(wǎng)絡(luò)知識這塊了解的不是很多,遇到這些面試題會手足無措。本篇文章知識主要集中在 HTTP 這塊。文中知識來自 《圖解 HTTP》與維基百科,若...
閱讀 3406·2021-09-22 15:17
閱讀 2761·2021-09-02 15:15
閱讀 1794·2019-08-30 15:54
閱讀 2014·2019-08-30 14:02
閱讀 2544·2019-08-29 16:58
閱讀 3003·2019-08-29 16:08
閱讀 1345·2019-08-26 12:24
閱讀 1670·2019-08-26 10:41