diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/Saml2Utils.java index 91dae712f4..860dee0f97 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.internal; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java index 50d0d13967..162c56f569 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.authentication; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java index 75129fbeaf..c65295d033 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.authentication.logou import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2Utils.java index 0d1c1d33b6..0f873de8ed 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.metadata; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java index 6f8818605f..2c72a4df7b 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.registration; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2Utils.java index e923f1eab8..86bcb03cf9 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.web; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java index 70b9f47528..4c5f52faf9 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.web.authentication; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java index 34de005d98..9f285a8f81 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java @@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.web.authentication.l import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; @@ -64,7 +65,7 @@ final class Saml2Utils { static String samlInflate(byte[] b) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); + InflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true)); iout.write(b); iout.finish(); return new String(out.toByteArray(), StandardCharsets.UTF_8); @@ -193,4 +194,27 @@ final class Saml2Utils { } + static class CappedOutputStream extends OutputStream { + + private static final long MAX_SIZE = 1024 * 1024; + + private final OutputStream delegate; + + private int size; + + CappedOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + if (this.size >= MAX_SIZE) { + throw new IOException("SAML payload exceeded maximum size of " + MAX_SIZE); + } + this.delegate.write(b); + this.size++; + } + + } + } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/internal/Saml2UtilsTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/internal/Saml2UtilsTests.java new file mode 100644 index 0000000000..dc7b7e0603 --- /dev/null +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/internal/Saml2UtilsTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2026-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.saml2.internal; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +import org.springframework.security.saml2.Saml2Exception; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +class Saml2UtilsTests { + + @Test + void testSaml2InflateWhenLargePayloadThenErrors() { + byte[] b = new byte[1024 * 1024 + 1]; + for (int i = 0; i < b.length; i++) { + b[i] = 56; + } + byte[] deflated = Saml2Utils.samlDeflate(new String(b, StandardCharsets.UTF_8)); + assertThatExceptionOfType(Saml2Exception.class).isThrownBy(() -> Saml2Utils.samlInflate(deflated)) + .withStackTraceContaining("SAML payload exceeded maximum size"); + } + +}