HXSecurity / DongTai-agent-java

Java Agent is a Java application probe of DongTai IAST, which collects method invocation data during runtime of Java application by dynamic hooks.
https://dongtai.io
Apache License 2.0
681 stars 191 forks source link

[Bug]: Java Agent 在 Spring WebFlux 和 Tomcat 组合下会导致 HTTP 请求不输出结果 #220

Closed rjsoftcode closed 2 years ago

rjsoftcode commented 2 years ago

Preflight Checklist

Version

1.3.0

Installation Type

Other (specify below)

Service Name

DongTai-agent-java

Describe the details of the bug and the steps to reproduce it

cn.huoxian.iast.api.WrapperOutputStreamCopier 这个类替换了请求响应原本的 ServletOutputStream, 但只重写了 void write(int b) 方法,而没有重写 void write(byte b[], int off, int len) 方法。在 Spring WebFlux 和 Tomcat 组合中, Spring 是使用 void write(byte b[], int off, int len) 输出响应结果的,而 Tomcat 并不会把 void write(byte b[], int off, int len) 的实现引向 void write(int b), 这是两个独立实现的方法。所以 WrapperOutputStreamCopier 在事实上就把响应结果输出的数据丢失了。请求端的表现是发出请求后一直等待结果数据却永远不会等到。

Spring WebMVC 不确定是不是也是这样的表现,没有做测试。但如果也是使用 void write(byte b[], int off, int len) 输出结果的话,表现应该是一样的。

在 OutputStream 的实现中,void write(byte b[], int off, int len) 方法写数据时,实际又调用了 void write(int b) 方法。但 Tomcat 在实现 ServletOutputStream 时,并没有保持这一作法,估计主要是性能上的考虑。两个方法是分别独立实现,并没有调用关系。所以在这种情况下 WrapperOutputStreamCopier 只重写 void write(int b) 方法肯定是不够的。

推荐的作法是,WrapperOutputStreamCopier 应该参考类似 HttpServletResponseWrapper 的作法,在上面再提供一个 ServletOutputStreamWrapper,用于重写所有的方法把实际动作再引向原始的 ServletOutputStream 对象,这样就不会有中断的可能性了。WrapperOutputStreamCopier 再继承 ServletOutputStreamWrapper,这样就可以只重写合适的方法来处理缓存数据就行了。 ServletOutputStreamWrapper 关注包装对象到原始对象之间的联接,而 WrapperOutputStreamCopier 主要关注自己要做的数据缓存。

Additional Information

No response

Logs

No response

rjsoftcode commented 2 years ago

ServletOutputStreamWrapper 这种方式会显得比较重,WrapperOutputStreamCopier 简单处理的话,只重写 void write(byte b[], int off, int len) 这个方法,也可以解决本主题所说的结果丢失的问题。但因为其它方法都是和原始 ServletOutputStream 中断联系的,所以在其它场景下还是可能会出另外的问题。建议还是保持包装对象和原始对象之间完整的能力衔接更合适。