Using CsrfPreventionFilter with GET-based <form> submissions

Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Using CsrfPreventionFilter with GET-based <form> submissions

Christopher Schultz-2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

All,

I'm playing with the CsrfPreventionFilter and things are working well
in the following situations:

<a href="url">link text</a>

and

<form method="post" action="url">
...
</form>

As long as the URL has been passed through request.encodeURL().

However, this one is causing me a problem:

<form method="GET" action="url">
...
</form>

This builds a form like this:

<form method="GET"
action="https://host/path?org.apache.catalina.filters.CSRF_NONCE=[...]">
...
</form>

Neither Firefox nor Chrome will send the query string present in a
<form> action attribute if the method="GET". The method must be "POST"
in order for this to be sent. This is due to the HTML standard[1].

Short of changing all <form> methods to "POST", is there any way
around this?

I have read the code for CsrfPreventionFilter and it does not appear
that the nonce if stored anywhere except in the CsrfResponseWrapper
for the request (and the session's nonce cache, but that isn't
request-specific).

Would it be inappropriate to add the CSRF_NONCE to the request
attributes so that application code could use it directly if
necessary? Something like this:

<form method="get" action="url">
  ...
  <input type="hidden" name="org.apache.catalina.filters.CSRF_NONCE"
value="<%= request.getAttribute("CSRF_NONCE") %>" />
</form>

- -chris

[1] https://www.w3.org/TR/html401/interact/forms.html#h-17.13.3.4
-----BEGIN PGP SIGNATURE-----
Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/

iQIzBAEBCAAdFiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAl3FuqUACgkQHPApP6U8
pFiRNg/+IIcX8T9/gdui3oGLn3oTWcL2wufs5XN8FUsyYkm9R0Pgj2tzfyHVykF9
Lqr+jYw6wBmNAo/j319+Wcv7YfN/JHSTKOITvPuquQST4pXYOfYVl4SRBXuqJ7bs
gI2hTcyH2eUGSk6mSfjD+F4RQ2uigKQgnTXp1XTmFgEW5An/LPxY6o6ruEJ3RbSW
ceaO9hR4NSBbtB2urT6JsKPAiuZvOy9qELRBoVc54vNLoTqPe2oNUx4AHnq2cRuE
eKhegWlyj+XYVcVDEK0SK1irmgiN6YVc6Cxyy0QD+pEf0SvPwXeRtvS+3Ucjfpnv
nQSZDUbia/lXNktMnCiSl3c/ZEfo2AS9br/dlHbWCu5y8ugngaIHrbFPTU5QLNEP
0mFjvMYCm4QIqu79/qOyPzDReNpWBuqsLNXfJLbhBG6MuCWLhSzHOLQnmoXb2hmg
60vX9/B1/AgZkOv5Uv2EL/AqvyMLH9SnxuR7RVSf4FFoGD8PLpxCGruskb5HoYAr
IVyLxhzvvbE/ViXXGlwXcfuwaS1EgOXhWZqM+rl8wT1MhHnYd/SX5uGRHqjd43gO
fuOphdHNC+G5ErCyYqy4urvxyP9vuhipU43O1eUDQV+rRAdI6m+q26gTgA8U+D7i
LgJ0ZYGj+pzWi7SHyBoKIcA8u1vJrZqBFC6Fa9jlpHgQ/A/1Rtg=
=Ehsd
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: Using CsrfPreventionFilter with GET-based <form> submissions

Peter Kreuser
Chris,

>
> Am 09.11.2019 um 03:58 schrieb Christopher Schultz <[hidden email]>:
>
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> All,
>
> I'm playing with the CsrfPreventionFilter and things are working well
> in the following situations:
>
> <a href="url">link text</a>
>
> and
>
> <form method="post" action="url">
> ...
> </form>
>
> As long as the URL has been passed through request.encodeURL().
>
> However, this one is causing me a problem:
>
> <form method="GET" action="url">
> ...
> </form>
>
> This builds a form like this:
>
> <form method="GET"
> action="https://host/path?org.apache.catalina.filters.CSRF_NONCE=[...]">
> ...
> </form>
>
> Neither Firefox nor Chrome will send the query string present in a
> <form> action attribute if the method="GET". The method must be "POST"
> in order for this to be sent. This is due to the HTML standard[1].
>
> Short of changing all <form> methods to "POST", is there any way
> around this?
>
> I have read the code for CsrfPreventionFilter and it does not appear
> that the nonce if stored anywhere except in the CsrfResponseWrapper
> for the request (and the session's nonce cache, but that isn't
> request-specific).
>
> Would it be inappropriate to add the CSRF_NONCE to the request
> attributes so that application code could use it directly if
> necessary? Something like this:
>
> <form method="get" action="url">
> ...
> <input type="hidden" name="org.apache.catalina.filters.CSRF_NONCE"
> value="<%= request.getAttribute("CSRF_NONCE") %>" />
> </form>

If i remember correctly, this is the way struts handles CSRF Tokens. However there the nonce comes directly from the session . Not request.

Peter

> - -chris
>
> [1] https://www.w3.org/TR/html401/interact/forms.html#h-17.13.3.4
> -----BEGIN PGP SIGNATURE-----
> Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/
>
> iQIzBAEBCAAdFiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAl3FuqUACgkQHPApP6U8
> pFiRNg/+IIcX8T9/gdui3oGLn3oTWcL2wufs5XN8FUsyYkm9R0Pgj2tzfyHVykF9
> Lqr+jYw6wBmNAo/j319+Wcv7YfN/JHSTKOITvPuquQST4pXYOfYVl4SRBXuqJ7bs
> gI2hTcyH2eUGSk6mSfjD+F4RQ2uigKQgnTXp1XTmFgEW5An/LPxY6o6ruEJ3RbSW
> ceaO9hR4NSBbtB2urT6JsKPAiuZvOy9qELRBoVc54vNLoTqPe2oNUx4AHnq2cRuE
> eKhegWlyj+XYVcVDEK0SK1irmgiN6YVc6Cxyy0QD+pEf0SvPwXeRtvS+3Ucjfpnv
> nQSZDUbia/lXNktMnCiSl3c/ZEfo2AS9br/dlHbWCu5y8ugngaIHrbFPTU5QLNEP
> 0mFjvMYCm4QIqu79/qOyPzDReNpWBuqsLNXfJLbhBG6MuCWLhSzHOLQnmoXb2hmg
> 60vX9/B1/AgZkOv5Uv2EL/AqvyMLH9SnxuR7RVSf4FFoGD8PLpxCGruskb5HoYAr
> IVyLxhzvvbE/ViXXGlwXcfuwaS1EgOXhWZqM+rl8wT1MhHnYd/SX5uGRHqjd43gO
> fuOphdHNC+G5ErCyYqy4urvxyP9vuhipU43O1eUDQV+rRAdI6m+q26gTgA8U+D7i
> LgJ0ZYGj+pzWi7SHyBoKIcA8u1vJrZqBFC6Fa9jlpHgQ/A/1Rtg=
> =Ehsd
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: Using CsrfPreventionFilter with GET-based <form> submissions

markt
In reply to this post by Christopher Schultz-2
> All,
>
> I'm playing with the CsrfPreventionFilter and things are working well
> in the following situations:
>
> <a href="url">link text</a>
>
> and
>
> <form method="post" action="url">
> ...
> </form>
>
> As long as the URL has been passed through request.encodeURL().
>
> However, this one is causing me a problem:
>
> <form method="GET" action="url">
> ...
> </form>
>
> This builds a form like this:
>
> <form method="GET"
> action="https://host/path?org.apache.catalina.filters.CSRF_NONCE=[...]">
> ...
> </form>
>
> Neither Firefox nor Chrome will send the query string present in a
> <form> action attribute if the method="GET". The method must be "POST"
> in order for this to be sent. This is due to the HTML standard[1].

The spec assumes that the action attribute doesn't already have a query
string. A strict reading of that text would lead to a request of:

GET /path?<nonce>?<form-attributes>

which is clearly wrong. Is there an actual (security?) problem the
browsers are trying to solve here or is this just a case of how they
have decided to interpret the HTML standard?

> Short of changing all <form> methods to "POST", is there any way
> around this?
>
> I have read the code for CsrfPreventionFilter and it does not appear
> that the nonce if stored anywhere except in the CsrfResponseWrapper
> for the request (and the session's nonce cache, but that isn't
> request-specific).
>
> Would it be inappropriate to add the CSRF_NONCE to the request
> attributes so that application code could use it directly if
> necessary? Something like this:
>
> <form method="get" action="url">
>   ...
>   <input type="hidden" name="org.apache.catalina.filters.CSRF_NONCE"
> value="<%= request.getAttribute("CSRF_NONCE") %>" />
> </form>

I can't see any reason why not.

You could also add a "getYougest()" method the the nonce cache and then
look that up via the session since the nonce doesn't have to the one
generated for the current request.

Your approach looks cleaner though - and less dependent on the
implementation of the CsrfPreventionFilter.

Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: Using CsrfPreventionFilter with GET-based <form> submissions

Christopher Schultz-2
In reply to this post by Peter Kreuser
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Peter,

On 11/10/19 19:05, Peter Kreuser wrote:

> Chris,
>
>>
>> Am 09.11.2019 um 03:58 schrieb Christopher Schultz
>> <[hidden email]>:
>>
>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
>>
>> All,
>>
>> I'm playing with the CsrfPreventionFilter and things are working
>> well in the following situations:
>>
>> <a href="url">link text</a>
>>
>> and
>>
>> <form method="post" action="url"> ... </form>
>>
>> As long as the URL has been passed through request.encodeURL().
>>
>> However, this one is causing me a problem:
>>
>> <form method="GET" action="url"> ... </form>
>>
>> This builds a form like this:
>>
>> <form method="GET"
>> action="https://host/path?org.apache.catalina.filters.CSRF_NONCE=[...
]">
>>
>>
...

>> </form>
>>
>> Neither Firefox nor Chrome will send the query string present in
>> a <form> action attribute if the method="GET". The method must be
>> "POST" in order for this to be sent. This is due to the HTML
>> standard[1].
>>
>> Short of changing all <form> methods to "POST", is there any way
>> around this?
>>
>> I have read the code for CsrfPreventionFilter and it does not
>> appear that the nonce if stored anywhere except in the
>> CsrfResponseWrapper for the request (and the session's nonce
>> cache, but that isn't request-specific).
>>
>> Would it be inappropriate to add the CSRF_NONCE to the request
>> attributes so that application code could use it directly if
>> necessary? Something like this:
>>
>> <form method="get" action="url"> ... <input type="hidden"
>> name="org.apache.catalina.filters.CSRF_NONCE" value="<%=
>> request.getAttribute("CSRF_NONCE") %>" /> </form>
>
> If i remember correctly, this is the way struts handles CSRF
> Tokens.

I'm not sure what Struts has to do with this. I'm using Tomcat's CSRF
filter which apparently cannot work with GET-based forms. I'm not
saying that a GET-based form is a good idea, but we have a bunch of
them so I'm looking into how they can be effectively used with this
implementation of a CSRF filter.

I'm really surprised this hasn't come up, yet. Maybe nobody actually
implements CSRF protection, or maybe nobody uses Tomcat's filter to do
it, or maybe nobody uses GET-based HTML <form>s. But I can't believe
that I'm the only person in the world who is trying to use all three
at once.

> However there the nonce comes directly from the session . Not
> request.

The nonces are stored in the session, otherwise this wouldn't work.
But each request generates a new nonce, and that one would be the
"request's nonce".

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/

iQIzBAEBCAAdFiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAl3K7UkACgkQHPApP6U8
pFhZBRAAkrD4pr+agtuxblW/UA8ylVm6pceqTmlz8ki39I7k7T5fjgm+Yg1mjG9R
VgQNI/v0y94rQ3cCjIerUxTTNHTzgHUi2uRk9sSsnTsRXC4W+8wdRSojDSq9j1AB
z4ZQI3t5Z8+e9RWBDrYOd2TkvW93aGiMzOpcnashvJkhUSDR5102RJtvoz1yAK7r
Rwzv8feSJRy3/rxjiqQvHYdBH9DeRXVGH0CTP6KZ/+e/icFw0nnH3e+Jrh3+k5du
fIUi/97JPT0hxkkJbNkKsOJ3P/D4kqzKnbo6WqFr4UwYMCiJizNNTuu+3pG6LAjn
qTow+EL6/sG3Dtt/VCexyhC7jXdGjcrMDpxcXZx6NwiFxppK2kGWVMi7zKz4qm3X
ZLR1zSsfzRhUnVPmdjYUAtDonhCbWW+FdQmBtGhGhH7+3wOeXrGpBeWcAq7jjGoD
rgKQJMKUEN0PMH8j63tgp86Te6zhJCG23/ttBqFTLvP6XWbfHn6KoMFrwSMz8Spz
EyFDBJihspuFncOMjEJ5kPLmJzs2x811VVkMOBA3BPqIrN2qteTIja9+t3ismo4+
iBf0sV0q74HL24wvyAfONArae52vFmg4wJLiN38tztNLdoblJpTjqRs04gQ+Q0+5
u4KKouD6Oz37Zo1Z7IS/HezmfGwYRrZn96CgPzfam4ZQp4Hldi8=
=mYsp
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: Using CsrfPreventionFilter with GET-based <form> submissions

Peter Kreuser


Chris,

> Am 13.11.2019 um 02:35 schrieb Christopher Schultz <[hidden email]>:
>
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> Peter,
>
>> On 11/10/19 19:05, Peter Kreuser wrote:
>> Chris,
>>
>>>
>>> Am 09.11.2019 um 03:58 schrieb Christopher Schultz
>>> <[hidden email]>:
>>>
>>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
>>>
>>> All,
>>>
>>> I'm playing with the CsrfPreventionFilter and things are working
>>> well in the following situations:
>>>
>>> <a href="url">link text</a>
>>>
>>> and
>>>
>>> <form method="post" action="url"> ... </form>
>>>
>>> As long as the URL has been passed through request.encodeURL().
>>>
>>> However, this one is causing me a problem:
>>>
>>> <form method="GET" action="url"> ... </form>
>>>
>>> This builds a form like this:
>>>
>>> <form method="GET"
>>> action="https://host/path?org.apache.catalina.filters.CSRF_NONCE=[...
> ]">
>>>
>>>
> ...
>>> </form>
>>>
>>> Neither Firefox nor Chrome will send the query string present in
>>> a <form> action attribute if the method="GET". The method must be
>>> "POST" in order for this to be sent. This is due to the HTML
>>> standard[1].
>>>
>>> Short of changing all <form> methods to "POST", is there any way
>>> around this?
>>>
>>> I have read the code for CsrfPreventionFilter and it does not
>>> appear that the nonce if stored anywhere except in the
>>> CsrfResponseWrapper for the request (and the session's nonce
>>> cache, but that isn't request-specific).
>>>
>>> Would it be inappropriate to add the CSRF_NONCE to the request
>>> attributes so that application code could use it directly if
>>> necessary? Something like this:
>>>
>>> <form method="get" action="url"> ... <input type="hidden"
>>> name="org.apache.catalina.filters.CSRF_NONCE" value="<%=
>>> request.getAttribute("CSRF_NONCE") %>" /> </form>
>>
>> If i remember correctly, this is the way struts handles CSRF
>> Tokens.
>
> I'm not sure what Struts has to do with this. I'm using Tomcat's CSRF
> filter which apparently cannot work with GET-based forms. I'm not
> saying that a GET-based form is a good idea, but we have a bunch of
> them so I'm looking into how they can be effectively used with this
> implementation of a CSRF filter.

I just wanted to point out that struts implements the CSRF protection only with a hidden field. That’s a bit of a hassle, plus you have to handle this per form. Wether it is a POST or a GET.

> I'm really surprised this hasn't come up, yet. Maybe nobody actually
> implements CSRF protection, or maybe nobody uses Tomcat's filter to do
> it, or maybe nobody uses GET-based HTML <form>s. But I can't believe
> that I'm the only person in the world who is trying to use all three
> at once.
>
>> However there the nonce comes directly from the session . Not
>> request.
>
> The nonces are stored in the session, otherwise this wouldn't work.
> But each request generates a new nonce, and that one would be the
> "request's nonce".

That’s another difference to struts and a bit of a nuisance, the nonce is only created once (and there is no cache). Once you remove it or generate a new nonce, all requests from “old” forms Will fail. But that is another story.

But nevertheless the GET problem would be interesting to figure out. I will try this once I’m back from vacation.

Peter

> - -chris
> -----BEGIN PGP SIGNATURE-----
> Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/
>
> iQIzBAEBCAAdFiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAl3K7UkACgkQHPApP6U8
> pFhZBRAAkrD4pr+agtuxblW/UA8ylVm6pceqTmlz8ki39I7k7T5fjgm+Yg1mjG9R
> VgQNI/v0y94rQ3cCjIerUxTTNHTzgHUi2uRk9sSsnTsRXC4W+8wdRSojDSq9j1AB
> z4ZQI3t5Z8+e9RWBDrYOd2TkvW93aGiMzOpcnashvJkhUSDR5102RJtvoz1yAK7r
> Rwzv8feSJRy3/rxjiqQvHYdBH9DeRXVGH0CTP6KZ/+e/icFw0nnH3e+Jrh3+k5du
> fIUi/97JPT0hxkkJbNkKsOJ3P/D4kqzKnbo6WqFr4UwYMCiJizNNTuu+3pG6LAjn
> qTow+EL6/sG3Dtt/VCexyhC7jXdGjcrMDpxcXZx6NwiFxppK2kGWVMi7zKz4qm3X
> ZLR1zSsfzRhUnVPmdjYUAtDonhCbWW+FdQmBtGhGhH7+3wOeXrGpBeWcAq7jjGoD
> rgKQJMKUEN0PMH8j63tgp86Te6zhJCG23/ttBqFTLvP6XWbfHn6KoMFrwSMz8Spz
> EyFDBJihspuFncOMjEJ5kPLmJzs2x811VVkMOBA3BPqIrN2qteTIja9+t3ismo4+
> iBf0sV0q74HL24wvyAfONArae52vFmg4wJLiN38tztNLdoblJpTjqRs04gQ+Q0+5
> u4KKouD6Oz37Zo1Z7IS/HezmfGwYRrZn96CgPzfam4ZQp4Hldi8=
> =mYsp
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]