feat: implement CSRF token in export user data

Signed-off-by: Raccoon <raccoon@hackmd.io>
This commit is contained in:
Raccoon 2021-06-11 16:54:26 +08:00
parent 2d1b82e3ff
commit 9c6da79cd3
No known key found for this signature in database
GPG Key ID: 06770355DC9ECD38
6 changed files with 61 additions and 4 deletions

View File

@ -16,7 +16,8 @@ exports.showIndex = async (req, res) => {
errorMessage: req.flash('error'), errorMessage: req.flash('error'),
privacyStatement: fs.existsSync(path.join(config.docsPath, 'privacy.md')), privacyStatement: fs.existsSync(path.join(config.docsPath, 'privacy.md')),
termsOfUse: fs.existsSync(path.join(config.docsPath, 'terms-of-use.md')), termsOfUse: fs.existsSync(path.join(config.docsPath, 'terms-of-use.md')),
deleteToken: deleteToken deleteToken: deleteToken,
csrfToken: req.csrfToken()
} }
if (!isLogin) { if (!isLogin) {

View File

@ -17,8 +17,11 @@ const appRouter = Router()
// register route // register route
const csurf = require('csurf')
const csurfMiddleware = csurf({ cookie: true })
// get index // get index
appRouter.get('/', wrap(indexController.showIndex)) appRouter.get('/', csurfMiddleware, wrap(indexController.showIndex))
// ----- error page ----- // ----- error page -----
// get 403 forbidden // get 403 forbidden
@ -52,7 +55,7 @@ appRouter.get('/me', wrap(userController.getMe))
appRouter.get('/me/delete/:token?', wrap(userController.deleteUser)) appRouter.get('/me/delete/:token?', wrap(userController.deleteUser))
// export the data of the authenticated user // export the data of the authenticated user
appRouter.get('/me/export', userController.exportMyData) appRouter.post('/me/export', urlencodedParser, csurfMiddleware, userController.exportMyData)
appRouter.get('/user/:username/avatar.svg', userController.getMyAvatar) appRouter.get('/user/:username/avatar.svg', userController.getMyAvatar)

45
package-lock.json generated
View File

@ -4323,6 +4323,16 @@
"resolved": "https://registry.npmjs.org/csextends/-/csextends-1.2.0.tgz", "resolved": "https://registry.npmjs.org/csextends/-/csextends-1.2.0.tgz",
"integrity": "sha512-S/8k1bDTJIwuGgQYmsRoE+8P+ohV32WhQ0l4zqrc0XDdxOhjQQD7/wTZwCzoZX53jSX3V/qwjT+OkPTxWQcmjg==" "integrity": "sha512-S/8k1bDTJIwuGgQYmsRoE+8P+ohV32WhQ0l4zqrc0XDdxOhjQQD7/wTZwCzoZX53jSX3V/qwjT+OkPTxWQcmjg=="
}, },
"csrf": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz",
"integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==",
"requires": {
"rndm": "1.2.0",
"tsscmp": "1.0.6",
"uid-safe": "2.1.5"
}
},
"css-b64-images": { "css-b64-images": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz", "resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
@ -4608,6 +4618,31 @@
} }
} }
}, },
"csurf": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz",
"integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"csrf": "3.1.0",
"http-errors": "~1.7.3"
},
"dependencies": {
"http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
}
}
},
"cyclist": { "cyclist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@ -14155,6 +14190,11 @@
"inherits": "^2.0.1" "inherits": "^2.0.1"
} }
}, },
"rndm": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz",
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w="
},
"run-async": { "run-async": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@ -15858,6 +15898,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}, },
"tsscmp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",

View File

@ -49,6 +49,7 @@
"connect-session-sequelize": "~6.0.0", "connect-session-sequelize": "~6.0.0",
"cookie": "~0.4.0", "cookie": "~0.4.0",
"cookie-parser": "~1.4.4", "cookie-parser": "~1.4.4",
"csurf": "~1.11.0",
"deep-freeze": "~0.0.1", "deep-freeze": "~0.0.1",
"ejs": "~2.6.2", "ejs": "~2.6.2",
"express": "~4.17.1", "express": "~4.17.1",

View File

@ -428,3 +428,7 @@ $('.ui-use-tags').on('change', function () {
$('.search').keyup(() => { $('.search').keyup(() => {
checkHistoryList() checkHistoryList()
}) })
$(".ui-export-user-data").click(function (e) {
document.exportNoteData.submit()
})

View File

@ -19,6 +19,9 @@
<button class="btn btn-sm btn-success ui-signin" data-toggle="modal" data-target=".signin-modal"><%= __('Sign In') %></button> <button class="btn btn-sm btn-success ui-signin" data-toggle="modal" data-target=".signin-modal"><%= __('Sign In') %></button>
<% } %> <% } %>
</div> </div>
<form name="exportNoteData" action="<%- serverURL %>/me/export" method="post">
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
</form>
<div class="ui-signout" style="float: right; margin-top: 8px;<% if(!signin) { %> display: none;<% } %>"> <div class="ui-signout" style="float: right; margin-top: 8px;<% if(!signin) { %> display: none;<% } %>">
<a type="button" href="<%- serverURL %>/new" class="btn btn-sm btn-primary"><i class="fa fa-plus"></i> <%= __('New note') %></a> <a type="button" href="<%- serverURL %>/new" class="btn btn-sm btn-primary"><i class="fa fa-plus"></i> <%= __('New note') %></a>
<span class="ui-profile dropdown pull-right"> <span class="ui-profile dropdown pull-right">
@ -27,7 +30,7 @@
</button> </button>
<ul class="dropdown-menu" aria-labelledby="profileLabel"> <ul class="dropdown-menu" aria-labelledby="profileLabel">
<li><a href="<%- serverURL %>/features"><i class="fa fa-dot-circle-o fa-fw"></i> <%= __('Features') %></a></li> <li><a href="<%- serverURL %>/features"><i class="fa fa-dot-circle-o fa-fw"></i> <%= __('Features') %></a></li>
<li><a href="<%- serverURL %>/me/export"><i class="fa fa-cloud-download fa-fw"></i> <%= __('Export user data') %></a></li> <li><a href="#" class="ui-export-user-data"><i class="fa fa-cloud-download fa-fw"></i> <%= __('Export user data') %></a></li>
<li><a class="ui-delete-user" data-toggle="modal" data-target=".delete-user-modal"><i class="fa fa-trash fa-fw"></i> <%= __('Delete user') %></a></li> <li><a class="ui-delete-user" data-toggle="modal" data-target=".delete-user-modal"><i class="fa fa-trash fa-fw"></i> <%= __('Delete user') %></a></li>
<li><a href="<%- serverURL %>/logout"><i class="fa fa-sign-out fa-fw"></i> <%= __('Sign Out') %></a></li> <li><a href="<%- serverURL %>/logout"><i class="fa fa-sign-out fa-fw"></i> <%= __('Sign Out') %></a></li>
</ul> </ul>