add anywhere csrf protection
This commit is contained in:
parent
c55264cf42
commit
24ea8deeec
|
|
@ -1,7 +1,7 @@
|
|||
{# app/Views/macros/forms.twig #}
|
||||
{% macro form_open(action, attributes = '') %}
|
||||
<form action="{{ action }}" method="post" {{ attributes|raw }}>
|
||||
{# Выводим глобальную переменную csrf_token #}
|
||||
{# Добавляем data-ajax="true" для автоматической CSRF защиты #}
|
||||
<form action="{{ action }}" method="post" data-ajax="true" {{ attributes|raw }}>
|
||||
{{ csrf_field()|raw }}
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@
|
|||
{% endif %}
|
||||
|
||||
{# Форма принятия/отклонения #}
|
||||
<form action="/invitation/accept/{{ token }}" method="POST">
|
||||
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>">
|
||||
<form action="/invitation/accept/{{ token }}" method="POST" data-ajax="true">
|
||||
{{ csrf_field()|raw }}
|
||||
<input type="hidden" name="action" value="accept">
|
||||
|
||||
<div class="d-flex gap-3">
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@
|
|||
{% endif %}
|
||||
|
||||
{# Форма регистрации #}
|
||||
<form action="/invitation/complete/{{ token }}" method="POST">
|
||||
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>">
|
||||
<form action="/invitation/complete/{{ token }}" method="POST" data-ajax="true">
|
||||
{{ csrf_field()|raw }}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="completeName" class="form-label">Ваше имя</label>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="inviteUserForm" action="/organizations/users/{{ organization_id }}/invite" method="POST">
|
||||
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>">
|
||||
<form id="inviteUserForm" action="/organizations/users/{{ organization_id }}/invite" method="POST" data-ajax="true">
|
||||
{{ csrf_field()|raw }}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="inviteEmail" class="form-label">Email адрес</label>
|
||||
|
|
|
|||
|
|
@ -93,4 +93,98 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
|
||||
document.cookie = cookieString;
|
||||
}
|
||||
})();
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Получение CSRF токена из мета-тега
|
||||
*/
|
||||
function getCsrfToken() {
|
||||
const meta = document.querySelector('meta[name="csrf-token"]');
|
||||
return meta ? meta.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление CSRF токена в мета-теге
|
||||
*/
|
||||
function updateCsrfToken(token, hash) {
|
||||
const tokenMeta = document.querySelector('meta[name="csrf-token"]');
|
||||
const hashMeta = document.querySelector('meta[name="csrf-hash"]');
|
||||
if (tokenMeta) tokenMeta.setAttribute('content', token);
|
||||
if (hashMeta) hashMeta.setAttribute('content', hash);
|
||||
}
|
||||
|
||||
// Перехват fetch()
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function(url, options = {}) {
|
||||
// Добавляем CSRF токен в заголовки
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
|
||||
// Если headers это объект - добавляем токен
|
||||
if (options.headers instanceof Headers) {
|
||||
options.headers.set('X-CSRF-TOKEN', getCsrfToken());
|
||||
} else if (typeof options.headers === 'object') {
|
||||
options.headers['X-CSRF-TOKEN'] = getCsrfToken();
|
||||
}
|
||||
|
||||
// Выполняем запрос
|
||||
return originalFetch(url, options).then(response => {
|
||||
// Проверяем, пришёл ли новый токен в ответе
|
||||
const newToken = response.headers.get('X-CSRF-TOKEN');
|
||||
const newHash = response.headers.get('X-CSRF-HASH');
|
||||
if (newToken && newHash) {
|
||||
updateCsrfToken(newToken, newHash);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
};
|
||||
|
||||
// Перехват XMLHttpRequest (для jQuery и других библиотек)
|
||||
const originalOpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function(method, url) {
|
||||
this._csrfToken = getCsrfToken();
|
||||
return originalOpen.apply(this, arguments);
|
||||
};
|
||||
|
||||
const originalSend = XMLHttpRequest.prototype.send;
|
||||
XMLHttpRequest.prototype.send = function(body) {
|
||||
// Добавляем CSRF токен в заголовки
|
||||
this.setRequestHeader('X-CSRF-TOKEN', this._csrfToken || getCsrfToken());
|
||||
|
||||
// Слушаем ответ для обновления токена
|
||||
this.addEventListener('load', function() {
|
||||
const newToken = this.getResponseHeader('X-CSRF-TOKEN');
|
||||
const newHash = this.getResponseHeader('X-CSRF-HASH');
|
||||
if (newToken && newHash) {
|
||||
updateCsrfToken(newToken, newHash);
|
||||
}
|
||||
});
|
||||
|
||||
return originalSend.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Автоматическое обновление CSRF токена в AJAX формах перед отправкой
|
||||
document.addEventListener('submit', function(e) {
|
||||
const form = e.target;
|
||||
if (form.dataset.ajax === 'true') {
|
||||
const tokenInput = form.querySelector('input[name="csrf_token"]');
|
||||
if (tokenInput) {
|
||||
tokenInput.value = getCsrfToken();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Обновление CSRF токена в скрытых полях при загрузке страницы (только для AJAX форм)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const forms = document.querySelectorAll('form[data-ajax="true"]');
|
||||
forms.forEach(function(form) {
|
||||
const tokenInput = form.querySelector('input[name="csrf_token"]');
|
||||
if (tokenInput) {
|
||||
tokenInput.value = getCsrfToken();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
Loading…
Reference in New Issue