Skip to content

Commit

Permalink
PHRAS-3857 Check CSRF token on Prod and Admin forms (#4361)
Browse files Browse the repository at this point in the history
* csrf token form

* add csrf token

* add csrf

* add csrf

* add csrf

* test

* test

* test

* add form token in report

* csrf token upload

* lazaret csrf form

* upload test

* lazaret test

* add csrf token

* fix test

* fix set cover publication

---------

Co-authored-by: jygaulier <[email protected]>
  • Loading branch information
aynsix and jygaulier authored Oct 3, 2023
1 parent 78a36ae commit b6a5f90
Show file tree
Hide file tree
Showing 71 changed files with 567 additions and 100 deletions.
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

0 comments on commit b6a5f90

Please sign in to comment.