spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.54k stars 40.54k forks source link

x-www-form-urlencoded escaping leads to truncated request #40102

Closed lukasz7251 closed 5 months ago

lukasz7251 commented 5 months ago

In Spring Boot Web, starting from version 3.2.0, there is an issue where the request body is truncated if it contains characters that need to be escaped, such as ":" or "=".

Steps to reproduce:
1. Create a basic Spring Boot Web app with version 3.2.0 or greater.
2. Create a sample controller with the following code:
   ```kotlin
   @PostMapping("/test")
   fun testRequest(@RequestBody requestAsString: String) {
       requestAsString.split("&").forEach { println(it) }
   }
  1. Invoke the controller with parameters, including one with a special character, using the following curl command:
    curl -X POST \
    'http://localhost:8080/test' \
    -d 'A=0:1' \
    -d 'B=123'

Actual output (>=3.2.0):

A=0%3A1
B=1

Expected output (=3.1.10):

A=0%3A1
B=123
bclozel commented 5 months ago

I don't think this is strictly a Spring issue. As of Spring Framework 6.1, we only read the amount of bytes that are declared in the Content-Length header of the request.

Using httpie and Spring Boot 3.2.x, I'm getting the correct result:

http -vvv --form POST localhost:8080/test A='0:1' B='123'
POST /test HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 13
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:8080
User-Agent: HTTPie/3.2.2

A=0%3A1&B=123

HTTP/1.1 200
Connection: keep-alive
Content-Length: 0
Date: Mon, 25 Mar 2024 18:17:16 GMT
Keep-Alive: timeout=60

Note, the "Content-Length" header shows "13".

With the curl command listed above, the content is URL encoded but somehow the Content-Length is only 10.

curl -vv -X POST 'http://localhost:8080/test' -d 'A=0:1' -d 'B=123'
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying [::1]:8080...
* Connected to localhost (::1) port 8080
> POST /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.4.0
> Accept: */*
> Content-Length: 11
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200
< Content-Length: 0
< Date: Mon, 25 Mar 2024 18:22:18 GMT
<
* Connection #0 to host localhost left intact

I'm not a curl expert, so I don't know if this is a bug or the expected behavior. I do know though that using the --data-urlencode option fixes things:

curl -X POST 'http://localhost:8080/test' --data-urlencode 'A=0:1' -d 'B=123'

I'm closing this issue as a result, as there's nothing we can do from our side since the request is invalid in the first place when the problem happens.

Thanks for your report!