fix #444: check bearer token on /hibp/notify

This commit is contained in:
Luke Crouch 2018-09-24 12:05:03 -05:00
Родитель 700dc29f4a
Коммит bf446223ad
7 изменённых файлов: 45 добавлений и 2 удалений

Просмотреть файл

@ -53,3 +53,5 @@ HIBP_KANON_API_TOKEN=""
HIBP_THROTTLE_DELAY=2000
# Max number of times to try an HIBP request before throwing error
HIBP_THROTTLE_MAX_TRIES=5
# Authorization token for HIBP to present to /hibp/notify endpoint
HIBP_NOTIFY_TOKEN="unsafe-default-token-for-dev"

Просмотреть файл

@ -33,6 +33,7 @@ const kEnvironmentVariables = [
"HIBP_RELOAD_BREACHES_TIMER",
"HIBP_THROTTLE_DELAY",
"HIBP_THROTTLE_MAX_TRIES",
"HIBP_NOTIFY_TOKEN",
"DATABASE_URL",
"SERVER_URL",
"DELETE_UNVERIFIED_SUBSCRIBERS_TIMER",

Просмотреть файл

@ -13,6 +13,10 @@ const log = mozlog("controllers.hibp");
async function notify (req, res) {
if (!req.token || req.token !== AppConstants.HIBP_NOTIFY_TOKEN) {
const errorMessage = "HIBP notify endpoint requires valid authorization token.";
throw new Error(errorMessage);
}
if(!["breachName", "hashPrefix", "hashSuffixes"].every(req.body.hasOwnProperty, req.body)) {
throw new Error("Breach notification requires breachName, hashPrefix, and hashSuffixes.");
}

5
package-lock.json сгенерированный
Просмотреть файл

@ -2883,6 +2883,11 @@
}
}
},
"express-bearer-token": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/express-bearer-token/-/express-bearer-token-2.2.0.tgz",
"integrity": "sha512-w4A62k6modqSlE/CJzurj2YwisoVHWqrXiNs5P+CZD7MFORbcsOBbpjD3SGGKEW44R46Kps3QopaICE6p7dORw=="
},
"express-handlebars": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz",

Просмотреть файл

@ -16,6 +16,7 @@
"dompurify": "^1.0.4",
"dotenv": "^5.0.1",
"express": "^4.16.2",
"express-bearer-token": "^2.2.0",
"express-handlebars": "^3.0.0",
"git-rev-sync": "^1.12.0",
"got": "^8.3.1",

Просмотреть файл

@ -3,6 +3,8 @@
const express = require("express");
const bodyParser = require("body-parser");
const bearerToken = require("express-bearer-token");
const {asyncMiddleware} = require("../middleware");
const {notify, breaches} = require("../controllers/hibp");
@ -10,6 +12,7 @@ const {notify, breaches} = require("../controllers/hibp");
const router = express.Router();
const jsonParser = bodyParser.json();
router.use("/notify", bearerToken());
router.post("/notify", jsonParser, asyncMiddleware(notify));
router.get("/breaches", jsonParser, asyncMiddleware(breaches));

Просмотреть файл

@ -1,5 +1,6 @@
"use strict";
const AppConstants = require("../../app-constants");
const HIBPLib = require("../../hibp");
const hibp = require("../../controllers/hibp");
const EmailUtils = require("../../email-utils");
@ -9,6 +10,32 @@ const { testBreaches } = require("../test-breaches");
require("../resetDB");
test("notify POST without token should throw error", async() => {
const testEmail = "victim@spoofattack.com";
const testHash = sha1(testEmail);
const testPrefix = testHash.slice(0, 6).toUpperCase();
const testSuffix = testHash.slice(6).toUpperCase();
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "SomeSensitiveBreach" } };
const mockResponse = { status: jest.fn(), json: jest.fn() };
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("HIBP notify endpoint requires valid authorization token.");
});
test("notify POST with invalid token should throw error", async() => {
const testEmail = "victim@spoofattack.com";
const testHash = sha1(testEmail);
const testPrefix = testHash.slice(0, 6).toUpperCase();
const testSuffix = testHash.slice(6).toUpperCase();
const mockRequest = { token: "token-that-doesnt-match-AppConstants", body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "SomeSensitiveBreach" } };
const mockResponse = { status: jest.fn(), json: jest.fn() };
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("HIBP notify endpoint requires valid authorization token.");
});
test("notify POST with breach, subscriber hash prefix and suffixes should call sendEmail and respond with 200", async () => {
jest.mock("../../email-utils");
EmailUtils.sendEmail = jest.fn();
@ -19,7 +46,7 @@ test("notify POST with breach, subscriber hash prefix and suffixes should call s
HIBPLib.getUnsafeBreachesForEmail = jest.fn();
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: testBreaches } } };
const mockRequest = { token: AppConstants.HIBP_NOTIFY_TOKEN, body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: testBreaches } } };
const mockResponse = { status: jest.fn(), json: jest.fn() };
await hibp.notify(mockRequest, mockResponse);
@ -47,7 +74,7 @@ test("notify POST with unknown breach should throw error", async () => {
const testPrefix = testHash.slice(0, 6).toUpperCase();
const testSuffix = testHash.slice(6).toUpperCase();
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: [] } } };
const mockRequest = { token: AppConstants.HIBP_NOTIFY_TOKEN, body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: [] } } };
const mockResponse = { status: jest.fn(), json: jest.fn() };
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("Unrecognized breach: test");