Article rating AJAX never sends the CSRF token, so `articlerating()` rejects every click with "Invalid Token" - Forum | JoomShaper
Staff replied Helix Framework

Article rating AJAX never sends the CSRF token, so `articlerating()` rejects every click with "Invalid Token"

Asked by Goran 2 weeks ago Last activity 2 weeks ago

The article rating widget cannot record a vote. Every click is answered with "Invalid Token", so the star rating
feature is inert on a default install with article ratings enabled.

templates/shaper_helixultimate/js/main.js:301-334 builds the AJAX payload:

$('.article-ratings .rating-star').on('click', function (event) {
    event.preventDefault();
    var $parent = $(this).closest('.article-ratings');

    var request = {
        option: 'com_ajax',
        template: template,
        action: 'rating',
        rating: $(this).data('number'),
        article_id: $parent.data('id'),
        format: 'json',
    };

    $.ajax({ type: 'POST', data: request, ... });
});

The payload carries no CSRF token, but the endpoint it reaches requires one. templates/shaper_helixultimate/helper.php:27:

Session::checkToken() or die(json_encode($output));

Session::checkToken() (libraries/src/Session/Session.php:64-88) looks for the token in the X-CSRF-Token header or in
the POST body; neither is present, so it returns false and the handler dies with {"status":false,"message":"Invalid Token"}.

Reproduced from a normal browser session on an article page, same session for both requests:

POST option=com_ajax&template=shaper_helixultimate&action=rating&rating=5&article_id=45&format=json
  -> {"status":false,"message":"Invalid Token"}

POST option=com_ajax&template=shaper_helixultimate&action=rating&rating=5&article_id=45&format=json&<token>=1
  -> {"status":true,"message":"Thanks for your rating.","rating_count":1,"ratings":"<span class=\"rating-star\" ..."}

The only difference between the two is the token field, and Joomla already publishes it on every page in
joomla-script-options under csrf.token, so the fix is three lines in the same handler:

var csrfToken = window.Joomla && Joomla.getOptions ? Joomla.getOptions('csrf.token', '') : '';
if (csrfToken) {
    request[csrfToken] = 1;
}

Two smaller things in the same endpoint, which only become reachable once the token is sent:

  1. helper.php:32 takes rating as a plain (int) and helper.php:86 adds it straight into rating_sum with no
    range check. A request with rating=99999 inflates the stored average for that article. Clamping to 1..5 is enough.

  2. helper.php:31 takes article_id as a plain (int) and helper.php:94 inserts a #__content_rating row without
    checking that the article exists or is published, so rows can be created for ids that are not articles.

    Thank you.

1 reply

Ziaul Kabir Staff

Hi Goran,

Thank you for the detailed report and for providing the reproduction steps and suggested fixes.

We have forwarded this issue and your additional findings to our development team for review. They will investigate the CSRF token issue along with the rating validation and article ID checks you mentioned.

We appreciate the thorough technical investigation and the clear explanation.

Thanks!

Log in to reply.