Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate multipart boundary using SecureRandom [SPR-16635] #21176

Closed
spring-projects-issues opened this issue Mar 23, 2018 · 2 comments
Closed
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Mar 23, 2018

Rossen Stoyanchev opened SPR-16635 and commented

From RFC 2046:

The boundary delimiter MUST NOT appear inside any of the encapsulated parts,
on a line by itself or as the prefix of any line.  This implies that it is
crucial that the composing agent be able to choose and specify a
unique boundary parameter value that does not contain the boundary
parameter value of an enclosing multipart as a prefix.

Currently Random is used to generate the boundary which guarantees the above will not occur by accident. In certain scenarios, based on an advanced and esoteric type of attack, it is theoretically possible for the boundary to be guessed and intermixed with content received as input from an outside source. We should switch to using SecureRandom to completely eliminate that possibility.

Note however that the use of SecureRandom also introduces the possibility for lack of entropy depending on how the JVM is configured. This is a more general issue, however, not specific to the Spring Framework, but we need to provide guidance in the docs.


Affects: 4.3.14

Issue Links:

Referenced from: commits f6ea740, e02ff3a, d007c25, ab2410c

Backported to: 4.3.15

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

After some further investigation, it seems the default SecureRandom constructor should provide optimal performance. See for example:
https://www.synopsys.com/blogs/software-security/proper-use-of-javas-securerandom/

@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.0.5 milestone Jan 11, 2019
@abialas
Copy link

abialas commented Sep 8, 2021

I have exactly the same problem. I am using spring-web 5.3.9 and when I want to test bean which uploads multipart body I get this stacktrace due to SecureRandom:

reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes
	at java.base/java.io.FileInputStream.readBytes(FileInputStream.java)
	at java.base/java.io.FileInputStream.read(FileInputStream.java:271)
	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:132)
	at java.base/sun.security.provider.NativePRNG$RandomIO.readFully(NativePRNG.java:424)
	at java.base/sun.security.provider.NativePRNG$RandomIO.ensureBufferValid(NativePRNG.java:526)
	at java.base/sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.java:545)
	at java.base/sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.java:220)
	at java.base/java.security.SecureRandom.nextBytes(SecureRandom.java:751)
	at java.base/java.security.SecureRandom.next(SecureRandom.java:808)
	at java.base/java.util.Random.nextInt(Random.java:390)
	at org.springframework.util.MimeTypeUtils.generateMultipartBoundary(MimeTypeUtils.java:390)
	at org.springframework.http.codec.multipart.MultipartWriterSupport.generateMultipartBoundary(MultipartWriterSupport.java:104)
	at org.springframework.http.codec.multipart.MultipartHttpMessageWriter.writeMultipart(MultipartHttpMessageWriter.java:185)
	at org.springframework.http.codec.multipart.MultipartHttpMessageWriter.lambda$write$0(MultipartHttpMessageWriter.java:158)
	at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:152)
	at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53)
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
	at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:424)
	at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:654)
	at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onStateChange(DefaultPooledConnectionProvider.java:201)
	at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onStateChange(DefaultPooledConnectionProvider.java:457)
	at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:230)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:216)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:209)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelActive(CombinedChannelDuplexHandler.java:412)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelActive(ChannelInboundHandlerAdapter.java:69)
	at io.netty.channel.CombinedChannelDuplexHandler.channelActive(CombinedChannelDuplexHandler.java:211)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:230)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:216)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:209)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1398)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:230)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:216)
	at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:895)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:305)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:335)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:707)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:831)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants