Инструменты пользователя

Инструменты сайта


nodejs:sessija_i_avtorizacijam:csrf

CSRF - защита (Добавить токен в формы csurf)

Атака CSRF

Нельзя говорить про AJAX и не упомянуть про важнейшую деталь его реализации – защиту от CSRF-атак.

CSRF (Cross-Site Request Forgery, также XSRF) – опаснейшая атака, которая приводит к тому, что хакер может выполнить на неподготовленном сайте массу различных действий от имени других, зарегистрированных посетителей.

Какие это действия – отправка ли сообщений, перевод денег со счёта на счёт или смена паролей – зависят от сайта, но в любом случае эта атака входит в образовательный минимум веб-разработчика.

server.js

const express = require('express');//Подключаем модуль
const path = require('path');//Модуль для работы с путями к файлам и папкам
const csrf = require('csurf');//CSRF - защита (Cross-Site Request Forger)
const exphbs = require('express-handlebars');//Подключаем шаблонизатор
const db = require('mongoose'); //библиотека для mongodb
const session = require('express-session');//модуль для создания сессий, в express
const MongoStore = require('connect-mongodb-session')(session);//Реквайр возвращает функцию которую мы сразу вызываем,
// куда мы передаем пакет session, который используем для синхронизации. Вернет класс
const config =  require('./config/config'); //конфиг с url к БД, и секретным ключом
const User = require('./models/user'); //
const varMiddleware = require('./middleware/variables');//модуль проверки авторизации
const userMiddleware = require('./middleware/user');//модуль инициализации авторизованного пользователя
 
const app= express();//Результат работы модуля express, по сути создает сервер
const PORT = process.env.PORT || 3000; //Получить порт из переменного окружения или использовать дефолтный
const store = new MongoStore({//создаем коллекцию в БД
    collection: 'sessions',//Название коллекции
    uri: config.mongoURI//коннект
 
});
 
/**
 * Подключаем роуты
 */
const homeRoutes = require('./routes/home');//главная страница
const coursesRoutes = require('./routes/courses');//Страница courses
const addRoutes = require('./routes/add');//Страница add
const aboutRoutes = require('./routes/about');//Страница about
const cardRoutes = require('./routes/card');//Страница about
const ordersRoutes = require('./routes/orders');//Страница about
const authRoutes = require('./routes/auth');//Страница auth
 
const hbs= exphbs.create({//Настройки шаблонизатора
    defaultLayout: 'main',//Шаблон по умолчанию, основной, (template/layouts/main.hbs)
    extname: 'hbs' //Расширение шаблонов, пользовательское. По умолчанию express-handlebars
});
 
app.engine('hbs', hbs.engine);//Подключени шаблонизатора к express, регестрируем движок
app.set('view engine', 'hbs');//Запускаем шаблонизатор, используем
app.set('views', 'template');//Указываем папку с шаблонами
 
app.use(express.static(path.join(__dirname,'public')));//Статические папки для хранения стилей картинок
app.use(express.urlencoded({extended: true}));
app.use(session({//инициализация сессии
    secret: 'key secret',//секретный ключ для сессии
    resave: false,
    saveUninitialized: false,
    store: store //передаем объект
}));
app.use(csrf());//Включение CSRF защиты (Cross-Site Request Forger), подключать нужно после сессии
app.use(varMiddleware);//middleware - срабатывает при загруке страницы, данный метод проверяет авторизацию middleware/variables.js
app.use(userMiddleware);//middleware - срабатывает при загруке страницы, инициализируем авторизованного пользователя
/**
 * Регестрируем роуты, с префиксами
 */
app.use('/', homeRoutes);//Главная страница
app.use('/courses',coursesRoutes);//страница courses
app.use('/add',addRoutes);//страница add
app.use('/about',aboutRoutes);//Страница about
app.use('/card',cardRoutes);//Страница корзина
app.use('/orders',ordersRoutes);//Страница заказов
app.use('/auth',authRoutes);//Страница заказов
 
/**
 * Запуск сервера
 * @returns {Promise<void>}
 */
async function start(){
    try{
        await db.connect(config.mongoURI,{//подключение к БД
            useNewUrlParser: true,
            useFindAndModify: false
        });
 
        console.log('MongoDB has started ...');
        app.listen(PORT, () => {//Слушать 3000 порт
            console.log(`server is running on port ${PORT}`);
        });
    }
    catch(e){
        console.log(e);//Ксли коннект не удался;
    }
}
start();

если используется обычная форма, token отправляем скрытым полем

<form action="/auth/login" method="post">
	<div class="row">
		<div class="input-field col s12">
			<input  id="email" type="text" name="email" class="validate" required />
			<label for="email">email</label>
			<span class="helper-text" data-error="введите email" data-success="right"></span>
		</div>
		<div class="input-field col s12">
			<input id="password" type="text" name="password" class="validate" required />
			<label for="password">пароль</label>
			<span class="helper-text" data-error="Введите password" data-success="right"></span>
		</div>
	</div>
 
	<div class="row">
		<div class="input-field col">
			<input type="hidden" name="_csrf" value="{{csrf}}">
			<input type="submit"  class="btn" value="войти">
		</div>
	</div>
</form>

public/js/script.js если отправляем форму через ajax, токен отдаем заголовком

const $card = document.querySelector('#card');
 
if($card){
    $card.addEventListener('click', (evt) => {
       // evt.preventDefault();
        if(evt.target.classList.contains('js-remove')){//получить список элементов с классами содержащие класс "js-remove"
            const id  = evt.target.dataset.id; //аналог jquery,  $(evt.target).data('id')
            const csrf  = evt.target.dataset.csrf; //аналог jquery,  $(evt.target).data('csrf')
            console.log(evt.target.dataset.csrf);
            fetch('/card/remove/'+id,{//Ajax запрос замена XMLHttpRequest()
                method: 'delete',//delete - запрос
                headers:{'X-XSRF-TOKEN': csrf}//заголовки запроса
                //body:JSON.stringify({//Тело запроса
                //    _csrf:csrf
                //})
            }).then(res => res.json())
              .then(card => {//Возвращает промис
                console.log(card);
                if(card.courses.length){
                    const html= card.courses.map((c)=> {
                        return `
                        <tr>
                            <td>${c.title}</td>
                            <td>${c.count}</td>
                            <td><a href="#" class="btn"><i class="material-icons js-remove" data-id="${c.id}">delete</i></a></td>
                        </tr>
                        `;
                    }).join();
                    $card.querySelector('tbody').innerHTML = html;
                    $card.querySelector('.price').textContent = new Intl.NumberFormat('ru-Ru', //локаль
                        {
                            currency: 'rub',//опции
                            style: 'currency'
                        }).format(card.price);//здесь какоето число
                }
                else{
                    $card.innerHTML = `<p>Корзина пуста</p>`
                }
            });
        }
    })
}
nodejs/sessija_i_avtorizacijam/csrf.txt · Последние изменения: 2023/01/12 12:18 (внешнее изменение)