@yung96

Как проверять подлинность данных, отправляемых клиентом в базу данных?

У меня есть веб приложение, которое представляет из себя небольшую игрушку по типу Blum. Т. к. я являюсь новичком в этой сфере, все расчёты, связанные с важными переменными происходят со стороны клиента.
Пример:
startGameButton.addEventListener("click", () => {
        if (tokens !== 0) {
            tokens -= 1;
            updateUserData();
            scoreElement.textContent = `Ð ${score}`;
            startMenu.style.display = "none";
            gameContainer.classList.remove("hidden");
            totalScoreDisplay.classList.add("hidden");
            highlightRandomDots();
            clearTimeout(gameTimer);
            clearInterval(countdownTimer);
            startCountdown(30);
        } else {
            tokensDis.classList.add("red");
            setTimeout(() => {
                tokensDis.classList.remove("red");
            }, 500);
        }
    });


В этом куске кода критическая переменная это tokens. При нажатии на кнопку start со стороны клиента у пользователя отнимается один токен и кол-во общих токенов отправляется в бд с помощью updateUserData(), данные считываются с помощью функции fetchUserData();
function fetchUserData() {
        if (isUpdating) return;
    
        fetch(`../getUserData.php?username=${username}&inviter_id=${inviter_id}`)
            .then(response => response.json())
            .then(data => {
                tokens = data.tokens;
                coins = data.coins;
                claimed1 = data.claimed1;
                claimed2 = data.claimed2;
                farming_final_time = String(data.farming_final_time);
                invites = data.invites;
                tokensDis.textContent = `️ ${tokens}`;
                totalScoreDisplay.textContent = `Ð ${coins}`;
    
                if (claimed1) {
                    document.getElementById('claim').classList.add("claimed");
                    document.getElementById('claim').disabled = true;
                }
    
                checkFarming();
                checkInvites();
            })
            .catch(error => console.error('Error:', error));
    }
    

    function updateUserData() {
        isUpdating = true;
        if (invites != 0) {
            document.getElementById('splash-screen').style.display = 'none';
            tokens = parseInt(tokens, 10) + 20*invites;
            invites = 0;
        }
        const data = {
            username: username,
            tokens: tokens,
            coins: coins,
            claimed1: claimed1,
            claimed2: claimed2,
            farming_final_time: farming_final_time,
            invites: invites,
        };

        fetch('../updateUserData.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                console.log('User data updated successfully');
            } else {
                console.error('Error updating user data:', data.message);
            }
        })
        .catch(error => console.error('Error:', error));
        isUpdating = false;
    }


Вот серверный код
<?php
include 'config.php';

// Получение данных из входного JSON
$data = json_decode(file_get_contents('php://input'), true);

// Проверка, что все ключи существуют в данных
if (!isset($data['username'], $data['tokens'], $data['coins'], $data['claimed1'], $data['claimed2'], $data['farming_final_time'], $data['invites'])) {
    echo json_encode(["status" => "error", "message" => "Incomplete data"]);
    exit();
}

$username = $data['username'];
$tokens = $data['tokens'];
$coins = $data['coins'];
$claimed1 = $data['claimed1'];
$claimed2 = $data['claimed2'];
$farming_final_time = $data['farming_final_time'];
$invites = $data['invites'];

// Обновление данных пользователя
$sql = "UPDATE users SET tokens = ?, coins = ?, claimed1 = ?, claimed2 = ?, farming_final_time = ?, invites = ? WHERE username = ?";
$stmt = $conn->prepare($sql);

if (!$stmt) {
    echo json_encode(["status" => "error", "message" => $conn->error]);
    exit();
}

$stmt->bind_param("iiissss", $tokens, $coins, $claimed1, $claimed2, $farming_final_time, $invites, $username);

if ($stmt->execute()) {
    echo json_encode(["status" => "success"]);
} else {
    echo json_encode(["status" => "error", "message" => $stmt->error]);
}

$stmt->close();
$conn->close();
?>


В ходе изучения темы я понял, что это очень небезопасный метод, пользователь может отправлять в базу данных любые данные, которые он захочет (накручивать токены и тд) с помощью консоли браузера, путём изменения переменной на стороне клиента и отправкой в базу с помощью php скрипта. Такой вопрос, как мне максимально обезопасить эту процедуру? Чтобы если пользователь пытался накрутить токены, сервер не давал отправить эти данные в базу данных.
  • Вопрос задан
  • 1489 просмотров
Решения вопроса 1
ThunderCat
@ThunderCat Куратор тега Веб-разработка
{PHP, MySql, HTML, JS, CSS} developer
В этом куске кода критическая переменная это tokens. При нажатии на кнопку start со стороны клиента у пользователя отнимается один токен и кол-во общих токенов отправляется в бд
Не надо тупить. Раз токен это критичные данные, то никаких "со стороны клиента" быть не должно. Нажат старт - на сервер отправилось "старт пошел", из данных в бд вычитается/прибавляется значение, обратно отсылается что в итоге получилось. С остальным так же - на сервер отправляется событие, а сервер считает чего куда прибавлять и возвращает результат на фронт.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
все расчёты, связанные с важными переменными происходят со стороны клиента

Все расчеты связанные с важными переменными должны происходить на стороне сервера
668c1b7a7dc2f372259442.png
Ответ написан
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Способ только один - все действия делаются исключительно на сервере. От клиента только приходят команды на операции с токенами.
Ответ написан
kellas
@kellas
веб-разработчик
Введи лимит, например максимум 1 токен в секунду.

Записывай в БД timestamp типа updatedAt, время когда последний раз было изменено количество токенов. (по-умолчанию это время создания аккаунта)

Когда пользователь пришлет с клиента новое число, вытаскивай дату предыдущей записи, считай разницу во времени, смотри сколько токенов за это прошедшее время максимум можно получить с учётом лимита и если число клиента больше - прост заменяй его не максимально допустимое значение.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы