NML Says

Infrastructure 3 - Security cont.

References for this Part

Wladston Ferreira Filho, Computer Science Distilled: Learn The Art Of Solving Computational Problems code.energy, 2017

https://www.geeksforgeeks.org/computer-security-overview/

Model Solution Previous Lesson

Example 1. routes/users.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const express = require('express');
const control = require('../controllers/controllers.js');

const router = express.Router();

/* get registration form */
router.get('/register', function(req, res, next) {
    res.render('register', {
        title: 'Registrer Please'
    });
});

/* post registration data from form */
router.post('/register', control.postRegistration, function(req, res, next) {
    res.render('login', {
        title: 'Login Please'
    });
});

/* get login form */
router.get('/login', function(req, res, next) {
    res.render('login', {
        title: 'Login Please'
  });
});

/* post login data from form */
router.post('/login', control.verifyLogin, function(req, res, next) {
    res.status(200).json("Bearer " + res.locals.token);
});

module.exports = router;
Example 2. routes/index.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const express = require('express');
const con = require('../controllers/controllers.js');

const router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { 
        title: 'Express' 
    });
});

/* GET quotes page. */
router.get('/quotes', function(req, res, next) {
    res.render('quotes', { 
        title: 'Quotes from Menu'
    });
});
router.post('/quotes', con.isAuth, con.getQuotes, function(req, res, next) {
    res.render('quotes', {
        title: 'Quotes from Filter',
        rows: res.locals.quotes
    });
});

/* POST quote */
router.post('/quote', con.isAuth, con.postQuote, con.getQuotes, function(req, res, next) {
    res.render('/quotes', {
        title: 'Quotes',
        rows: res.locals.quotes
    });
});



module.exports = router;
Example 3. controllers/controllers.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* controllers.js */

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const models = require('../models/dbHandlers');

module.exports = {
    getQuotes: async function(req, res, next) {
        try {
            await models.selectQuotes(req, res);
            next();
        } catch(err) {
            res.status(500).json({message: err.message});
        }
    },

    isAdmin: async function(req, res, next) {
        try {
            if (res.locals.authorized && res.locals.profile == SU) 
                next();
            else
                throw new Error('You must be a logged in admin');
        } catch(err) {
            res.status(500).json({message: err.message});
        }
    }, 

    isAuth: async function(req, res, next) {
        try {
            if (!req.body.jwttoken)
                throw new Error('Missing token in hidden field')
            let token = req.body.jwttoken.split(' ')[1];
            if (!token)
                throw new Error('Malformed token!');
            let rc = await jwt.verify(token, process.env.SECRET);
            if (!rc)
                throw new Error('Token does not verify');
            // check expiration
            next();
        } catch(err) {
            res.status(500).json({message: err.message});
        }
    },

    postQuote: async function(req, res, next) {
        try {
            next();
        } catch(err) {
            res.status(500).json({message: err.message});
        }
    },

    postRegistration: async function(req, res, next) {
        try {
            if (
                req.body.email === '' ||
                req.body.password === '' ||
                req.body.passwordrep === '' ||
                req.body.bio === '' 
            ) {
                throw new Error('All fields must have content');
            }
            if (req.body.password != req.body.passwordrep)
                throw new Error('Non matching passwords');
            let hash = await bcrypt.hash(req.body.password, parseInt(process.env.ROUNDS));
            res.locals.hash = hash;
            await models.insertUser(req, res);
            next();
        } catch(err) {
            res.status(500).json({message: err.message});
        }
    },

    verifyLogin: async function(req, res, next) {
        try {
            await models.getUser(req, res, next);
            let rc = await bcrypt.compare(req.body.password, '' + res.locals.user.password);
            if (!rc)
                throw new Error('Error in Credentials');

            const payload = { email: res.locals.user.email, profile: res.locals.user.profile };
            const lifetime = { expiresIn: '1h' };
            let token = await jwt.sign(payload, process.env.SECRET, lifetime);
            if (!token)
                throw new Error('Error in credentials, token');
            res.locals.token = token;
            next();
        } catch (err) {
            return res.status(500).json({message: err.message});
        }
    }

}
Example 4. models/dbHandlers.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const models = require('./dbConnectionHandler');

module.exports = {
    getUser: async function(req, res) {
        try {
            const dbh = await models.connect();
            let sql = 'select * from user where email = ?';
            let row = await dbh.query(sql, [req.body.email]);
            res.locals.user = row[0];
        } catch(err) {
            throw new Error(err.message);
        }
    },

    insertUser: async function(req, res) {
        try {
            const dbh = await models.connect();
            let sql = 'insert into user (email, password, bio) values(?, ?, ?)';
            await dbh.query(sql, [req.body.email, res.locals.hash, req.body.bio]);
        } catch (err) {
            throw new Error(err.message);
        }
    },

    selectQuotes: async function(req, res) {
        try {
            let f = '%';
            if (req.body.filter != '*')
                f = '%' + req.body.filter + '%';
            const dbh = await models.connect();
            let sql = `select * from quote where attribution like '${f}'`;
            let rows = await dbh.query(sql);
            res.locals.quotes = rows;
        } catch (err) {
            throw new Error('db quote read err');
        }
    }
}
Example 5. public/javascripts/all.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
'use strict';
const $ = function(id) {
    return document.getElementById(id);
};

const presub = async function(ev) {
    ev.preventDefault();
        
    // send login form with fetch
    let form = $('forms22');
    let data = {
        email: form.email.value,
        password: form.password.value
    }

    let response = await fetch('/users/login', {
        method: 'POST', 
        headers: {
            'Content-Type': 'application/json; charset=UTF-8',
        },
        body: JSON.stringify(data),
    });

    let text = await response.text(); // read response body as text
    let token = text.substring(1, text.length - 1);
    localStorage.setItem('token', token);
    window.location.href = '/';
};

const addToken = async function(ev) {
    ev.preventDefault();
    if (!localStorage.getItem('token'))
        window.location.href = '/login';
    ev.target.jwttoken.value = localStorage.getItem('token');
    ev.target.submit();
}

const doSomething = function() {
    if ($('forms22'))
        $('forms22').addEventListener('submit', presub);
    else if ($('formqf')) {
        $('formqf').addEventListener('submit', addToken);
    }
}

window.addEventListener('load', doSomething);
Example 6. views/layout.pug
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
doctype html
html
    head
        title= title
        link(rel='stylesheet', href='/stylesheets/style.css')
        script(src='/javascripts/all.js')
    body
        header
            ul
                li
                    a(href='/') Home
                li
                    a(href='/users/register') Register
                li
                    a(href='/users/login') Login
                li
                    a(class='demandsToken' href='/quotes') Quotes
        main
            block content
Example 7. views/quotes.pug
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
extends layout

block content
    h1= title
    
    section
        form(action='/quotes' id='formqf' method='post')
            input(type='hidden' name='jwttoken')
            label(for='filter') Filter
            br
            input(type='text' name='filter' value='*')
            br
            br
            input(type='submit' value='Go')
            br
        if rows 
            each row in rows
                p #{row.quote}
                    br
                    span -- #{row.attribution}

Security cont.

In the previous session we referred specifically to data security. Our source https://www.geeksforgeeks.org/computer-security-overview/ classifies computer security into four types:

  • Cyber security. What comes from the internet.
  • Information security. What we covered in the previous session, ie what we call data security.
  • Application security. What the developer deals with when creating authentication, encryption of select data, and profiling of users.
  • Network security.

GeeksforGeeks also lists types of cyber attacks. Let us look at their site.

Exercises

Exercise ITI.3.0

Knowing what we have done already, and knowing that it works.

  • Add a view with a form for entering a quote.
  • See that the router can display the view.
  • Add a route for receiving the entered quote.
  • Create the appropriate handler.
  • Create the appropriate model function to enter the quote into the database.
  • Test with quotes view.
  • Test the duration of the jwt ie that viewing quotes, and entering new quotes is no longer necessary after the token expires.