You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Christopher Schultz <ch...@christopherschultz.net> on 2026/02/19 15:49:33 UTC

CSRF prevention filter changes - possible bug?

All,

I recently made a change to the CSRF prevention filter in 
98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf 
tokens in URLs in case they were being repeatedly run through 
HttpServletResponse.encode().

It's possible there is a bug hiding in there but I'd like some feedback.

I'm using Velocity Tools to generate URLs and there is a tool class 
called LinkTool. It generates links with a builder-style interface with 
a base URL, parameters, and so on.

After upgrading to 9.0.115, I'm finding that a very small number of 
these links are being broken due to an interaction between LinkTool, my 
code, and the CsrfPreventionFilter's massaging of the URLs its producing.

The LinkTool is configured to produce HTML5-style URLs where the & 
characters are encoded as &amp;. I did triple-check to confirm that this 
was both expected and a reasonable reading of the HTML standard, which 
it appears to be. That is, use of &amp; is actually recommended in HTML 
pages. RFC 6068 and WHATWG agree on this point whi, frankly, is amazing 
enough that I'll go ahead and call it a Law.

Anyway, it looks like the original URL is being produced something like 
this:

/context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D

So far so good. Note that the URL has already gone through 
response.encodeURL() because it's got a csrf parameter in it.

Then another parameter is added by parsing the URL above (because 
#reasons, this is where my code comes in) and the URL then becomes:

/context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D

I haven't traced through the code, but I believe what's happening is this:

1. My code, through LinkTool, adds the "id" = "4" parameter
2. LinkTool re-generates the URL String including an &amp; parameter 
separator
3. LinkTool runs the result through response.encodeURL
4. CsrfPreventionFilter removes any existing CSRF query parameters; it 
finds one at the beginning of the URL and removes everything up to the 
next & character
5. CsrfPreventionFilter adds its csrf token to the end of the URL

The URL is now rendered and it's broken. :/

I think plausible arguments could be made that this is a bug in any of 
these 3 products: mine, VelocityTools, or Tomcat.

I'm primarily interested in getting this fixed in *my* product, but I 
wonder how many other products it might effect for similar reasons.

So, the topic here is only Tomcat-related: do we think that the 
CsrfPreventionFilter should consider &amp; to be a request parameter 
separator for the purposes of removing its own duplicates?

My reading of a few specs suggests that looking for "&amp;" should be 
safe since ; is a reserved character and ought to be escaped if the user 
actually intends for there to be an honest-to-goodness semicolon in 
their request parameter name or value. Thus, &amp; must mean that a 
literal & was intended.

So I think it's safe (and proper) for Tomcat to consume "&amp;" and not 
just "&" in this case.

Opinions?

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Konstantin Kolinko <kn...@gmail.com>.
вт, 24 мар. 2026 г. в 18:45, Christopher Schultz <ch...@christopherschultz.net>:
>
[...]
> >
> > If you decide to implement this feature, please make it configurable.
>
> +1
>
> Unfortunately, I *think* the performance characteristics will be
> different depending upon what kind of delimiter is being used. Searching
> for a single code point ('&') is faster than searching for a substring
> ("&amp;"). I would also imagine that searching for '&' is faster than
> searching for "&" (char vs string). I would like to keep this as fast as
> possible, so I suspect there will only be two options, here: & or &amp;
> and the search-and-replacement will use two different mechanisms.

+1 for the proposed two options.

BTW, If one wants, there is a way to combine the two searches:
search for the leading '&' and use String.regionMatches() to test for
the following "amp;".

Best regards,
Konstantin Kolinko

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Mark,

On 3/24/26 12:15 PM, Mark Thomas wrote:
> On 24/03/2026 15:44, Christopher Schultz wrote:
>> Konstantin,
>>
>> On 3/24/26 7:24 AM, Konstantin Kolinko wrote:
>>> вт, 24 мар. 2026 г. в 12:13, Mark Thomas <ma...@apache.org>:
> 
> <snip/>
> 
>>>> You have convinced me.
>>>>
>>>> I withdraw my objection to handling "&amp;" as equivalent to "&" in
>>>> encodeURL() and encodeRedirectURL().
>>>
>>> I see a problem.
>>> It is easy to implement removeQueryParameters() to remove either 
>>> "&amp;" or "&",
>>> but a problem is with the subsequent addNonce() call.
>>
>> Yup. I was thinking about that as I wrote my previous post.
>>
>>> If the original URL had more than one parameter
>>> then you can detect whether it used "&" or "%amp;", and use the same.
>>>
>>> But if it had a single parameter then there is no way to decide what
>>> delimiter should be used.
>>>
>>> If you decide to implement this feature, please make it configurable.
>>
>> +1
>>
>> Unfortunately, I *think* the performance characteristics will be 
>> different depending upon what kind of delimiter is being used. 
>> Searching for a single code point ('&') is faster than searching for a 
>> substring ("&amp;"). I would also imagine that searching for '&' is 
>> faster than searching for "&" (char vs string). I would like to keep 
>> this as fast as possible, so I suspect there will only be two options, 
>> here: & or &amp; and the search-and-replacement will use two different 
>> mechanisms.
>>
>> Before I commit to using separate code, I'll set up a proper 
>> performance test to see what the effective difference is. I think that 
>> a speedup of more than 5% is probably worth it, given how often these 
>> calls are likely to be made.
> 
> Currently you are removing the query parameter and the re-adding it. 
> What percentage of those operations are effectively a NO-OP? Is it high 
> enough to detect the NO-OP cases and do nothing?

Good question.

The most common case is that the URL doesn't have any csrf parameter in 
it at all. If that happens, then the search will repeatedly search for 
'&', match nothing, and make no modifications to the url String value.

If a URL is being repeatedly sent through response.encodeURL() during 
the same request (which is the only time I can think of where we'd 
replace csrf=ABC with the exact same token) then I think that's an 
application problem. This code protects against that, but it also is 
able to take a URL with an "old" token and replace it with the 
most-recent one.

So I don't think I need to change the code at all at this point in order 
to improve performance for the no-op case.

I'm happy to hear a counter-argument. It would be trivial to check to 
see if the current value equals the discovered value.... but then the 
logic needs to change to avoid adding it a *second time*.

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Mark Thomas <ma...@apache.org>.
On 24/03/2026 15:44, Christopher Schultz wrote:
> Konstantin,
> 
> On 3/24/26 7:24 AM, Konstantin Kolinko wrote:
>> вт, 24 мар. 2026 г. в 12:13, Mark Thomas <ma...@apache.org>:

<snip/>

>>> You have convinced me.
>>>
>>> I withdraw my objection to handling "&amp;" as equivalent to "&" in
>>> encodeURL() and encodeRedirectURL().
>>
>> I see a problem.
>> It is easy to implement removeQueryParameters() to remove either 
>> "&amp;" or "&",
>> but a problem is with the subsequent addNonce() call.
> 
> Yup. I was thinking about that as I wrote my previous post.
> 
>> If the original URL had more than one parameter
>> then you can detect whether it used "&" or "%amp;", and use the same.
>>
>> But if it had a single parameter then there is no way to decide what
>> delimiter should be used.
>>
>> If you decide to implement this feature, please make it configurable.
> 
> +1
> 
> Unfortunately, I *think* the performance characteristics will be 
> different depending upon what kind of delimiter is being used. Searching 
> for a single code point ('&') is faster than searching for a substring 
> ("&amp;"). I would also imagine that searching for '&' is faster than 
> searching for "&" (char vs string). I would like to keep this as fast as 
> possible, so I suspect there will only be two options, here: & or &amp; 
> and the search-and-replacement will use two different mechanisms.
> 
> Before I commit to using separate code, I'll set up a proper performance 
> test to see what the effective difference is. I think that a speedup of 
> more than 5% is probably worth it, given how often these calls are 
> likely to be made.

Currently you are removing the query parameter and the re-adding it. 
What percentage of those operations are effectively a NO-OP? Is it high 
enough to detect the NO-OP cases and do nothing?

Just a thought.

Mark


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Konstantin,

On 3/24/26 7:24 AM, Konstantin Kolinko wrote:
> вт, 24 мар. 2026 г. в 12:13, Mark Thomas <ma...@apache.org>:
>>
>> On 23/03/2026 22:05, Christopher Schultz wrote:
>>> Mark,
>>>
>>> On 3/3/26 10:00 AM, Christopher Schultz wrote:
>>>> Mark,
>>>>
>>>> On 2/25/26 4:23 AM, Mark Thomas wrote:
>>
>> <snip/>
>>
>>>>> I disagree.
>>>>>
>>>>> Something LinkTool ? is confusing HTML encoding and URL encoding.
>>>>>
>>>>> A literal & is encoded as &amp; in HTML (and XML).
>>>>>
>>>>> A literal & is encoded as %26 if it needs to be encoded in a URI.
>>>>>
>>>>> I think you need to get to the bottom of why a URI is being encoded
>>>>> using HTML encoding and fix that. Once that has been addressed then
>>>>> you can look at the URI and see if any further work is required.
>>>>
>>>> Yes, I'm using LinkTool. I honestly think the problem is probably there.
>>>>
>>>> LinkTool uses a fluent API (ugh) and any time you convert the object
>>>> to a String, it will use &amp; because it's intended to be used for
>>>> generating links in HTML.
>>>>
>>>> We have a use case where we need to create a LinkTool object and re-
>>>> use it for a lot of links based upon a single root link (this use case
>>>> is a pager, where the only thing that changes is a parameter or two).
>>>>
>>>> I think the issue is when we parse a URL string, it's not reversing
>>>> its own process of & -> &amp;.
>>>>
>>>> So either LinkTool needs to change, or I need to massage the strings
>>>> passed-into it for that purpose.
>>>
>>> This is not the issue.
>>>
>>> The problem is that LinkTool's toString implementation looks like this:
>>>
>>>     public String toString() {
>>>       String str = super.toString();
>>>       return str.length() == 0 ? str : this.response.encodeURL(str);
>>>     }
>>>
>>> The super.toString() call is what generates the entire URL from all the
>>> other components. It's sensitive to its "xhtml" configuration flag and,
>>> when that's set to "true", it will generate parameter delimiters as
>>> "&amp;" instead of "&".
>>>
>>> I believe there are only a few options here:
>>>
>>> 1. Stop using XHTML=true in Velocity Tools
>>> 2. Change Tomcat to allow the use of &amp;-separated parameters
>>> 3. Modify LinkTool to stop calling response.encodeURL
>>>
>>> I believe that Velocity Tools's implementation of XHTML-formatted URLs
>>> is simply incompatible with Tomcat's implementation of CSRF parameter
>>> handling.
>>>
>>> This doesn't happen elsewhere in Tomcat because jsessionid is the only
>>> other similar operation we perform, and the ; doesn't need any special
>>> handling in XHTML.
>>>
>>> I'd like to reconsider whether &amp; should be supported, here. It's not
>>> absolutely clear what SHOULD be done, here, but I'd like to continue the
>>> conversation  a little to see what we can come up with.
>>>
>>> If &amp; is NOT supported, here, then applications wanting to generate
>>> &amp;-separated URLs will have to:
>>>
>>> 1. Generate a URL
>>> 2. Run it through response.encodeURL
>>> 3. Post-process one more time to swap & -> &amp;
>>>
>>> To remove LinkTool from the conversation we could compare to how this
>>> would need to be done using JSTL:
>>>
>>> <c:url var="myUrl" value="/products">
>>>       <c:param name="id" value="123" />
>>>       <c:param name="category" value="books & electronics" />
>>> </c:url>
>>>
>>> <a href="${myUrl}">View Product</a>
>>>
>>> To get XHTML semantics, the application would need to change the final
>>> line to this:
>>>
>>> <a href="<c:out value='${myUrl}' />">View Product</a>
>>>
>>> That's fine for a single page, but doing it for the entire application
>>> isn't practical. The application can use .jspx and I think it will use
>>> &amp; but that happens in the XML serializer at the very end of the
>>> whole process.
>>>
>>> I think Velocity Tools has just decided to handle that conversion
>>> because there is no JSPX-style post-conversion process available to it.
>>>
>>> I could somewhat easily be convinced that "generating URLs in a page
>>> with &amp; delimiters is an experiment that failed, and we should forget
>>> that it ever happened."
>>
>> You have convinced me.
>>
>> I withdraw my objection to handling "&amp;" as equivalent to "&" in
>> encodeURL() and encodeRedirectURL().
> 
> I see a problem.
> It is easy to implement removeQueryParameters() to remove either "&amp;" or "&",
> but a problem is with the subsequent addNonce() call.

Yup. I was thinking about that as I wrote my previous post.

> If the original URL had more than one parameter
> then you can detect whether it used "&" or "%amp;", and use the same.
> 
> But if it had a single parameter then there is no way to decide what
> delimiter should be used.
> 
> If you decide to implement this feature, please make it configurable.

+1

Unfortunately, I *think* the performance characteristics will be 
different depending upon what kind of delimiter is being used. Searching 
for a single code point ('&') is faster than searching for a substring 
("&amp;"). I would also imagine that searching for '&' is faster than 
searching for "&" (char vs string). I would like to keep this as fast as 
possible, so I suspect there will only be two options, here: & or &amp; 
and the search-and-replacement will use two different mechanisms.

Before I commit to using separate code, I'll set up a proper performance 
test to see what the effective difference is. I think that a speedup of 
more than 5% is probably worth it, given how often these calls are 
likely to be made.

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Konstantin Kolinko <kn...@gmail.com>.
вт, 24 мар. 2026 г. в 12:13, Mark Thomas <ma...@apache.org>:
>
> On 23/03/2026 22:05, Christopher Schultz wrote:
> > Mark,
> >
> > On 3/3/26 10:00 AM, Christopher Schultz wrote:
> >> Mark,
> >>
> >> On 2/25/26 4:23 AM, Mark Thomas wrote:
>
> <snip/>
>
> >>> I disagree.
> >>>
> >>> Something LinkTool ? is confusing HTML encoding and URL encoding.
> >>>
> >>> A literal & is encoded as &amp; in HTML (and XML).
> >>>
> >>> A literal & is encoded as %26 if it needs to be encoded in a URI.
> >>>
> >>> I think you need to get to the bottom of why a URI is being encoded
> >>> using HTML encoding and fix that. Once that has been addressed then
> >>> you can look at the URI and see if any further work is required.
> >>
> >> Yes, I'm using LinkTool. I honestly think the problem is probably there.
> >>
> >> LinkTool uses a fluent API (ugh) and any time you convert the object
> >> to a String, it will use &amp; because it's intended to be used for
> >> generating links in HTML.
> >>
> >> We have a use case where we need to create a LinkTool object and re-
> >> use it for a lot of links based upon a single root link (this use case
> >> is a pager, where the only thing that changes is a parameter or two).
> >>
> >> I think the issue is when we parse a URL string, it's not reversing
> >> its own process of & -> &amp;.
> >>
> >> So either LinkTool needs to change, or I need to massage the strings
> >> passed-into it for that purpose.
> >
> > This is not the issue.
> >
> > The problem is that LinkTool's toString implementation looks like this:
> >
> >    public String toString() {
> >      String str = super.toString();
> >      return str.length() == 0 ? str : this.response.encodeURL(str);
> >    }
> >
> > The super.toString() call is what generates the entire URL from all the
> > other components. It's sensitive to its "xhtml" configuration flag and,
> > when that's set to "true", it will generate parameter delimiters as
> > "&amp;" instead of "&".
> >
> > I believe there are only a few options here:
> >
> > 1. Stop using XHTML=true in Velocity Tools
> > 2. Change Tomcat to allow the use of &amp;-separated parameters
> > 3. Modify LinkTool to stop calling response.encodeURL
> >
> > I believe that Velocity Tools's implementation of XHTML-formatted URLs
> > is simply incompatible with Tomcat's implementation of CSRF parameter
> > handling.
> >
> > This doesn't happen elsewhere in Tomcat because jsessionid is the only
> > other similar operation we perform, and the ; doesn't need any special
> > handling in XHTML.
> >
> > I'd like to reconsider whether &amp; should be supported, here. It's not
> > absolutely clear what SHOULD be done, here, but I'd like to continue the
> > conversation  a little to see what we can come up with.
> >
> > If &amp; is NOT supported, here, then applications wanting to generate
> > &amp;-separated URLs will have to:
> >
> > 1. Generate a URL
> > 2. Run it through response.encodeURL
> > 3. Post-process one more time to swap & -> &amp;
> >
> > To remove LinkTool from the conversation we could compare to how this
> > would need to be done using JSTL:
> >
> > <c:url var="myUrl" value="/products">
> >      <c:param name="id" value="123" />
> >      <c:param name="category" value="books & electronics" />
> > </c:url>
> >
> > <a href="${myUrl}">View Product</a>
> >
> > To get XHTML semantics, the application would need to change the final
> > line to this:
> >
> > <a href="<c:out value='${myUrl}' />">View Product</a>
> >
> > That's fine for a single page, but doing it for the entire application
> > isn't practical. The application can use .jspx and I think it will use
> > &amp; but that happens in the XML serializer at the very end of the
> > whole process.
> >
> > I think Velocity Tools has just decided to handle that conversion
> > because there is no JSPX-style post-conversion process available to it.
> >
> > I could somewhat easily be convinced that "generating URLs in a page
> > with &amp; delimiters is an experiment that failed, and we should forget
> > that it ever happened."
>
> You have convinced me.
>
> I withdraw my objection to handling "&amp;" as equivalent to "&" in
> encodeURL() and encodeRedirectURL().

I see a problem.
It is easy to implement removeQueryParameters() to remove either "&amp;" or "&",
but a problem is with the subsequent addNonce() call.

If the original URL had more than one parameter
then you can detect whether it used "&" or "%amp;", and use the same.

But if it had a single parameter then there is no way to decide what
delimiter should be used.

If you decide to implement this feature, please make it configurable.

Best regards,
Konstantin Kolinko

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Mark Thomas <ma...@apache.org>.
On 23/03/2026 22:05, Christopher Schultz wrote:
> Mark,
> 
> On 3/3/26 10:00 AM, Christopher Schultz wrote:
>> Mark,
>>
>> On 2/25/26 4:23 AM, Mark Thomas wrote:

<snip/>

>>> I disagree.
>>>
>>> Something LinkTool ? is confusing HTML encoding and URL encoding.
>>>
>>> A literal & is encoded as &amp; in HTML (and XML).
>>>
>>> A literal & is encoded as %26 if it needs to be encoded in a URI.
>>>
>>> I think you need to get to the bottom of why a URI is being encoded 
>>> using HTML encoding and fix that. Once that has been addressed then 
>>> you can look at the URI and see if any further work is required.
>>
>> Yes, I'm using LinkTool. I honestly think the problem is probably there.
>>
>> LinkTool uses a fluent API (ugh) and any time you convert the object 
>> to a String, it will use &amp; because it's intended to be used for 
>> generating links in HTML.
>>
>> We have a use case where we need to create a LinkTool object and re- 
>> use it for a lot of links based upon a single root link (this use case 
>> is a pager, where the only thing that changes is a parameter or two).
>>
>> I think the issue is when we parse a URL string, it's not reversing 
>> its own process of & -> &amp;.
>>
>> So either LinkTool needs to change, or I need to massage the strings 
>> passed-into it for that purpose.
> 
> This is not the issue.
> 
> The problem is that LinkTool's toString implementation looks like this:
> 
>    public String toString() {
>      String str = super.toString();
>      return str.length() == 0 ? str : this.response.encodeURL(str);
>    }
> 
> The super.toString() call is what generates the entire URL from all the 
> other components. It's sensitive to its "xhtml" configuration flag and, 
> when that's set to "true", it will generate parameter delimiters as 
> "&amp;" instead of "&".
> 
> I believe there are only a few options here:
> 
> 1. Stop using XHTML=true in Velocity Tools
> 2. Change Tomcat to allow the use of &amp;-separated parameters
> 3. Modify LinkTool to stop calling response.encodeURL
> 
> I believe that Velocity Tools's implementation of XHTML-formatted URLs 
> is simply incompatible with Tomcat's implementation of CSRF parameter 
> handling.
> 
> This doesn't happen elsewhere in Tomcat because jsessionid is the only 
> other similar operation we perform, and the ; doesn't need any special 
> handling in XHTML.
> 
> I'd like to reconsider whether &amp; should be supported, here. It's not 
> absolutely clear what SHOULD be done, here, but I'd like to continue the 
> conversation  a little to see what we can come up with.
> 
> If &amp; is NOT supported, here, then applications wanting to generate 
> &amp;-separated URLs will have to:
> 
> 1. Generate a URL
> 2. Run it through response.encodeURL
> 3. Post-process one more time to swap & -> &amp;
> 
> To remove LinkTool from the conversation we could compare to how this 
> would need to be done using JSTL:
> 
> <c:url var="myUrl" value="/products">
>      <c:param name="id" value="123" />
>      <c:param name="category" value="books & electronics" />
> </c:url>
> 
> <a href="${myUrl}">View Product</a>
> 
> To get XHTML semantics, the application would need to change the final 
> line to this:
> 
> <a href="<c:out value='${myUrl}' />">View Product</a>
> 
> That's fine for a single page, but doing it for the entire application 
> isn't practical. The application can use .jspx and I think it will use 
> &amp; but that happens in the XML serializer at the very end of the 
> whole process.
> 
> I think Velocity Tools has just decided to handle that conversion 
> because there is no JSPX-style post-conversion process available to it.
> 
> I could somewhat easily be convinced that "generating URLs in a page 
> with &amp; delimiters is an experiment that failed, and we should forget 
> that it ever happened."

You have convinced me.

I withdraw my objection to handling "&amp;" as equivalent to "&" in 
encodeURL() and encodeRedirectURL().

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Mark,

On 3/3/26 10:00 AM, Christopher Schultz wrote:
> Mark,
> 
> On 2/25/26 4:23 AM, Mark Thomas wrote:
>> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
>>
>>> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
>>> chris@christopherschultz.net> wrote:
>>>
>>>> All,
>>>>
>>>> I recently made a change to the CSRF prevention filter in
>>>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
>>>> tokens in URLs in case they were being repeatedly run through
>>>> HttpServletResponse.encode().
>>>>
>>>> It's possible there is a bug hiding in there but I'd like some 
>>>> feedback.
>>>>
>>>> I'm using Velocity Tools to generate URLs and there is a tool class
>>>> called LinkTool. It generates links with a builder-style interface with
>>>> a base URL, parameters, and so on.
>>>>
>>>> After upgrading to 9.0.115, I'm finding that a very small number of
>>>> these links are being broken due to an interaction between LinkTool, my
>>>> code, and the CsrfPreventionFilter's massaging of the URLs its 
>>>> producing.
>>>>
>>>> The LinkTool is configured to produce HTML5-style URLs where the &
>>>> characters are encoded as &amp;. I did triple-check to confirm that 
>>>> this
>>>> was both expected and a reasonable reading of the HTML standard, which
>>>> it appears to be. That is, use of &amp; is actually recommended in HTML
>>>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is amazing
>>>> enough that I'll go ahead and call it a Law.
>>>>
>>>> Anyway, it looks like the original URL is being produced something like
>>>> this:
>>>>
>>>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>>
>>>> So far so good. Note that the URL has already gone through
>>>> response.encodeURL() because it's got a csrf parameter in it.
>>>>
>>>> Then another parameter is added by parsing the URL above (because
>>>> #reasons, this is where my code comes in) and the URL then becomes:
>>>>
>>>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>>
>>>> I haven't traced through the code, but I believe what's happening is 
>>>> this:
>>>>
>>>> 1. My code, through LinkTool, adds the "id" = "4" parameter
>>>> 2. LinkTool re-generates the URL String including an &amp; parameter
>>>> separator
>>>> 3. LinkTool runs the result through response.encodeURL
>>>> 4. CsrfPreventionFilter removes any existing CSRF query parameters; it
>>>> finds one at the beginning of the URL and removes everything up to the
>>>> next & character
>>>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
>>>>
>>>> The URL is now rendered and it's broken. :/
>>>>
>>>> I think plausible arguments could be made that this is a bug in any of
>>>> these 3 products: mine, VelocityTools, or Tomcat.
>>>>
>>>> I'm primarily interested in getting this fixed in *my* product, but I
>>>> wonder how many other products it might effect for similar reasons.
>>>>
>>>> So, the topic here is only Tomcat-related: do we think that the
>>>> CsrfPreventionFilter should consider &amp; to be a request parameter
>>>> separator for the purposes of removing its own duplicates?
>>>>
>>>> My reading of a few specs suggests that looking for "&amp;" should be
>>>> safe since ; is a reserved character and ought to be escaped if the 
>>>> user
>>>> actually intends for there to be an honest-to-goodness semicolon in
>>>> their request parameter name or value. Thus, &amp; must mean that a
>>>> literal & was intended.
>>>>
>>>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and not
>>>> just "&" in this case.
>>>>
>>>> Opinions?
>>>>
>>>> -chris
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
>>>> For additional commands, e-mail: dev-help@tomcat.apache.org
>>>>
>>>>
>>> I agree, Tomcat should be fixed to consume  "&amp;", since it currently
>>> breaks valid urls.
>>>
>>> Kind regards,
>>> Dimitris
>>
>> I disagree.
>>
>> Something LinkTool ? is confusing HTML encoding and URL encoding.
>>
>> A literal & is encoded as &amp; in HTML (and XML).
>>
>> A literal & is encoded as %26 if it needs to be encoded in a URI.
>>
>> I think you need to get to the bottom of why a URI is being encoded 
>> using HTML encoding and fix that. Once that has been addressed then 
>> you can look at the URI and see if any further work is required.
> 
> Yes, I'm using LinkTool. I honestly think the problem is probably there.
> 
> LinkTool uses a fluent API (ugh) and any time you convert the object to 
> a String, it will use &amp; because it's intended to be used for 
> generating links in HTML.
> 
> We have a use case where we need to create a LinkTool object and re-use 
> it for a lot of links based upon a single root link (this use case is a 
> pager, where the only thing that changes is a parameter or two).
> 
> I think the issue is when we parse a URL string, it's not reversing its 
> own process of & -> &amp;.
> 
> So either LinkTool needs to change, or I need to massage the strings 
> passed-into it for that purpose.

This is not the issue.

The problem is that LinkTool's toString implementation looks like this:

   public String toString() {
     String str = super.toString();
     return str.length() == 0 ? str : this.response.encodeURL(str);
   }

The super.toString() call is what generates the entire URL from all the 
other components. It's sensitive to its "xhtml" configuration flag and, 
when that's set to "true", it will generate parameter delimiters as 
"&amp;" instead of "&".

I believe there are only a few options here:

1. Stop using XHTML=true in Velocity Tools
2. Change Tomcat to allow the use of &amp;-separated parameters
3. Modify LinkTool to stop calling response.encodeURL

I believe that Velocity Tools's implementation of XHTML-formatted URLs 
is simply incompatible with Tomcat's implementation of CSRF parameter 
handling.

This doesn't happen elsewhere in Tomcat because jsessionid is the only 
other similar operation we perform, and the ; doesn't need any special 
handling in XHTML.

I'd like to reconsider whether &amp; should be supported, here. It's not 
absolutely clear what SHOULD be done, here, but I'd like to continue the 
conversation  a little to see what we can come up with.

If &amp; is NOT supported, here, then applications wanting to generate 
&amp;-separated URLs will have to:

1. Generate a URL
2. Run it through response.encodeURL
3. Post-process one more time to swap & -> &amp;

To remove LinkTool from the conversation we could compare to how this 
would need to be done using JSTL:

<c:url var="myUrl" value="/products">
     <c:param name="id" value="123" />
     <c:param name="category" value="books & electronics" />
</c:url>

<a href="${myUrl}">View Product</a>

To get XHTML semantics, the application would need to change the final 
line to this:

<a href="<c:out value='${myUrl}' />">View Product</a>

That's fine for a single page, but doing it for the entire application 
isn't practical. The application can use .jspx and I think it will use 
&amp; but that happens in the XML serializer at the very end of the 
whole process.

I think Velocity Tools has just decided to handle that conversion 
because there is no JSPX-style post-conversion process available to it.

I could somewhat easily be convinced that "generating URLs in a page 
with &amp; delimiters is an experiment that failed, and we should forget 
that it ever happened."

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Mark,

On 2/25/26 4:23 AM, Mark Thomas wrote:
> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
> 
>> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
>> chris@christopherschultz.net> wrote:
>>
>>> All,
>>>
>>> I recently made a change to the CSRF prevention filter in
>>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
>>> tokens in URLs in case they were being repeatedly run through
>>> HttpServletResponse.encode().
>>>
>>> It's possible there is a bug hiding in there but I'd like some feedback.
>>>
>>> I'm using Velocity Tools to generate URLs and there is a tool class
>>> called LinkTool. It generates links with a builder-style interface with
>>> a base URL, parameters, and so on.
>>>
>>> After upgrading to 9.0.115, I'm finding that a very small number of
>>> these links are being broken due to an interaction between LinkTool, my
>>> code, and the CsrfPreventionFilter's massaging of the URLs its 
>>> producing.
>>>
>>> The LinkTool is configured to produce HTML5-style URLs where the &
>>> characters are encoded as &amp;. I did triple-check to confirm that this
>>> was both expected and a reasonable reading of the HTML standard, which
>>> it appears to be. That is, use of &amp; is actually recommended in HTML
>>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is amazing
>>> enough that I'll go ahead and call it a Law.
>>>
>>> Anyway, it looks like the original URL is being produced something like
>>> this:
>>>
>>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>
>>> So far so good. Note that the URL has already gone through
>>> response.encodeURL() because it's got a csrf parameter in it.
>>>
>>> Then another parameter is added by parsing the URL above (because
>>> #reasons, this is where my code comes in) and the URL then becomes:
>>>
>>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>
>>> I haven't traced through the code, but I believe what's happening is 
>>> this:
>>>
>>> 1. My code, through LinkTool, adds the "id" = "4" parameter
>>> 2. LinkTool re-generates the URL String including an &amp; parameter
>>> separator
>>> 3. LinkTool runs the result through response.encodeURL
>>> 4. CsrfPreventionFilter removes any existing CSRF query parameters; it
>>> finds one at the beginning of the URL and removes everything up to the
>>> next & character
>>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
>>>
>>> The URL is now rendered and it's broken. :/
>>>
>>> I think plausible arguments could be made that this is a bug in any of
>>> these 3 products: mine, VelocityTools, or Tomcat.
>>>
>>> I'm primarily interested in getting this fixed in *my* product, but I
>>> wonder how many other products it might effect for similar reasons.
>>>
>>> So, the topic here is only Tomcat-related: do we think that the
>>> CsrfPreventionFilter should consider &amp; to be a request parameter
>>> separator for the purposes of removing its own duplicates?
>>>
>>> My reading of a few specs suggests that looking for "&amp;" should be
>>> safe since ; is a reserved character and ought to be escaped if the user
>>> actually intends for there to be an honest-to-goodness semicolon in
>>> their request parameter name or value. Thus, &amp; must mean that a
>>> literal & was intended.
>>>
>>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and not
>>> just "&" in this case.
>>>
>>> Opinions?
>>>
>>> -chris
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
>>> For additional commands, e-mail: dev-help@tomcat.apache.org
>>>
>>>
>> I agree, Tomcat should be fixed to consume  "&amp;", since it currently
>> breaks valid urls.
>>
>> Kind regards,
>> Dimitris
> 
> I disagree.
> 
> Something LinkTool ? is confusing HTML encoding and URL encoding.
> 
> A literal & is encoded as &amp; in HTML (and XML).
> 
> A literal & is encoded as %26 if it needs to be encoded in a URI.
> 
> I think you need to get to the bottom of why a URI is being encoded 
> using HTML encoding and fix that. Once that has been addressed then you 
> can look at the URI and see if any further work is required.

Yes, I'm using LinkTool. I honestly think the problem is probably there.

LinkTool uses a fluent API (ugh) and any time you convert the object to 
a String, it will use &amp; because it's intended to be used for 
generating links in HTML.

We have a use case where we need to create a LinkTool object and re-use 
it for a lot of links based upon a single root link (this use case is a 
pager, where the only thing that changes is a parameter or two).

I think the issue is when we parse a URL string, it's not reversing its 
own process of & -> &amp;.

So either LinkTool needs to change, or I need to massage the strings 
passed-into it for that purpose.

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Juancarlos Segovia <ju...@gmail.com>.
ok

El jue, 26 feb 2026 5:44, Dimitris Soumis <ds...@apache.org> escribió:

> On Thu, Feb 26, 2026 at 12:55 AM Mark Thomas via dev <
> dev@tomcat.apache.org>
> wrote:
>
> > 25 Feb 2026 23:29:29 Dimitris Soumis <ds...@apache.org>:
> >
> > > On Wed, Feb 25, 2026 at 11:31 AM Mark Thomas <ma...@apache.org> wrote:
> > >
> > >> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
> > >>
> > >>> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
> > >>> chris@christopherschultz.net> wrote:
> > >>>
> > >>>> All,
> > >>>>
> > >>>> I recently made a change to the CSRF prevention filter in
> > >>>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
> > >>>> tokens in URLs in case they were being repeatedly run through
> > >>>> HttpServletResponse.encode().
> > >>>>
> > >>>> It's possible there is a bug hiding in there but I'd like some
> > >>>> feedback.
> > >>>>
> > >>>> I'm using Velocity Tools to generate URLs and there is a tool class
> > >>>> called LinkTool. It generates links with a builder-style interface
> > >>>> with
> > >>>> a base URL, parameters, and so on.
> > >>>>
> > >>>> After upgrading to 9.0.115, I'm finding that a very small number of
> > >>>> these links are being broken due to an interaction between LinkTool,
> > >>>> my
> > >>>> code, and the CsrfPreventionFilter's massaging of the URLs its
> > >>>> producing.
> > >>>>
> > >>>> The LinkTool is configured to produce HTML5-style URLs where the &
> > >>>> characters are encoded as &amp;. I did triple-check to confirm that
> > >>>> this
> > >>>> was both expected and a reasonable reading of the HTML standard,
> > >>>> which
> > >>>> it appears to be. That is, use of &amp; is actually recommended in
> > >>>> HTML
> > >>>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is
> > >>>> amazing
> > >>>> enough that I'll go ahead and call it a Law.
> > >>>>
> > >>>> Anyway, it looks like the original URL is being produced something
> > >>>> like
> > >>>> this:
> > >>>>
> > >>>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> > >>>>
> > >>>> So far so good. Note that the URL has already gone through
> > >>>> response.encodeURL() because it's got a csrf parameter in it.
> > >>>>
> > >>>> Then another parameter is added by parsing the URL above (because
> > >>>> #reasons, this is where my code comes in) and the URL then becomes:
> > >>>>
> > >>>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> > >>>>
> > >>>> I haven't traced through the code, but I believe what's happening is
> > >>>> this:
> > >>>>
> > >>>> 1. My code, through LinkTool, adds the "id" = "4" parameter
> > >>>> 2. LinkTool re-generates the URL String including an &amp; parameter
> > >>>> separator
> > >>>> 3. LinkTool runs the result through response.encodeURL
> > >>>> 4. CsrfPreventionFilter removes any existing CSRF query parameters;
> > >>>> it
> > >>>> finds one at the beginning of the URL and removes everything up to
> > >>>> the
> > >>>> next & character
> > >>>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
> > >>>>
> > >>>> The URL is now rendered and it's broken. :/
> > >>>>
> > >>>> I think plausible arguments could be made that this is a bug in any
> > >>>> of
> > >>>> these 3 products: mine, VelocityTools, or Tomcat.
> > >>>>
> > >>>> I'm primarily interested in getting this fixed in *my* product, but
> > >>>> I
> > >>>> wonder how many other products it might effect for similar reasons.
> > >>>>
> > >>>> So, the topic here is only Tomcat-related: do we think that the
> > >>>> CsrfPreventionFilter should consider &amp; to be a request parameter
> > >>>> separator for the purposes of removing its own duplicates?
> > >>>>
> > >>>> My reading of a few specs suggests that looking for "&amp;" should
> > >>>> be
> > >>>> safe since ; is a reserved character and ought to be escaped if the
> > >>>> user
> > >>>> actually intends for there to be an honest-to-goodness semicolon in
> > >>>> their request parameter name or value. Thus, &amp; must mean that a
> > >>>> literal & was intended.
> > >>>>
> > >>>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and
> > >>>> not
> > >>>> just "&" in this case.
> > >>>>
> > >>>> Opinions?
> > >>>>
> > >>>> -chris
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> ---------------------------------------------------------------------
> > >>>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> > >>>> For additional commands, e-mail: dev-help@tomcat.apache.org
> > >>>>
> > >>>>
> > >>> I agree, Tomcat should be fixed to consume  "&amp;", since it
> > >>> currently
> > >>> breaks valid urls.
> > >>>
> > >>> Kind regards,
> > >>> Dimitris
> > >>
> > >> I disagree.
> > >>
> > >> Something LinkTool ? is confusing HTML encoding and URL encoding.
> > >>
> > >> A literal & is encoded as &amp; in HTML (and XML).
> > >>
> > >> A literal & is encoded as %26 if it needs to be encoded in a URI.
> > >>
> > >> I think you need to get to the bottom of why a URI is being encoded
> > >> using
> > >> HTML encoding and fix that. Once that has been addressed then you can
> > >> look at the URI and see if any further work is required.
> > >>
> > >> Mark
> > >>
> > >> ---------------------------------------------------------------------
> > >> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> > >> For additional commands, e-mail: dev-help@tomcat.apache.org
> > >>
> > >>
> > > Still, I believe removeQueryParameters() should be modified to handle
> > > "%26"
> > > and other possible encodings.
> >
> > %26 shouldn't need any special handling. It should pass through unaltered
> > - as should any %nn encoding.
> >
> > > Although "&amp;" is HTML encoding and seems that LinkTool is
> > > misbehaving,
> > > the result is still a valid URL. Thus, why not to catch that case?
> >
> > Two reasons.
> >
> > If the URI is valid Tomcat can't tell if it is actually what the user
> > wanted or if it is because LinkTool is being used and LinkTool is
> > misbehaving.
> >
> > Unless there is a very strong case to do so (think major browser vendor
> > not following the RFCs and having a long history of ignoring bug reports
> > causing breakage that affects lots of Tomcat users) we don't provide
> > workarounds for other people's broken code.
> >
> > Mark
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> > For additional commands, e-mail: dev-help@tomcat.apache.org
> >
> >
> It makes total sense, thanks for the explanation.
>
> Dimitris
>

Re: CSRF prevention filter changes - possible bug?

Posted by Dimitris Soumis <ds...@apache.org>.
On Thu, Feb 26, 2026 at 12:55 AM Mark Thomas via dev <de...@tomcat.apache.org>
wrote:

> 25 Feb 2026 23:29:29 Dimitris Soumis <ds...@apache.org>:
>
> > On Wed, Feb 25, 2026 at 11:31 AM Mark Thomas <ma...@apache.org> wrote:
> >
> >> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
> >>
> >>> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
> >>> chris@christopherschultz.net> wrote:
> >>>
> >>>> All,
> >>>>
> >>>> I recently made a change to the CSRF prevention filter in
> >>>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
> >>>> tokens in URLs in case they were being repeatedly run through
> >>>> HttpServletResponse.encode().
> >>>>
> >>>> It's possible there is a bug hiding in there but I'd like some
> >>>> feedback.
> >>>>
> >>>> I'm using Velocity Tools to generate URLs and there is a tool class
> >>>> called LinkTool. It generates links with a builder-style interface
> >>>> with
> >>>> a base URL, parameters, and so on.
> >>>>
> >>>> After upgrading to 9.0.115, I'm finding that a very small number of
> >>>> these links are being broken due to an interaction between LinkTool,
> >>>> my
> >>>> code, and the CsrfPreventionFilter's massaging of the URLs its
> >>>> producing.
> >>>>
> >>>> The LinkTool is configured to produce HTML5-style URLs where the &
> >>>> characters are encoded as &amp;. I did triple-check to confirm that
> >>>> this
> >>>> was both expected and a reasonable reading of the HTML standard,
> >>>> which
> >>>> it appears to be. That is, use of &amp; is actually recommended in
> >>>> HTML
> >>>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is
> >>>> amazing
> >>>> enough that I'll go ahead and call it a Law.
> >>>>
> >>>> Anyway, it looks like the original URL is being produced something
> >>>> like
> >>>> this:
> >>>>
> >>>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> >>>>
> >>>> So far so good. Note that the URL has already gone through
> >>>> response.encodeURL() because it's got a csrf parameter in it.
> >>>>
> >>>> Then another parameter is added by parsing the URL above (because
> >>>> #reasons, this is where my code comes in) and the URL then becomes:
> >>>>
> >>>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> >>>>
> >>>> I haven't traced through the code, but I believe what's happening is
> >>>> this:
> >>>>
> >>>> 1. My code, through LinkTool, adds the "id" = "4" parameter
> >>>> 2. LinkTool re-generates the URL String including an &amp; parameter
> >>>> separator
> >>>> 3. LinkTool runs the result through response.encodeURL
> >>>> 4. CsrfPreventionFilter removes any existing CSRF query parameters;
> >>>> it
> >>>> finds one at the beginning of the URL and removes everything up to
> >>>> the
> >>>> next & character
> >>>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
> >>>>
> >>>> The URL is now rendered and it's broken. :/
> >>>>
> >>>> I think plausible arguments could be made that this is a bug in any
> >>>> of
> >>>> these 3 products: mine, VelocityTools, or Tomcat.
> >>>>
> >>>> I'm primarily interested in getting this fixed in *my* product, but
> >>>> I
> >>>> wonder how many other products it might effect for similar reasons.
> >>>>
> >>>> So, the topic here is only Tomcat-related: do we think that the
> >>>> CsrfPreventionFilter should consider &amp; to be a request parameter
> >>>> separator for the purposes of removing its own duplicates?
> >>>>
> >>>> My reading of a few specs suggests that looking for "&amp;" should
> >>>> be
> >>>> safe since ; is a reserved character and ought to be escaped if the
> >>>> user
> >>>> actually intends for there to be an honest-to-goodness semicolon in
> >>>> their request parameter name or value. Thus, &amp; must mean that a
> >>>> literal & was intended.
> >>>>
> >>>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and
> >>>> not
> >>>> just "&" in this case.
> >>>>
> >>>> Opinions?
> >>>>
> >>>> -chris
> >>>>
> >>>>
> >>>>
> >>>> ---------------------------------------------------------------------
> >>>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> >>>> For additional commands, e-mail: dev-help@tomcat.apache.org
> >>>>
> >>>>
> >>> I agree, Tomcat should be fixed to consume  "&amp;", since it
> >>> currently
> >>> breaks valid urls.
> >>>
> >>> Kind regards,
> >>> Dimitris
> >>
> >> I disagree.
> >>
> >> Something LinkTool ? is confusing HTML encoding and URL encoding.
> >>
> >> A literal & is encoded as &amp; in HTML (and XML).
> >>
> >> A literal & is encoded as %26 if it needs to be encoded in a URI.
> >>
> >> I think you need to get to the bottom of why a URI is being encoded
> >> using
> >> HTML encoding and fix that. Once that has been addressed then you can
> >> look at the URI and see if any further work is required.
> >>
> >> Mark
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> >> For additional commands, e-mail: dev-help@tomcat.apache.org
> >>
> >>
> > Still, I believe removeQueryParameters() should be modified to handle
> > "%26"
> > and other possible encodings.
>
> %26 shouldn't need any special handling. It should pass through unaltered
> - as should any %nn encoding.
>
> > Although "&amp;" is HTML encoding and seems that LinkTool is
> > misbehaving,
> > the result is still a valid URL. Thus, why not to catch that case?
>
> Two reasons.
>
> If the URI is valid Tomcat can't tell if it is actually what the user
> wanted or if it is because LinkTool is being used and LinkTool is
> misbehaving.
>
> Unless there is a very strong case to do so (think major browser vendor
> not following the RFCs and having a long history of ignoring bug reports
> causing breakage that affects lots of Tomcat users) we don't provide
> workarounds for other people's broken code.
>
> Mark
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>
It makes total sense, thanks for the explanation.

Dimitris

Re: CSRF prevention filter changes - possible bug?

Posted by Mark Thomas via dev <de...@tomcat.apache.org>.
25 Feb 2026 23:29:29 Dimitris Soumis <ds...@apache.org>:

> On Wed, Feb 25, 2026 at 11:31 AM Mark Thomas <ma...@apache.org> wrote:
>
>> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
>>
>>> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
>>> chris@christopherschultz.net> wrote:
>>>
>>>> All,
>>>>
>>>> I recently made a change to the CSRF prevention filter in
>>>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
>>>> tokens in URLs in case they were being repeatedly run through
>>>> HttpServletResponse.encode().
>>>>
>>>> It's possible there is a bug hiding in there but I'd like some
>>>> feedback.
>>>>
>>>> I'm using Velocity Tools to generate URLs and there is a tool class
>>>> called LinkTool. It generates links with a builder-style interface
>>>> with
>>>> a base URL, parameters, and so on.
>>>>
>>>> After upgrading to 9.0.115, I'm finding that a very small number of
>>>> these links are being broken due to an interaction between LinkTool,
>>>> my
>>>> code, and the CsrfPreventionFilter's massaging of the URLs its
>>>> producing.
>>>>
>>>> The LinkTool is configured to produce HTML5-style URLs where the &
>>>> characters are encoded as &amp;. I did triple-check to confirm that
>>>> this
>>>> was both expected and a reasonable reading of the HTML standard, 
>>>> which
>>>> it appears to be. That is, use of &amp; is actually recommended in
>>>> HTML
>>>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is
>>>> amazing
>>>> enough that I'll go ahead and call it a Law.
>>>>
>>>> Anyway, it looks like the original URL is being produced something
>>>> like
>>>> this:
>>>>
>>>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>>
>>>> So far so good. Note that the URL has already gone through
>>>> response.encodeURL() because it's got a csrf parameter in it.
>>>>
>>>> Then another parameter is added by parsing the URL above (because
>>>> #reasons, this is where my code comes in) and the URL then becomes:
>>>>
>>>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>>>
>>>> I haven't traced through the code, but I believe what's happening is
>>>> this:
>>>>
>>>> 1. My code, through LinkTool, adds the "id" = "4" parameter
>>>> 2. LinkTool re-generates the URL String including an &amp; parameter
>>>> separator
>>>> 3. LinkTool runs the result through response.encodeURL
>>>> 4. CsrfPreventionFilter removes any existing CSRF query parameters; 
>>>> it
>>>> finds one at the beginning of the URL and removes everything up to 
>>>> the
>>>> next & character
>>>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
>>>>
>>>> The URL is now rendered and it's broken. :/
>>>>
>>>> I think plausible arguments could be made that this is a bug in any 
>>>> of
>>>> these 3 products: mine, VelocityTools, or Tomcat.
>>>>
>>>> I'm primarily interested in getting this fixed in *my* product, but 
>>>> I
>>>> wonder how many other products it might effect for similar reasons.
>>>>
>>>> So, the topic here is only Tomcat-related: do we think that the
>>>> CsrfPreventionFilter should consider &amp; to be a request parameter
>>>> separator for the purposes of removing its own duplicates?
>>>>
>>>> My reading of a few specs suggests that looking for "&amp;" should 
>>>> be
>>>> safe since ; is a reserved character and ought to be escaped if the
>>>> user
>>>> actually intends for there to be an honest-to-goodness semicolon in
>>>> their request parameter name or value. Thus, &amp; must mean that a
>>>> literal & was intended.
>>>>
>>>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and
>>>> not
>>>> just "&" in this case.
>>>>
>>>> Opinions?
>>>>
>>>> -chris
>>>>
>>>>
>>>> 
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
>>>> For additional commands, e-mail: dev-help@tomcat.apache.org
>>>>
>>>>
>>> I agree, Tomcat should be fixed to consume  "&amp;", since it 
>>> currently
>>> breaks valid urls.
>>>
>>> Kind regards,
>>> Dimitris
>>
>> I disagree.
>>
>> Something LinkTool ? is confusing HTML encoding and URL encoding.
>>
>> A literal & is encoded as &amp; in HTML (and XML).
>>
>> A literal & is encoded as %26 if it needs to be encoded in a URI.
>>
>> I think you need to get to the bottom of why a URI is being encoded 
>> using
>> HTML encoding and fix that. Once that has been addressed then you can
>> look at the URI and see if any further work is required.
>>
>> Mark
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
>> For additional commands, e-mail: dev-help@tomcat.apache.org
>>
>>
> Still, I believe removeQueryParameters() should be modified to handle 
> "%26"
> and other possible encodings.

%26 shouldn't need any special handling. It should pass through unaltered 
- as should any %nn encoding.

> Although "&amp;" is HTML encoding and seems that LinkTool is 
> misbehaving,
> the result is still a valid URL. Thus, why not to catch that case?

Two reasons.

If the URI is valid Tomcat can't tell if it is actually what the user 
wanted or if it is because LinkTool is being used and LinkTool is 
misbehaving.

Unless there is a very strong case to do so (think major browser vendor 
not following the RFCs and having a long history of ignoring bug reports 
causing breakage that affects lots of Tomcat users) we don't provide 
workarounds for other people's broken code.

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Dimitris Soumis <ds...@apache.org>.
On Wed, Feb 25, 2026 at 11:31 AM Mark Thomas <ma...@apache.org> wrote:

> 25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:
>
> > On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
> > chris@christopherschultz.net> wrote:
> >
> >> All,
> >>
> >> I recently made a change to the CSRF prevention filter in
> >> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
> >> tokens in URLs in case they were being repeatedly run through
> >> HttpServletResponse.encode().
> >>
> >> It's possible there is a bug hiding in there but I'd like some
> >> feedback.
> >>
> >> I'm using Velocity Tools to generate URLs and there is a tool class
> >> called LinkTool. It generates links with a builder-style interface
> >> with
> >> a base URL, parameters, and so on.
> >>
> >> After upgrading to 9.0.115, I'm finding that a very small number of
> >> these links are being broken due to an interaction between LinkTool,
> >> my
> >> code, and the CsrfPreventionFilter's massaging of the URLs its
> >> producing.
> >>
> >> The LinkTool is configured to produce HTML5-style URLs where the &
> >> characters are encoded as &amp;. I did triple-check to confirm that
> >> this
> >> was both expected and a reasonable reading of the HTML standard, which
> >> it appears to be. That is, use of &amp; is actually recommended in
> >> HTML
> >> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is
> >> amazing
> >> enough that I'll go ahead and call it a Law.
> >>
> >> Anyway, it looks like the original URL is being produced something
> >> like
> >> this:
> >>
> >> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> >>
> >> So far so good. Note that the URL has already gone through
> >> response.encodeURL() because it's got a csrf parameter in it.
> >>
> >> Then another parameter is added by parsing the URL above (because
> >> #reasons, this is where my code comes in) and the URL then becomes:
> >>
> >> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
> >>
> >> I haven't traced through the code, but I believe what's happening is
> >> this:
> >>
> >> 1. My code, through LinkTool, adds the "id" = "4" parameter
> >> 2. LinkTool re-generates the URL String including an &amp; parameter
> >> separator
> >> 3. LinkTool runs the result through response.encodeURL
> >> 4. CsrfPreventionFilter removes any existing CSRF query parameters; it
> >> finds one at the beginning of the URL and removes everything up to the
> >> next & character
> >> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
> >>
> >> The URL is now rendered and it's broken. :/
> >>
> >> I think plausible arguments could be made that this is a bug in any of
> >> these 3 products: mine, VelocityTools, or Tomcat.
> >>
> >> I'm primarily interested in getting this fixed in *my* product, but I
> >> wonder how many other products it might effect for similar reasons.
> >>
> >> So, the topic here is only Tomcat-related: do we think that the
> >> CsrfPreventionFilter should consider &amp; to be a request parameter
> >> separator for the purposes of removing its own duplicates?
> >>
> >> My reading of a few specs suggests that looking for "&amp;" should be
> >> safe since ; is a reserved character and ought to be escaped if the
> >> user
> >> actually intends for there to be an honest-to-goodness semicolon in
> >> their request parameter name or value. Thus, &amp; must mean that a
> >> literal & was intended.
> >>
> >> So I think it's safe (and proper) for Tomcat to consume "&amp;" and
> >> not
> >> just "&" in this case.
> >>
> >> Opinions?
> >>
> >> -chris
> >>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> >> For additional commands, e-mail: dev-help@tomcat.apache.org
> >>
> >>
> > I agree, Tomcat should be fixed to consume  "&amp;", since it currently
> > breaks valid urls.
> >
> > Kind regards,
> > Dimitris
>
> I disagree.
>
> Something LinkTool ? is confusing HTML encoding and URL encoding.
>
> A literal & is encoded as &amp; in HTML (and XML).
>
> A literal & is encoded as %26 if it needs to be encoded in a URI.
>
> I think you need to get to the bottom of why a URI is being encoded using
> HTML encoding and fix that. Once that has been addressed then you can
> look at the URI and see if any further work is required.
>
> Mark
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>
Still, I believe removeQueryParameters() should be modified to handle "%26"
and other possible encodings.
Although "&amp;" is HTML encoding and seems that LinkTool is misbehaving,
the result is still a valid URL. Thus, why not to catch that case?

Dimitris

Re: CSRF prevention filter changes - possible bug?

Posted by Mark Thomas <ma...@apache.org>.
25 Feb 2026 07:32:00 Dimitris Soumis <ds...@apache.org>:

> On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
> chris@christopherschultz.net> wrote:
>
>> All,
>>
>> I recently made a change to the CSRF prevention filter in
>> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
>> tokens in URLs in case they were being repeatedly run through
>> HttpServletResponse.encode().
>>
>> It's possible there is a bug hiding in there but I'd like some 
>> feedback.
>>
>> I'm using Velocity Tools to generate URLs and there is a tool class
>> called LinkTool. It generates links with a builder-style interface 
>> with
>> a base URL, parameters, and so on.
>>
>> After upgrading to 9.0.115, I'm finding that a very small number of
>> these links are being broken due to an interaction between LinkTool, 
>> my
>> code, and the CsrfPreventionFilter's massaging of the URLs its 
>> producing.
>>
>> The LinkTool is configured to produce HTML5-style URLs where the &
>> characters are encoded as &amp;. I did triple-check to confirm that 
>> this
>> was both expected and a reasonable reading of the HTML standard, which
>> it appears to be. That is, use of &amp; is actually recommended in 
>> HTML
>> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is 
>> amazing
>> enough that I'll go ahead and call it a Law.
>>
>> Anyway, it looks like the original URL is being produced something 
>> like
>> this:
>>
>> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>
>> So far so good. Note that the URL has already gone through
>> response.encodeURL() because it's got a csrf parameter in it.
>>
>> Then another parameter is added by parsing the URL above (because
>> #reasons, this is where my code comes in) and the URL then becomes:
>>
>> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>>
>> I haven't traced through the code, but I believe what's happening is 
>> this:
>>
>> 1. My code, through LinkTool, adds the "id" = "4" parameter
>> 2. LinkTool re-generates the URL String including an &amp; parameter
>> separator
>> 3. LinkTool runs the result through response.encodeURL
>> 4. CsrfPreventionFilter removes any existing CSRF query parameters; it
>> finds one at the beginning of the URL and removes everything up to the
>> next & character
>> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
>>
>> The URL is now rendered and it's broken. :/
>>
>> I think plausible arguments could be made that this is a bug in any of
>> these 3 products: mine, VelocityTools, or Tomcat.
>>
>> I'm primarily interested in getting this fixed in *my* product, but I
>> wonder how many other products it might effect for similar reasons.
>>
>> So, the topic here is only Tomcat-related: do we think that the
>> CsrfPreventionFilter should consider &amp; to be a request parameter
>> separator for the purposes of removing its own duplicates?
>>
>> My reading of a few specs suggests that looking for "&amp;" should be
>> safe since ; is a reserved character and ought to be escaped if the 
>> user
>> actually intends for there to be an honest-to-goodness semicolon in
>> their request parameter name or value. Thus, &amp; must mean that a
>> literal & was intended.
>>
>> So I think it's safe (and proper) for Tomcat to consume "&amp;" and 
>> not
>> just "&" in this case.
>>
>> Opinions?
>>
>> -chris
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
>> For additional commands, e-mail: dev-help@tomcat.apache.org
>>
>>
> I agree, Tomcat should be fixed to consume  "&amp;", since it currently
> breaks valid urls.
>
> Kind regards,
> Dimitris

I disagree.

Something LinkTool ? is confusing HTML encoding and URL encoding.

A literal & is encoded as &amp; in HTML (and XML).

A literal & is encoded as %26 if it needs to be encoded in a URI.

I think you need to get to the bottom of why a URI is being encoded using 
HTML encoding and fix that. Once that has been addressed then you can 
look at the URI and see if any further work is required.

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: CSRF prevention filter changes - possible bug?

Posted by Dimitris Soumis <ds...@apache.org>.
On Thu, Feb 19, 2026 at 5:56 PM Christopher Schultz <
chris@christopherschultz.net> wrote:

> All,
>
> I recently made a change to the CSRF prevention filter in
> 98187ffef4cec6043a602b500a2e3dfd5ef71c20 that removed duplicate csrf
> tokens in URLs in case they were being repeatedly run through
> HttpServletResponse.encode().
>
> It's possible there is a bug hiding in there but I'd like some feedback.
>
> I'm using Velocity Tools to generate URLs and there is a tool class
> called LinkTool. It generates links with a builder-style interface with
> a base URL, parameters, and so on.
>
> After upgrading to 9.0.115, I'm finding that a very small number of
> these links are being broken due to an interaction between LinkTool, my
> code, and the CsrfPreventionFilter's massaging of the URLs its producing.
>
> The LinkTool is configured to produce HTML5-style URLs where the &
> characters are encoded as &amp;. I did triple-check to confirm that this
> was both expected and a reasonable reading of the HTML standard, which
> it appears to be. That is, use of &amp; is actually recommended in HTML
> pages. RFC 6068 and WHATWG agree on this point whi, frankly, is amazing
> enough that I'll go ahead and call it a Law.
>
> Anyway, it looks like the original URL is being produced something like
> this:
>
> /context/path?csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>
> So far so good. Note that the URL has already gone through
> response.encodeURL() because it's got a csrf parameter in it.
>
> Then another parameter is added by parsing the URL above (because
> #reasons, this is where my code comes in) and the URL then becomes:
>
> /context/path?amp;id=4&csrf=C076D4BC8A49A67BE4EE7517C3A0129D
>
> I haven't traced through the code, but I believe what's happening is this:
>
> 1. My code, through LinkTool, adds the "id" = "4" parameter
> 2. LinkTool re-generates the URL String including an &amp; parameter
> separator
> 3. LinkTool runs the result through response.encodeURL
> 4. CsrfPreventionFilter removes any existing CSRF query parameters; it
> finds one at the beginning of the URL and removes everything up to the
> next & character
> 5. CsrfPreventionFilter adds its csrf token to the end of the URL
>
> The URL is now rendered and it's broken. :/
>
> I think plausible arguments could be made that this is a bug in any of
> these 3 products: mine, VelocityTools, or Tomcat.
>
> I'm primarily interested in getting this fixed in *my* product, but I
> wonder how many other products it might effect for similar reasons.
>
> So, the topic here is only Tomcat-related: do we think that the
> CsrfPreventionFilter should consider &amp; to be a request parameter
> separator for the purposes of removing its own duplicates?
>
> My reading of a few specs suggests that looking for "&amp;" should be
> safe since ; is a reserved character and ought to be escaped if the user
> actually intends for there to be an honest-to-goodness semicolon in
> their request parameter name or value. Thus, &amp; must mean that a
> literal & was intended.
>
> So I think it's safe (and proper) for Tomcat to consume "&amp;" and not
> just "&" in this case.
>
> Opinions?
>
> -chris
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>
I agree, Tomcat should be fixed to consume  "&amp;", since it currently
breaks valid urls.

Kind regards,
Dimitris