Check the signature of the form by checking the timestamp

parent 32412fe1
Pipeline #52448586 passed with stage
in 1 minute and 7 seconds
......@@ -2,6 +2,7 @@
// core
const path = require('path')
const crypto = require('crypto')
// npm
const express = require('express')
......@@ -32,6 +33,21 @@ const sitemap = [
]
const sitemapTxt = sitemap.join('\n') + '\n'
function createTsSig () {
const ts = (new Date()).toISOString()
const sig = crypto.createHmac('sha256', env.hmacSecretKey)
.update(ts)
.digest('hex')
return { ts, sig }
}
function checkTsSig (ts, sig) {
const sigCheck = crypto.createHmac('sha256', env.hmacSecretKey)
.update(ts)
.digest('hex')
return sigCheck === sig
}
// --------------------------------------------------------------------------------------------------------------------
// app
......@@ -83,9 +99,12 @@ app.get(
'/',
(req, res) => {
stats.home.inc()
const hmac = createTsSig()
res.render('index', {
menu: 'home',
ts: (new Date()).toISOString(),
ts: hmac.ts,
sig: hmac.sig,
form: {}
})
}
......@@ -98,7 +117,7 @@ app.post(
// deconstruct all of the input fields
console.log('body:', req.body)
const { url, name, email, location, ts } = req.body
const { url, name, email, location, ts, sig } = req.body
// For SPAM, we're going to check the following:
//
......@@ -106,14 +125,17 @@ app.post(
// 2. the dummy field 'email' is (and always should be) '[email protected]'
// 3. the dummy field 'location' is (and always should be) exactly the same as 'url'
// 4. the field 'ts' is (and always should be) within the past 5 mins
// 5. the signature field should be valid and dependent on the 'ts'
// 0. before SPAM check that URL is defined
if (url === '') {
const hmac = createTsSig()
res.render('index', {
ts: (new Date()).toISOString(),
ts: hmac.ts,
sig: hmac.sig,
menu: 'home',
err: 'Provide a URL',
form: {},
form: {}
})
}
......@@ -137,20 +159,28 @@ app.post(
// 4. the field 'ts' is (and always should be) within the past 5 mins
// check it actually looks like a timestamp e.g. '2019-03-18T20:05:42.276Z'
if ( !ts.match(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d+Z$/) ) {
if (!ts.match(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d+Z$/)) {
res.send('Thanks')
return
}
const diffInMins = dateFns.differenceInMinutes( new Date(ts), new Date() )
if ( diffInMins < 0 || diffInMins > 5 ) {
const diffInMins = dateFns.differenceInMinutes(new Date(ts), new Date())
if (diffInMins < 0 || diffInMins > 5) {
res.send('Thanks')
return
}
// 5. the signature field should be valid and dependent on the 'ts'
if (!checkTsSig(ts, sig)) {
res.send('Thanks')
return
}
const result = await api.createUrl(url)
if (!result.ok) {
const hmac = createTsSig()
res.render('index', {
ts: (new Date()).toISOString(),
ts: hmac.ts,
sig: hmac.sig,
menu: 'home',
err: result.msg,
form: {
......
......@@ -14,6 +14,8 @@ const protocol = process.env.PROTOCOL || 'https'
const baseUrl = `${protocol}://${apex}`
const port = process.env.PORT || 3000
const hmacSecretKey = process.env.HMAC_SECRET_KEY
const databaseUrl = process.env.DATABASE_URL
const googleAnalytics = process.env.GOOGLE_ANALYTICS
......@@ -27,6 +29,7 @@ module.exports = {
port,
isProd,
isDev,
hmacSecretKey,
databaseUrl,
googleAnalytics
}
......
var url = document.getElementById('url');
var loc = document.getElementById('location');
var url = document.getElementById('url')
var loc = document.getElementById('location')
url.addEventListener('change', function(ev) {
loc.value = ev.target.value;
url.addEventListener('change', function (ev) {
loc.value = ev.target.value
})
......@@ -10,6 +10,7 @@ block content
input.hidden(type="text" name="email" placeholder="Your Email" value="[email protected]")
input#location.hidden(type="text" name="location" placeholder="Location" value="")
input.hidden(type="text" name="ts" value=ts)
input.hidden(type="text" name="sig" value=sig)
input#url.p-6.m-3.border.border-grey-dark.rounded(type="text" name="url" placeholder="https://..." value=form.url)
input.bg-blue-dark.text-white.p-6.m-3.rounded(type="submit" value="Shorten")
if err
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment