Есть ли способ запретить пользователям редактировать сеанс локального хранилища?

Я создаю реляционный блог, где я использую ember_simple_auth:session хранить сессию как

{"authenticated":{"authenticator":"authenticator:devise","token":"rh2f9iy7EjJXESAM5koQ","email":"user@example.com","userId":1}}

Однако в инструментах разработчика в Chrome (и, возможно, в других браузерах) довольно легко отредактировать электронную почту и идентификатор пользователя, чтобы выдать себя за другого пользователя при перезагрузке страницы.

РЕДАКТИРОВАТЬ #1

Из разговора с Иоахимом и Николаем я понял, что лучший способ решить эту проблему - проверять подлинность localStorage каждый раз, когда мне это нужно (что происходит только при перезагрузке страницы), а не пытаться предотвратить изменения.

Чтобы подтвердить подлинность, я создаю обещание, которое должно быть решено до использования AccountSession. Обещание serverValidation() запрашивает создание модели токена с текущей информацией localStorage, и когда сервер получает ее, он проверяет информацию и отвечает 200 простой сериализацией пользователя с типом в качестве токена, если информация является достоверной. Вы можете проверить больше информации об исходном коде.

Учетная запись сеанса

import Ember from 'ember';

const { inject: { service }, RSVP } = Ember;
export default Ember.Service.extend ({
    session: service('session'),
    store: service(),
    serverValidation: false,

    // Create a Promise to handle a server request that validates the current LocalStorage
    // If valid, then set SessionAccount User.
    loadCurrentUser() {
        if (!Ember.isEmpty(this.get('session.data.authenticated.userId'))) {
            this.serverValidation().then(() => {
                return new RSVP.Promise((resolve, reject) => {
                    const userId = this.get('session.data.authenticated.userId');
                        // Get User to Session-Account Block
                        if(this.get('serverValidation') === true) {
                            return this.get('store').find('user', userId).then((user) => {
                                this.set('user', user);
                                resolve();
                            }).catch((reason) => {
                                console.log(reason.errors);
                                var possible404 = reason.errors.filterBy('status','404');
                                var possible500 = reason.errors.filterBy('status','500');
                                if(possible404.length !== 0) {
                                    alert('404 | Sign In Not Found Error');
                                    this.get('session').invalidate();
                                }
                                else if(possible500.length !== 0) {
                                    alert('500 | Sign In Server Error');
                                    this.get('session').invalidate();
                                }
                                reject();
                            });
                        }
                        else{
                            alert('Session for Server Validation failed! Logging out!');
                            this.get('session').invalidate();
                            resolve();
                        }
                });
            });
        } else {
            // Session is empty...
        }
    },
    serverValidation() {
        return new RSVP.Promise((resolve) => {
            var tokenAuthentication = this.get('store').createRecord('token', {
                id: this.get('session.data.authenticated.userId'),
                email: this.get('session.data.authenticated.email'),
                authenticity_token: this.get('session.data.authenticated.token'),
            });
            tokenAuthentication.save().then(() => {
                this.set('serverValidation',true);
                console.log('Server Validation complete with 200');
                resolve();
            }).catch((reason) => {
                this.set('serverValidation',false);
                resolve();
            });
        });
    }
});

Контроллер токенов

# Users Controller: JSON response through Active Model Serializers
class Api::V1::TokensController < ApiController
    respond_to :json

    def create
        if token_by_id == token_by_token
            if token_by_email == token_by_id
                render json: token_by_id, serializer: TokenSerializer, status: 200
            else
                render json: {}, status: 404
            end
        else
            render json: {}, status: 404
        end
    end

    private

    def token_by_id
        User.find(user_params[:id])
    end

    def token_by_email
        User.find_by(email: user_params[:email])
    end

    def token_by_token
        User.find_by(authentication_token: user_params[:authenticity_token])
    end

    def user_params
        ActiveModelSerializers::Deserialization.jsonapi_parse!(params.to_unsafe_h)
    end
end

2 ответа

Решение

Невозможно запретить пользователю редактировать содержимое своего локального хранилища, хранилища сеансов или файлов cookie.

Но это не должно вас беспокоить. Пользователь идентифицируется по значению токена. Маркер генерируется и отправляется ему аутентификатором при входе в систему. Чтобы выдать себя за другого пользователя, отредактировав данные сеанса, он должен знать, что другой пользователь вошел в систему, и знать токен этого пользователя.

Токен уже подписан на стороне сервера, стандартный механизм JWT.
Сказав это, может быть несколько способов проверить темперирование в локальном хранилище:

  1. Создайте токен, как вы это уже делали.
  2. Создайте случайный секретный ключ, который будет храниться на сервере.
  3. Создайте соответствующий HMAC, используя этот секретный ключ.
  4. Отправьте токен + HMAC пользователю.
  5. Когда пользователь отправляет вам этот токен, сначала проверьте правильность HMAC, если нет, то сразу же отклоните токен.
  6. Если HMAC правильный, проверьте токен, как вы уже это делали.

Другой способ:
Наряду с токеном, контрольная сумма HMAC также может храниться отдельно, и при отправке клиентом обратно на сервер проверять совпадение контрольной суммы.

Другие вопросы по тегам