Gjør applikasjonen sikker, med enkle grep

#hailtothechefbaby

Makers of a sustainable future

Sikker utvikling

1

Ikke ha secrets i kode

const clientId = '21312adsa2432nsd438';
const clientSecret = 'ZGY2OWVjNTUtODgzYS01ZTljLThmOGMtNzQ0OTE4NWY2YTYx';
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;

Ikke commit .env!

$ ~/Workspace/knowit> git add .env

Vær paranoid

2

Valider input

<form>
  <div>
    <label for="choose">Would you prefer a banana or a cherry?</label>
    <input
      type="text"
      id="choose"
      name="i-like"
      required
      minlength="6"
      maxlength="6" />
  </div>
  <div>
    <label for="number">How many would you like?</label>
    <input type="number" id="number" name="amount" value="1" min="1" max="10" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>
<form>
  <label for="mail">
    I would like you to provide me with an email address:
  </label>
  <input type="email" id="mail" name="mail" />
  <button>Submit</button>
</form>
const email = document.getElementById("mail");

email.addEventListener("input", (event) => {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I am expecting an email address!");
  } else {
    email.setCustomValidity("");
  }
});

Bruk CAPTCHA

Bruk CSP

helmetjs

import { resolve } from 'path';
import express from 'express';
import helmet from 'helmet';

const port = process.env.PORT || 3000;
const app = express();

app.use(helmet());
app.use((req, res) => {
  res.sendFile(resolve(__dirname, '../index.html'));
});
app.listen(port);

console.log('server started on port ' + port);
…
app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ['\'self\'', '\'unsafe-inline\'', 'https://*.knowit.no'],
      scriptSrc: ['\'self\'', '\'unsafe-inline\'', 'https://*.knowit.no'],
      scriptSrcElem: ['\'self\'', '\'unsafe-inline\'', 'https://*.knowit.no'],
      scriptSrcAttr: ['\'self\'', '\'unsafe-inline\'', 'https://*.knowit.no'],
      styleSrc: ['\'self\'', '\'unsafe-inline\'', 'https://*.knowit.no'],
      connectSrc: ['\'self\'', 'localhost:*', 'https://*.knowit.no'],
      imgSrc: ['\'self\'', 'data:', '\'unsafe-inline\'', 'https://*.knowit.no']
    }
  })
);

app.use(
  helmet({
    contentSecurityPolicy: false,
    crossOriginEmbedderPolicy: false
  })
);
…

Krypter, krypter, krypter!

3*

Autentisering, roller mm

4*

Håndter feil riktig!

5

…

// This throws an error!
const response = await fetch('/api/person',{ method: 'POST', …});

…
…

try{
  const response = await fetch('/api/person',{ method: 'POST', …});
  
  …
} catch(e){
  
  console.log(e);
  // or
  res.status(500).send(e.message)
 
}

…
…
try{
  const response = fetch('/api/person',{ method: 'POST', …});
  …
} catch(e){
  const secureErrorMessage = getSecureErrorMessage(e);
  // or
  res.status(500).send(secureErrorMessage)
 
}
…

const getSecureErrorMessage = (e) => {
  const { message } = e;
  
  if( includesIdentifierToTriggerSecureMessage(message)){
    return 'New error message that does not contain dangerous information'
  }
}

Unngå feilkonfigurasjon

6

Ikke la webserveren liste filer i mapper

Oppdater sertifikater

Slett gamle brukere

HTTPS

7

Logger

8

Audit

Activity Streams 2.0

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "summary": "Martin added an article to his blog",
  "type": "Add",
  "published": "2015-02-10T15:04:55Z",
  "actor": {
   "type": "Person",
   "id": "http://www.test.example/martin",
   "name": "Martin Smith",
   "url": "http://example.org/martin",
   "image": {
     "type": "Link",
     "href": "http://example.org/martin/image.jpg",
     "mediaType": "image/jpeg"
   }
  },
  "object" : {
   "id": "http://www.test.example/blog/abc123/xyz",
   "type": "Article",
   "url": "http://example.org/blog/2011/02/entry",
   "name": "Why I love Activity Streams"
  },
  "target" : {
   "id": "http://example.org/blog/",
   "type": "OrderedCollection",
   "name": "Martin's Blog"
  }
}

morgan

import { resolve } from 'path';
import express from 'express';
import morgan from 'morgan';

const port = process.env.PORT || 3000;
const app = express();

app.use(morgan('combined'));
app.use((req, res) => {
  res.sendFile(resolve(__dirname, '../index.html'));
});

app.listen(port);
console.log('server started on port ' + port);
::1 - - [22/Mar/2023:13:21:13 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /env.js HTTP/1.1" 200 59 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /start.knowit.css HTTP/1.1" 200 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /start.knowit.js HTTP/1.1" 200 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /start.knowit.js HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /default-d474b761.js HTTP/1.1" 200 831 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "POST /auth/refresh HTTP/1.1" 403 20 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /images/meta/favicon-32x32.png HTTP/1.1" 200 1215 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /meta/site.webmanifest HTTP/1.1" 200 428 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /auth/login HTTP/1.1" 302 480 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /images/meta/android-chrome-192x192.png HTTP/1.1" 200 2318 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
::1 - - [22/Mar/2023:13:21:13 +0000] "GET /auth/userInfo HTTP/1.1" 200 12 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"

Testing

9*

Vær proaktiv

10

Kjør pentester

npm audit <3

Hold deg oppdatert!

Vær REaktiv

11

Vær tidlig ute

Post mortems er king!

G D P R

12*

Universell utforming

13*

Thanks for listening!

Alexander Vassbotn Røyne-Helgesen

Principal Engineer, Knowit, Oslo, Norway