Какая лучшая практика проверки достоверности запроса через ajax?

Вот у меня пользователи могут подписываться на пост определенный и для этого есть кнопка "подписаться".

Код во вьюхе такой:
<div id="subscribe" class="btn_subscribe" data-user-id="<?= $user->id ?>"
         data-post-id="<?= $modelPost->id ?>"
         data-user-subscribe="0">
    </div>


Соответственно скрипт отправляет id пользователя и id поста, на который он хочет подписаться.

Код запроса:
$('#subscribe').on('click', function (e) {

    var el = document.querySelector('#subscribe');
    var dataToServer = '';
    dataToServer += 'userID=';
    dataToServer += el.dataset.userId;
    dataToServer += '&postID=';
    dataToServer += el.dataset.postId;

    $.ajax({

        async: true,
        cache: false,
        type: "POST",
        url: url,
        data: dataToServer,
        dataType: "html",
        ifModified: true,
        timeout: 10000,

        dataFilter: function (data, type) {

            var answer = JSON.parse(data);
            console.log(answer);
        }

    });

});


Код экшена упрощенный такой:
public function actionChangeEvent()
    {

        if (Yii::$app->request->isAjax && isset($_POST['userID']) && isset($_POST['postID'])) {

            if ($_POST['userID'] == 0) {
                return json_encode(['no user', 0]);
            }

            $user = User::findOne($_POST['userID']);
            $post = Post::findOne($_POST['postID']);

            if (isset($user) && isset($post)) {

                $alreadySubscribe = Subscribe::find()
                    ->where(['userID' => $user->id])
                    ->andWhere(['postID' => $post->id])
                    ->one();

                if (isset($alreadySubscribe)) {

                    // пользователь хочет отписаться
                    $alreadySubscribe->delete();
                    return json_encode(['removed success', Subscribe::find()->where(['postID' => $post->id])->count()]);

                } else {

                    // пользователь хочет подписаться
                    $subscribe = new Subscribe();
                    $subscribe->createdAT = date("Y-m-d H:i:s");
                    $subscribe->userID = $user->id;
                    $subscribe->postID = $post->id;
                    $subscribe->save();

                    return json_encode(['added success', Subscribe::find()->where(['postID' => $post->id])->count()]);
                }


            }

        } else {

            echo 'ошибка';

        }
    }


Так вот, текущая реализация полностью устраивает кроме безопасности - можно легко подменить идшники пользователя или поста и накрутить подписки. Для этого очевидно нужно как-то удостовериться что запрос идет именно от авторизованного пользователя и именно свой айдишник он отправляет в запросе, а не чужой. Идеи есть, но уверен что есть какие-то лучшие практики..

Да, csrf валидация мне по-прежнему не понятно как работает и можно ли ее использовать в данном контексте для решения этой задачи.. Все варианты интересны, заранее благодарен за ответы.
  • Вопрос задан
  • 100 просмотров
Решения вопроса 1
Denormalization
@Denormalization
1) Нужно убрать отправку user id через Ajax. Это вообще ни к чему.
2) user id нужно брать у текущего авторизованного юзера (В Yii что-то вроде Yii::$app->user->id)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
@davidnum95
$_POST['userID']
Не лучшая идея доставать параметры напрямую из $_POST, для этого есть Yii::$app->request->getBodyParams().
Если у вас стандартная реализация авторизации, то для авторизованных пользователей в Yii::$app->user->id будет id текущего пользователя.
Ответ написан
Комментировать
slo_nik
@slo_nik Куратор тега Yii
Добрый день.
Про обращение напрямую к глобальному массиву и получение id пользователя уже написали не раз.
А проверку csrf токена можно сделать так:
в представлении
$this->registerJs('
   $("#subscribe").on("click", function(e){
        $.ajax({
              method: "POST",
              data: {id: $(this).attr("data-post-id"),_csrf: "' . Yii::$app->request->csrfToken . '"},
              success: function(data){
                console.log(data)
              }
            })
    })
', View::POS_END)

в контроллере
if(Yii::$app->request->isAjax){
    if(Yii::$app->request->validateCsrfToken()){
       // продолжаем выполнение кода.
    }
    else{
        return 'Error Csrf Token';
    }
}


p.s.
Думаю, что строки подобные этой $user = User::findOne($_POST['userID']); необходимо заменить на
строку
$user = User::findOne(['id' => Yii::$app->user->identity->id]);
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы