spring.security 禁止 curl 发布请求
Posted
技术标签:
【中文标题】spring.security 禁止 curl 发布请求【英文标题】:spring.security forbid curl post request 【发布时间】:2021-09-04 08:59:24 【问题描述】:我使用 SSM 框架开发了一个演示网站,并使用 spring.security 进行身份验证。我可以使用 post 请求登录网站并使用 get 请求获取数据。但是,我无法使用发布请求添加数据。它总是被禁止的。如果 CSRF 被禁用,就可以了。我尝试了以下方法,它们都不起作用。
-
将“X-CSRF-TOKEN:CSRF 值”添加到标题中。
将“_csrf:CSRF 值”添加到标题中。
将 _csrf 添加到发布请求正文。
那么如何在启用 CSRF 的情况下使发布请求工作?另外,我不确定是否需要使用不同的帖子重新生成 CSRF 令牌。奇怪的是,登录帖子请求有效。
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:mybatis/*.xml
spring.security.user.name=root
spring.security.user.password=123456
debug=true
安全配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
用户控制器
@RestController
@RequestMapping("/rest/users")
public class UserController
@Autowired
private UserMapper userMapper;
@PostMapping
public void add(@RequestBody User user)
userMapper.add(user);
@GetMapping
public List<User> getAll()
return userMapper.findAll();
卷曲脚本
#!/usr/bin/env bash
host=192.168.44.109:8080
remote=http://$host
login=$remote/login
users=$remote/rest/users/
csrf=$( \
curl --url $login -L -c cookie.txt 2>&1 \
|grep _csrf \
|sed 's/^.*value="\(.*\)".*$/\1/' \
)
echo "before login, csrf=$csrf"
curl --url $login -L -b cookie.txt -c cookie.txt -i \
-d "username=root&password=123456&_csrf=$csrf"
curl --url $users -L -b cookie.txt -v \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: $csrf" \
-H "x-csrf-token: $csrf" \
-H "_csrf: $csrf" \
-d "\"name\": \"name2\""
rm -f cookie.txt
脚本输出
before login, csrf=6d7b2d7b-f9aa-4463-ad9b-468082df4d74
HTTP/1.1 302
Set-Cookie: JSESSIONID=D728694163DEEC78DDBC8869DC54C870; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://192.168.44.109:8080/
Content-Length: 0
Date: Sun, 20 Jun 2021 11:05:52 GMT
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/hal+json
Transfer-Encoding: chunked
Date: Sun, 20 Jun 2021 11:05:52 GMT
"_links" :
"profile" :
"href" : "http://192.168.44.109:8080/profile"
* Trying 192.168.44.109...
* TCP_NODELAY set
* Connected to 192.168.44.109 (192.168.44.109) port 8080 (#0)
> POST /rest/users/ HTTP/1.1
> Host: 192.168.44.109:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Cookie: JSESSIONID=D728694163DEEC78DDBC8869DC54C870
> Content-Type: application/json
> X-CSRF-Token: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> x-csrf-token: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> _csrf: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> Content-Length: 17
>
* upload completely sent off: 17 out of 17 bytes
< HTTP/1.1 403
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sun, 20 Jun 2021 11:05:52 GMT
<
* Connection #0 to host 192.168.44.109 left intact
"timestamp":"2021-06-20T11:05:52.540+00:00","status":403,"error":"Forbidden","message":"","path":"/rest/users/"* Closing connection 0
【问题讨论】:
【参考方案1】:是否需要用不同的帖子重新生成 CSRF 令牌
不,没有必要。
奇怪的是,登录帖子请求有效
登录后由spring.security重新生成token。
那么如何在启用 CSRF 的情况下使发布请求工作?
关键是如何获取登录后生成的新CSRF token。一种可能的解决方案是将 CSRF 令牌保存在 cookie 中。并且 SecurityConfig 应该改为:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
然后,可以通过awk '/XSRF-TOKEN/print $7' cookie
从cookie中获取csrf令牌,并使用X-XSRF-TOKEN请求头发送到服务器。完整的脚本改为:
#!/usr/bin/env bash
host=192.168.44.109:8080
remote=http://$host
login=$remote/login
users=$remote/rest/users/
csrf()
awk '/XSRF-TOKEN/print $7' cookie
curl $login -c cookie 1> /dev/null 2>/dev/null
echo "before login, csrf=$(csrf)"
curl $login -d "username=root&password=123456&_csrf=$(csrf)" -b cookie -c cookie -L -i
echo "after login, csrf=$(csrf)"
name=$(date '+%Y%m%d-%H:%M:%S')
curl $users -H "X-XSRF-TOKEN: $(csrf)" -H 'Content-Type: application/json' -d "\"name\": \"$name\"" -b cookie -L -v
rm -f cookie
【讨论】:
以上是关于spring.security 禁止 curl 发布请求的主要内容,如果未能解决你的问题,请参考以下文章
AJAX / Spring MVC - 没有 Spring Security 的 403 禁止错误
Grails Spring Security REST得到403禁止
为啥应用程序看不到 Spring Security 中的角色(禁止)
spring security - 针对不同 CURL 模式的多重身份验证