Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHRAS-3857 Check CSRF token on Prod and Admin forms #4361

Merged
merged 25 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Phraseanet-production-client/dist/lightbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,8 @@ var lightbox = function lightbox(services) {
url: '/lightbox/ajax/SET_NOTE/' + sselcont_id + '/',
dataType: 'json',
data: {
note: note
note: note,
lightbox_token: (0, _jquery2.default)(button).closest('form').find('input[name=lightbox_token]').val()
},
success: function success(datas) {
_hideNotes(container);
Expand Down
3 changes: 2 additions & 1 deletion Phraseanet-production-client/dist/lightbox.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,8 @@ var lightbox = function lightbox(services) {
url: '/lightbox/ajax/SET_NOTE/' + sselcont_id + '/',
dataType: 'json',
data: {
note: note
note: note,
lightbox_token: (0, _jquery2.default)(button).closest('form').find('input[name=lightbox_token]').val()
},
success: function success(datas) {
_hideNotes(container);
Expand Down
36 changes: 29 additions & 7 deletions Phraseanet-production-client/dist/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -10135,6 +10135,10 @@ var workzone = function workzone(services) {
data: formData,
success: function success(data) {
(0, _jquery2.default)('#DIALOG-field-mapping').dialog('close');
},
error: function error(xhr, status, _error) {
var err = JSON.parse(xhr.responseText);
alert(err.message);
}
});
});
Expand Down Expand Up @@ -10221,6 +10225,10 @@ var workzone = function workzone(services) {
data: formData,
success: function success(data) {
(0, _jquery2.default)('#DIALOG-field-mapping').dialog('close');
},
error: function error(xhr, status, _error2) {
var err = JSON.parse(xhr.responseText);
alert(err.message);
}
});
});
Expand Down Expand Up @@ -10981,7 +10989,8 @@ var workzone = function workzone(services) {
dataType: 'json',
data: {
exposeName: '' + exposeName,
publicationData: publicationData
publicationData: publicationData,
prodExposeEdit_token: (0, _jquery2.default)(this).find('input[name="prodExposeEdit_token"]').val()
},
success: function success(data) {
if (data.success) {
Expand Down Expand Up @@ -12282,7 +12291,9 @@ var thesaurusService = function thesaurusService(services) {
sbas[i].seeker = _jquery2.default.ajax({
url: _zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: (0, _jquery2.default)('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function success(j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down Expand Up @@ -12319,7 +12330,9 @@ var thesaurusService = function thesaurusService(services) {
sbas[i].seeker = _jquery2.default.ajax({
url: zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: (0, _jquery2.default)('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function success(j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down Expand Up @@ -22040,7 +22053,8 @@ var moveRecord = function moveRecord(services) {
var datas = {
lst: (0, _jquery2.default)('input[name="lst"]', $form).val(),
base_id: (0, _jquery2.default)('select[name="base_id"]', $form).val(),
chg_coll_son: coll_son
chg_coll_son: coll_son,
prodMoveCollection_token: (0, _jquery2.default)('input[name="prodMoveCollection_token"]', $form).val()
};

var buttonPanel = $dialog.getDomElement().closest('.ui-dialog').find('.ui-dialog-buttonpane');
Expand Down Expand Up @@ -62298,6 +62312,7 @@ var deleteRecord = function deleteRecord(services) {
$trash_counter = $form.find(".to_trash_count"),
$loader = $form.find(".form-action-loader");
var lst = (0, _jquery2.default)("input[name='lst']", $form).val().split(';');
var csrfToken = (0, _jquery2.default)("input[name='prodDeleteRecord_token']", $form).val();

/**
* same parameters for every delete call, except the list of (CHUNKSIZE) records
Expand All @@ -62307,9 +62322,10 @@ var deleteRecord = function deleteRecord(services) {
type: $form.attr("method"),
url: $form.attr("action"),
data: {
'lst': "" // set in f
lst: '', // set in f
prodDeleteRecord_token: csrfToken
},
dataType: "json"
dataType: 'json'
};

var runningTasks = 0,
Expand All @@ -62335,7 +62351,11 @@ var deleteRecord = function deleteRecord(services) {
}
// pop & truncate
ajaxParms.data.lst = lst.splice(0, CHUNKSIZE).join(';');
_jquery2.default.ajax(ajaxParms).success(function (data) {
_jquery2.default.ajax(ajaxParms).error(function (data) {
fCancel();
$dialog.close();
alert('invalid csrf token delete form');
}).success(function (data) {
// prod feedback only if result ok
_jquery2.default.each(data, function (i, n) {
var imgt = (0, _jquery2.default)('#IMGT_' + n),
Expand Down Expand Up @@ -68256,6 +68276,8 @@ var uploader = function uploader(services) {
params.push((0, _jquery2.default)('input', (0, _jquery2.default)('.collection-status:visible', uploaderInstance.getSettingsBox())).serializeArray());
params.push((0, _jquery2.default)('select', uploaderInstance.getSettingsBox()).serializeArray());

params.push([{ name: 'prodUpload_token', value: (0, _jquery2.default)('input[name=prodUpload_token]').val() }]);

_jquery2.default.each(params, function (i, p) {
_jquery2.default.each(p, function (i, f) {
data.formData.push(f);
Expand Down
36 changes: 29 additions & 7 deletions Phraseanet-production-client/dist/production.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -10135,6 +10135,10 @@ var workzone = function workzone(services) {
data: formData,
success: function success(data) {
(0, _jquery2.default)('#DIALOG-field-mapping').dialog('close');
},
error: function error(xhr, status, _error) {
var err = JSON.parse(xhr.responseText);
alert(err.message);
}
});
});
Expand Down Expand Up @@ -10221,6 +10225,10 @@ var workzone = function workzone(services) {
data: formData,
success: function success(data) {
(0, _jquery2.default)('#DIALOG-field-mapping').dialog('close');
},
error: function error(xhr, status, _error2) {
var err = JSON.parse(xhr.responseText);
alert(err.message);
}
});
});
Expand Down Expand Up @@ -10981,7 +10989,8 @@ var workzone = function workzone(services) {
dataType: 'json',
data: {
exposeName: '' + exposeName,
publicationData: publicationData
publicationData: publicationData,
prodExposeEdit_token: (0, _jquery2.default)(this).find('input[name="prodExposeEdit_token"]').val()
},
success: function success(data) {
if (data.success) {
Expand Down Expand Up @@ -12282,7 +12291,9 @@ var thesaurusService = function thesaurusService(services) {
sbas[i].seeker = _jquery2.default.ajax({
url: _zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: (0, _jquery2.default)('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function success(j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down Expand Up @@ -12319,7 +12330,9 @@ var thesaurusService = function thesaurusService(services) {
sbas[i].seeker = _jquery2.default.ajax({
url: zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: (0, _jquery2.default)('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function success(j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down Expand Up @@ -22040,7 +22053,8 @@ var moveRecord = function moveRecord(services) {
var datas = {
lst: (0, _jquery2.default)('input[name="lst"]', $form).val(),
base_id: (0, _jquery2.default)('select[name="base_id"]', $form).val(),
chg_coll_son: coll_son
chg_coll_son: coll_son,
prodMoveCollection_token: (0, _jquery2.default)('input[name="prodMoveCollection_token"]', $form).val()
};

var buttonPanel = $dialog.getDomElement().closest('.ui-dialog').find('.ui-dialog-buttonpane');
Expand Down Expand Up @@ -62298,6 +62312,7 @@ var deleteRecord = function deleteRecord(services) {
$trash_counter = $form.find(".to_trash_count"),
$loader = $form.find(".form-action-loader");
var lst = (0, _jquery2.default)("input[name='lst']", $form).val().split(';');
var csrfToken = (0, _jquery2.default)("input[name='prodDeleteRecord_token']", $form).val();

/**
* same parameters for every delete call, except the list of (CHUNKSIZE) records
Expand All @@ -62307,9 +62322,10 @@ var deleteRecord = function deleteRecord(services) {
type: $form.attr("method"),
url: $form.attr("action"),
data: {
'lst': "" // set in f
lst: '', // set in f
prodDeleteRecord_token: csrfToken
},
dataType: "json"
dataType: 'json'
};

var runningTasks = 0,
Expand All @@ -62335,7 +62351,11 @@ var deleteRecord = function deleteRecord(services) {
}
// pop & truncate
ajaxParms.data.lst = lst.splice(0, CHUNKSIZE).join(';');
_jquery2.default.ajax(ajaxParms).success(function (data) {
_jquery2.default.ajax(ajaxParms).error(function (data) {
fCancel();
$dialog.close();
alert('invalid csrf token delete form');
}).success(function (data) {
// prod feedback only if result ok
_jquery2.default.each(data, function (i, n) {
var imgt = (0, _jquery2.default)('#IMGT_' + n),
Expand Down Expand Up @@ -68256,6 +68276,8 @@ var uploader = function uploader(services) {
params.push((0, _jquery2.default)('input', (0, _jquery2.default)('.collection-status:visible', uploaderInstance.getSettingsBox())).serializeArray());
params.push((0, _jquery2.default)('select', uploaderInstance.getSettingsBox()).serializeArray());

params.push([{ name: 'prodUpload_token', value: (0, _jquery2.default)('input[name=prodUpload_token]').val() }]);

_jquery2.default.each(params, function (i, p) {
_jquery2.default.each(p, function (i, f) {
data.formData.push(f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,8 @@ const lightbox = services => {
url: '/lightbox/ajax/SET_NOTE/' + sselcont_id + '/',
dataType: 'json',
data: {
note: note
note: note,
lightbox_token: $(button).closest('form').find('input[name=lightbox_token]').val()
},
success: function (datas) {
_hideNotes(container);
Expand Down
11 changes: 9 additions & 2 deletions Phraseanet-production-client/src/components/record/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const deleteRecord = (services) => {
$trash_counter = $form.find(".to_trash_count"),
$loader = $form.find(".form-action-loader");
let lst = $("input[name='lst']", $form).val().split(';');
let csrfToken = $("input[name='prodDeleteRecord_token']", $form).val();

/**
* same parameters for every delete call, except the list of (CHUNKSIZE) records
Expand All @@ -85,9 +86,10 @@ const deleteRecord = (services) => {
type: $form.attr("method"),
url: $form.attr("action"),
data: {
'lst': "" // set in f
lst: '', // set in f
prodDeleteRecord_token: csrfToken
},
dataType: "json"
dataType: 'json'
};

let runningTasks = 0, // number of running tasks
Expand All @@ -113,6 +115,11 @@ const deleteRecord = (services) => {
// pop & truncate
ajaxParms.data.lst = lst.splice(0, CHUNKSIZE).join(';');
$.ajax(ajaxParms)
.error(function (data) {
fCancel();
$dialog.close();
alert('invalid csrf token delete form');
})
.success(function (data) { // prod feedback only if result ok
$.each(data, function (i, n) {
let imgt = $('#IMGT_' + n),
Expand Down
3 changes: 2 additions & 1 deletion Phraseanet-production-client/src/components/record/move.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ const moveRecord = (services) => {
var datas = {
lst: $('input[name="lst"]', $form).val(),
base_id: $('select[name="base_id"]', $form).val(),
chg_coll_son: coll_son
chg_coll_son: coll_son,
prodMoveCollection_token: $('input[name="prodMoveCollection_token"]', $form).val()
};

var buttonPanel = $dialog.getDomElement()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,9 @@ const thesaurusService = services => {
sbas[i].seeker = $.ajax({
url: zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: $('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function (j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down Expand Up @@ -680,7 +682,9 @@ const thesaurusService = services => {
sbas[i].seeker = $.ajax({
url: zurl,
type: 'POST',
data: [],
data: {
prodTabThesaurus_token: $('form.thesaurus-filter-submit-action input[name=prodTabThesaurus_token]').val()
},
dataType: 'json',
success: function (j) {
var z = '#TX_P\\.' + j.parm.sbid + '\\.T';
Expand Down
11 changes: 10 additions & 1 deletion Phraseanet-production-client/src/components/ui/workzone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ const workzone = (services) => {
data: formData,
success: function (data) {
$('#DIALOG-field-mapping').dialog('close');
},
error: function (xhr, status, error) {
let err = JSON.parse(xhr.responseText);
alert(err.message);
}
});
});
Expand Down Expand Up @@ -296,6 +300,10 @@ const workzone = (services) => {
data: formData,
success: function (data) {
$('#DIALOG-field-mapping').dialog('close');
},
error: function (xhr, status, error) {
let err = JSON.parse(xhr.responseText);
alert(err.message);
}
});

Expand Down Expand Up @@ -1068,7 +1076,8 @@ const workzone = (services) => {
dataType: 'json',
data: {
exposeName: `${exposeName}`,
publicationData: publicationData
publicationData: publicationData,
prodExposeEdit_token: $(this).find('input[name="prodExposeEdit_token"]').val()
},
success: function (data) {
if (data.success) {
Expand Down
2 changes: 2 additions & 0 deletions Phraseanet-production-client/src/components/uploader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ const uploader = (services) => {
params.push($('input', $('.collection-status:visible', uploaderInstance.getSettingsBox())).serializeArray());
params.push($('select', uploaderInstance.getSettingsBox()).serializeArray());

params.push([{name: 'prodUpload_token', value: $('input[name=prodUpload_token]').val()}]);

$.each(params, function (i, p) {
$.each(p, function (i, f) {
data.formData.push(f);
Expand Down
31 changes: 31 additions & 0 deletions lib/Alchemy/Phrasea/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
use Alchemy\Phrasea\Authentication\Authenticator;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Model\Entities\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;

class Controller
{
Expand Down Expand Up @@ -112,6 +114,35 @@ public function getAuthenticatedUser()
return $this->getAuthenticator()->getUser();
}

public function setSessionFormToken($formName)
{
$randomValue = bin2hex(random_bytes(35));
$this->app['session']->set($formName.'_token', $randomValue);

return $randomValue;
}

public function getSessionFormToken($formName)
{
return $this->app['session']->get($formName.'_token');
}

public function isCrsfValid(Request $request, $formName)
{
if (!$request->isMethod("POST") && !$request->isMethod("PUT")) {
return false;
}

$formTokenName = $formName . '_token';
$formToken = (string) $request->request->get($formTokenName);

if (empty($formToken) || $formToken != $this->getSessionFormToken($formName)) {
return false;
}

return true;
}

/**
* @return PropertyAccess
*/
Expand Down
Loading