Skip to content

Commit 4517cfd

Browse files
committed
Angular Security course
1 parent 692ef59 commit 4517cfd

8 files changed

+53
-8
lines changed

csrf/csrf-page.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ <h1>GOTCHA!!!</h1>
1515

1616
<script>
1717

18-
document.getElementById("csrf-form").submit();
18+
setTimeout(function() {
19+
document.getElementById("csrf-form").submit();
20+
},2000);
21+
22+
1923

2024
</script>
2125

server/create-user.route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {db} from "./database";
44
import * as argon2 from 'argon2';
55
import {validatePassword} from "./password-validation";
66
import moment = require("moment");
7-
import {createSessionToken} from "./security.utils";
7+
import {createCsrfToken, createSessionToken, randomBytes} from "./security.utils";
88

99

1010

@@ -34,8 +34,12 @@ async function createUserAndSession(res:Response, credentials) {
3434

3535
const sessionToken = await createSessionToken(user.id);
3636

37+
const csrfToken = await createCsrfToken();
38+
3739
res.cookie("SESSIONID", sessionToken, {httpOnly:true, secure:true});
3840

41+
res.cookie("CSRF-TOKEN", csrfToken);
42+
3943
res.status(200).json({id:user.id, email:user.email});
4044
}
4145

server/csrf.middleware.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
3+
4+
import {Request, Response, NextFunction} from 'express';
5+
6+
7+
8+
export function checkCsrfToken(req: Request, res: Response, next: NextFunction) {
9+
10+
const csrfCookie = req.cookies["CSRF-TOKEN"];
11+
12+
const csrfHeader = req.headers["x-csrf-token"];
13+
14+
if (csrfCookie && csrfHeader && csrfCookie === csrfHeader) {
15+
next();
16+
}
17+
else {
18+
console.log("Failed CSRF check ...");
19+
res.sendStatus(403);
20+
}
21+
22+
}
23+

server/login.route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {db} from "./database";
55
import * as argon2 from 'argon2';
66
import {User} from "../src/app/model/user";
77
import {DbUser} from "./db-user";
8-
import {createSessionToken, randomBytes} from "./security.utils";
8+
import {createCsrfToken, createSessionToken, randomBytes} from "./security.utils";
99

1010

1111

@@ -32,8 +32,12 @@ async function loginAndBuildResponse(credentials:any, user:DbUser, res: Respons
3232

3333
console.log("Login successful");
3434

35+
const csrfToken = createCsrfToken();
36+
3537
res.cookie("SESSIONID", sessionToken, {httpOnly:true, secure:true});
3638

39+
res.cookie("CSRF-TOKEN", csrfToken);
40+
3741
res.status(200).json({id:user.id, email:user.email});
3842

3943

server/logout.route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export function logout(req: Request, res: Response) {
99
const sessionId = req.cookies['SESSIONID'];
1010

1111
res.clearCookie("SESSIONID");
12+
res.clearCookie("CSRF-TOKEN");
1213

1314
res.sendStatus(200);
1415
}

server/security.utils.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const util = require('util');
66
const crypto = require('crypto');
77
import * as jwt from 'jsonwebtoken';
88
import * as fs from "fs";
9+
import {Response} from "express";
910

1011

1112

@@ -18,6 +19,8 @@ const RSA_PUBLIC_KEY = fs.readFileSync('./demos/public.key');
1819
export const randomBytes = util.promisify(crypto.randomBytes);
1920

2021

22+
const SESSION_DURATION = 240;
23+
2124

2225
export async function createSessionToken(userId:number) {
2326
return jwt.sign(
@@ -26,7 +29,7 @@ export async function createSessionToken(userId:number) {
2629
RSA_PRIVATE_KEY,
2730
{
2831
algorithm: 'RS256',
29-
expiresIn: 120,
32+
expiresIn: SESSION_DURATION,
3033
subject: '' + userId
3134
});
3235
}
@@ -43,8 +46,9 @@ export async function isSessionTokenValid(sessionToken:string) {
4346

4447

4548

46-
47-
49+
export async function createCsrfToken() {
50+
return randomBytes(32).then(bytes => bytes.toString('hex'));
51+
}
4852

4953

5054

server/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {getUser} from "./get-user.route";
1010
import {logout} from "./logout.route";
1111
import {login} from "./login.route";
1212
import {checkIfAuthenticated, retrieveUserIdFromRequest} from "./auth.middleware";
13+
import {checkCsrfToken} from "./csrf.middleware";
1314
const bodyParser = require('body-parser');
1415
const cookieParser = require('cookie-parser');
1516

@@ -41,7 +42,7 @@ app.route('/api/user')
4142
.get(getUser);
4243

4344
app.route('/api/logout')
44-
.post(checkIfAuthenticated, logout);
45+
.post(checkIfAuthenticated, checkCsrfToken, logout);
4546

4647
app.route('/api/login')
4748
.post(login);

src/app/app.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BrowserModule } from '@angular/platform-browser';
22
import { NgModule } from '@angular/core';
3-
import {HttpClientModule} from '@angular/common/http';
3+
import {HttpClientModule, HttpClientXsrfModule} from '@angular/common/http';
44

55
import { AppComponent } from './app.component';
66
import { LessonsComponent } from './lessons/lessons.component';
@@ -33,6 +33,10 @@ import 'rxjs/add/observable/of';
3333
imports: [
3434
BrowserModule,
3535
HttpClientModule,
36+
HttpClientXsrfModule.withOptions({
37+
cookieName: 'CSRF-TOKEN',
38+
headerName: 'x-csrf-token',
39+
}),
3640
RouterModule.forRoot(routesConfig),
3741
ReactiveFormsModule
3842
],

0 commit comments

Comments
 (0)