CVE-2021-43846 (Ruby, published 2021-12-20T22:15Z)
Description: value: `solidus_frontend` is the cart and storefront for the Solidus e-commerce project. Versions of `solidus_frontend` prior to 3.1.5, 3.0.5, and 2.11.14 contain a cross-site request forgery (CSRF) vulnerability that allows a malicious site to add an item to the user's cart without their knowledge. Versions 3.1.5, 3.0.5, and 2.11.14 contain a patch for this issue. The patch adds CSRF token verification to the "Add to cart" action. Adding forgery protection to a form that missed it can have some side effects. Other CSRF protection strategies as well as a workaround involving modifcation to config/application.rb` are available. More details on these mitigations are available in the GitHub Security Advisory.
Severity: MEDIUM (CVSS v3 base score: 4.3)
Weakness: CWE-352 \u2014 Cross-Site Request Forgery (CSRF)
The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
Repository: https://github.com/solidusio/solidus
Files changed:
frontend/app/controllers/spree/orders_controller.rb
Commit message: Protect `Spree::OrdersController#populate` against CSRF attacks

See
https://github.com/solidusio/solidus/security/advisories/GHSA-h3fg-h5v3-vf8m
for all the details.

Some time ago, all order actions were left out of CSRF protection (see
95ea57058ab1c5e722b327b10747cd41e68a4deb). The reason given was that the
authentication token got stale after the second rendering because the
product page is cached. That was limited to `#populate` in
cb797542c6948ef33d2cc9e6076c88f4cc927fb2 (see also
https://github.com/spree/spree/pull/5601).

However, those assumptions are not correct. Although the authenticity
token changes at every request, that doesn't mean that the old ones are
no longer valid. The variation comes from a one-time pad added to a
session-dependant token (and meant to avoid timing attacks). However,
before validation, that one-time pad is removed. That means the token
remains valid as long as the session has not been reset. Think about
submitting a form from one browser tab after opening another with the
same URL. Even if both tokens differ, the submission from the first tab
will still be valid. You can read
https://medium.com/rubyinside/a-deep-dive-into-csrf-protection-in-rails-19fa0a42c0ef
for an in-deep understanding.

The initial confusion could come because of
https://github.com/rails/rails/issues/21948. Due to browser-side cache,
a form can be re-rendered and sent without any attached request cookie.
That will cause an authentication error, as the sent token won't match
with the one in the session (none in this case). There's no perfect
solution for that, and all partial fixes should be seen at the
application level. From our side, we must provide a safe default. For an
excellent survey of all the available options, take a look at
https://github.com/betagouv/demarches-simplifiees.fr/blob/5b4f7f9ae9eaf0ac94008b62f7047e4714626cf9/doc/adr-csrf-forgery.md.
The information given in that link is third-party but it's very
relevant here. For that reason we've copied it in the security advisory
(see link above), but all the credit goes to @kemenaran.


Vulnerable code:
    skip_before_action :verify_authenticity_token, only: [:populate]

Fixed code: