|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2023 the original author or authors. |
| 2 | + * Copyright 2002-2024 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
16 | 16 |
|
17 | 17 | package org.springframework.security.messaging.web.csrf;
|
18 | 18 |
|
| 19 | +import java.util.Base64; |
19 | 20 | import java.util.HashMap;
|
20 | 21 |
|
21 | 22 | import org.junit.jupiter.api.BeforeEach;
|
@@ -141,6 +142,73 @@ public void preSendWhenUnsubscribeThenIgnores() {
|
141 | 142 | this.interceptor.preSend(message(), this.channel);
|
142 | 143 | }
|
143 | 144 |
|
| 145 | + // gh-13310, gh-15184 |
| 146 | + @Test |
| 147 | + public void preSendWhenCsrfBytesIsShorterThanRandomBytesThenThrowsInvalidCsrfTokenException() { |
| 148 | + /* |
| 149 | + * Token format: 3 random pad bytes + 2 padded bytes. |
| 150 | + */ |
| 151 | + byte[] actualBytes = { 1, 1, 1, 96, 99 }; |
| 152 | + String actualToken = Base64.getEncoder().encodeToString(actualBytes); |
| 153 | + this.messageHeaders.setNativeHeader(this.token.getHeaderName(), actualToken); |
| 154 | + this.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token); |
| 155 | + // @formatter:off |
| 156 | + assertThatExceptionOfType(InvalidCsrfTokenException.class) |
| 157 | + .isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class))); |
| 158 | + // @formatter:on |
| 159 | + } |
| 160 | + |
| 161 | + // gh-13310, gh-15184 |
| 162 | + @Test |
| 163 | + public void preSendWhenCsrfBytesIsLongerThanRandomBytesThenThrowsInvalidCsrfTokenException() { |
| 164 | + /* |
| 165 | + * Token format: 3 random pad bytes + 4 padded bytes. |
| 166 | + */ |
| 167 | + byte[] actualBytes = { 1, 1, 1, 96, 99, 98, 97 }; |
| 168 | + String actualToken = Base64.getEncoder().encodeToString(actualBytes); |
| 169 | + this.messageHeaders.setNativeHeader(this.token.getHeaderName(), actualToken); |
| 170 | + this.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token); |
| 171 | + // @formatter:off |
| 172 | + assertThatExceptionOfType(InvalidCsrfTokenException.class) |
| 173 | + .isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class))); |
| 174 | + // @formatter:on |
| 175 | + } |
| 176 | + |
| 177 | + // gh-13310, gh-15184 |
| 178 | + @Test |
| 179 | + public void preSendWhenTokenBytesIsShorterThanActualBytesThenThrowsInvalidCsrfTokenException() { |
| 180 | + this.messageHeaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE); |
| 181 | + CsrfToken csrfToken = new DefaultCsrfToken("header", "param", "a"); |
| 182 | + this.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), csrfToken); |
| 183 | + // @formatter:off |
| 184 | + assertThatExceptionOfType(InvalidCsrfTokenException.class) |
| 185 | + .isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class))); |
| 186 | + // @formatter:on |
| 187 | + } |
| 188 | + |
| 189 | + // gh-13310, gh-15184 |
| 190 | + @Test |
| 191 | + public void preSendWhenTokenBytesIsLongerThanActualBytesThenThrowsInvalidCsrfTokenException() { |
| 192 | + this.messageHeaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE); |
| 193 | + CsrfToken csrfToken = new DefaultCsrfToken("header", "param", "abcde"); |
| 194 | + this.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), csrfToken); |
| 195 | + // @formatter:off |
| 196 | + assertThatExceptionOfType(InvalidCsrfTokenException.class) |
| 197 | + .isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class))); |
| 198 | + // @formatter:on |
| 199 | + } |
| 200 | + |
| 201 | + // gh-13310, gh-15184 |
| 202 | + @Test |
| 203 | + public void preSendWhenActualBytesIsEmptyThenThrowsInvalidCsrfTokenException() { |
| 204 | + this.messageHeaders.setNativeHeader(this.token.getHeaderName(), ""); |
| 205 | + this.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token); |
| 206 | + // @formatter:off |
| 207 | + assertThatExceptionOfType(InvalidCsrfTokenException.class) |
| 208 | + .isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class))); |
| 209 | + // @formatter:on |
| 210 | + } |
| 211 | + |
144 | 212 | private Message<String> message() {
|
145 | 213 | return MessageBuilder.withPayload("message").copyHeaders(this.messageHeaders.toMap()).build();
|
146 | 214 | }
|
|
0 commit comments