CSRF 攻撃を防御したい場合は、まず CSRF 攻撃が何であるかを理解する必要があります。次の図を通して CSRF 攻撃のプロセスを整理します。
実際、このプロセスは非常に簡単です:
1. ユーザーが China Merchants Online Banking の Web サイトを開いてログインすると仮定します。2. ログインに成功すると、オンライン バンキングは Cookie をフロントエンドに返し、ブラウザーは Cookie を保存します。
3. ユーザーはオンライン バンキングからログアウトせずにブラウザで新しいタブを開き、危険な Web サイトにアクセスしました。
4. この危険な Web サイトにはハイパーリンクがあり、そのハイパーリンクのアドレスは中国商人オンライン バンキングを指しています。
4. ユーザーがこのリンクをクリックすると、このハイパーリンクにはブラウザに保存された Cookie が自動的に送信されるため、ユーザーは知らず知らずのうちにオンライン バンキングにアクセスし、損失を被る可能性があります。
CSRF のプロセスは大まかに次のとおりです。次に、CSRF がどのようなものであるかを簡単な例を使用して説明します。
CSRF Practice
<dependencies> <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> </dependencies>
2. 作成が成功した後、便宜上、application.properties ファイルで Spring Security のユーザー名/パスワードを直接構成します:
server.port= 8866 spring.security.user.name=javaboy spring.security.user.password=123
3。 2 つのテスト インターフェイスを提供します
package com.mry.csrf.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class CsrfDemoController { @PostMapping("/transfer") public void transferMoney(String name, Integer money) { System.out.println("name = " + name); System.out.println("money = " + money); } @GetMapping("/hello") public String hello() { return "hello"; } }
/transfer が転送インターフェイスであると仮定します (これは主に CSRF 攻撃を全員に示すための仮定であり、実際の転送インターフェイスはこれより複雑です)。
4. Spring Security はデフォルトで CSRF 攻撃を自動的に防御できるため、Spring Security を構成する必要もあるため、これをオフにする必要があります。
package com.mry.csrf.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .and() .csrf() .disable(); } }
構成が完了したら、csrf-simulate-web プロジェクトを開始します。
5. 別の csrf-loophole-web プロジェクトを作成しましょう。このプロジェクトは危険な Web サイトに相当します。便宜上、ここで作成するときに Web 依存関係を導入するだけで済みます。
プロジェクトが正常に作成されたら、まずプロジェクト ポートを変更します:server.port= 8855
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="http://localhost:8866/transfer" method="post"> <input type="hidden" value="javaboy" name="name"> <input type="hidden" value="10000" name="money"> <input type="submit" value="点击查看美女图片"> </form> </body> </html>
ここにハイパーリンクがあります。ハイパーリンクのテキストは、クリックすると美しい女性の写真が表示されます。ハイパーリンクをクリックすると、自動的に http://localhost:8866/transfer インターフェイスが要求されます。また、隠しドメインには 2 つのパラメータも含まれます。
構成が完了したら、csrf-loophole-web プロジェクトを開始できます。
次に、ユーザーはまず csrf-simulate-web プロジェクトのインターフェースにアクセスします。アクセスする際にはログインが必要であり、ユーザーはログイン操作を実行します。アクセスが完了した後、ユーザーはログイン操作を行いません。ログアウト操作を実行すると、ユーザーは csrf-loophole-web のページにアクセスし、ハイパーリンクを目にしました。この美しさがどのようなものか興味がありました。クリックするとすぐに、お金が送金されました。
CSRF 防御
CSRF 防御の中心となるアイデアは、フロントエンド リクエストに乱数を追加することです。
CSRF 攻撃では、ハッカー Web サイトは実際にはユーザーの Cookie が何であるかを知りません。このプロセスで自動的に Cookie が送信されるため、ユーザーは自分でオンライン バンキング Web サイトにリクエストを送信できます。情報。
したがって、私たちの防御アイデアは次のとおりです: ユーザーがオンライン バンキングにアクセスするとき、Cookie 内の情報に加えて、乱数も運ぶ必要があります。ユーザーがこの乱数を持たない場合、オンライン バンキング Web サイトはリクエストを拒否します。ハッカー Web サイトがユーザーにハイパーリンクをクリックさせると、その情報は自動的に Cookie に組み込まれますが、乱数は自動的に組み込まれないため、CSRF 攻撃は回避されます。
Spring Security はこれに対して優れたサポートを提供しています。見てみましょう。
バックエンドとバックエンドの非分離ソリューション
1.pom 情報
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2. プロジェクトが正常に作成された後も、application.properties
spring.security.user.name=mry spring.security.user.password=123456
3 でユーザー名とパスワードを設定します。次に、次の情報を提供します。テスト インターフェイス
package com.mry.csrf.controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class SecurityCsrfController { @PostMapping("/hello") @ResponseBody public String hello() { return "hello"; } }
デフォルトでは、GET、HEAD、TRACE、および OPTIONS は CSRF 攻撃について検証する必要がないため、このテスト インターフェイスは POST リクエストであることに注意してください。
4. 次に、resources/templates ディレクトリに新しい thymeleaf テンプレートを作成します
<!DOCTYPE html> <!--导入thymeleaf的名称空间--> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/hello" method="post"> <input type="hidden" th:value="${_csrf.token}" th:name="${_csrf.parameterName}"> <input type="submit" value="hello"> </form> </body> </html>
POST リクエストを送信するときに、追加の隠しドメインと隠しドメインのキーが送信されることに注意してくださいは ${_csrf.parameterName}、値は ${_csrf.token} です。
サーバーはこれら 2 つの値を自動的に取得します。フロントエンドでレンダリングするだけで済みます。
5. 次にフロントエンドの hello.html ページにコントローラーを追加します
@GetMapping("/hello") public String hello2() { return "hello"; }
6. 追加が完了したらプロジェクトを起動し、hello ページにアクセスします。ログインが成功すると、ログイン要求に追加のパラメータがあることがわかります。
这里我们用了 Spring Security 的默认登录页面,如果大家使用自定义登录页面,可以参考上面 hello.html 的写法,通过一个隐藏域传递 _csrf 参数。
访问到 hello 页面之后,再去点击【hello】按钮,就可以访问到 hello 接口了。
这是 Spring Security 中默认的方案,通过 Model 将相关的数据带到前端来。
如果你的项目是前后端不分项目,这种方案就可以了,如果你的项目是前后端分离项目,这种方案很明显不够用。
如果是前后端分离项目,Spring Security 也提供了解决方案。
这次不是将 _csrf 放在 Model 中返回前端了,而是放在 Cookie 中返回前端,配置方式如下:
package com.mry.csrf.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
有小伙伴可能会说放在 Cookie 中不是又被黑客网站盗用了吗?其实不会的,大家注意如下两个问题:
(1)黑客网站根本不知道你的 Cookie 里边存的啥,他也不需要知道,因为 CSRF 攻击是浏览器自动携带上 Cookie 中的数据的。
(2)我们将服务端生成的随机数放在 Cookie 中,前端需要从 Cookie 中自己提取出来 _csrf 参数,然后拼接成参数传递给后端,单纯的将 Cookie 中的数据传到服务端是没用的。
理解透了上面两点,你就会发现 _csrf 放在 Cookie 中是没有问题的,但是大家注意,配置的时候我们通过 withHttpOnlyFalse 方法获取了 CookieCsrfTokenRepository 的实例,该方法会设置 Cookie 中的 HttpOnly 属性为 false,也就是允许前端通过 js 操作 Cookie(否则你就没有办法获取到 _csrf)。
配置完成后,重启项目,此时我们就发现返回的 Cookie 中多了一项:
接下来,我们通过自定义登录页面,来看看前端要如何操作。
首先我们在 resources/static 目录下新建一个 html 页面叫做 login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script> </head> <body> <div> <input type="text" id="username"> <input type="password" id="password"> <input type="button" value="登录" id="loginBtn"> </div> <script> $("#loginBtn").click(function () { let _csrf = $.cookie('XSRF-TOKEN'); $.post('/login.html',{username:$("#username").val(),password:$("#password").val(),_csrf:_csrf},function (data) { alert(data); }) }) </script> </body> </html>
这段 html 给大家解释一下:
(1)首先引入 jquery 和 jquery.cookie ,方便我们一会操作 Cookie。
(2)定义三个 input,前两个是用户名和密码,第三个是登录按钮。
(3)点击登录按钮之后,我们先从 Cookie 中提取出 XSRF-TOKEN,这也就是我们要上传的 csrf 参数。
(4)通过一个 POST 请求执行登录操作,注意携带上 _csrf 参数。
服务端我们也稍作修改,如下:
package com.mry.csrf.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/static/js/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") .successHandler((req,resp,authentication)->{ resp.getWriter().write("success"); }) .permitAll() .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
一方面这里给 js 文件放行。
另一方面配置一下登录页面,以及登录成功的回调,这里简单期间,登录成功的回调我就给一个字符串就可以了。在登录成功后回调的详细解释。
OK,所有事情做完之后,我们访问 login.html 页面,输入用户名密码进行登录,结果如下:
可以看到,我们的 _csrf 配置已经生效了。
以上がSpringBoot の CSRF 攻撃に対する防御のプロセスと原則は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。