Merge pull request #146 from mozilla/hash-email-on-client-143
fix #143: hash email client-side before scanning
This commit is contained in:
Коммит
237706c39d
4
hibp.js
4
hibp.js
|
@ -5,17 +5,15 @@ const got = require("got");
|
|||
const AppConstants = require("./app-constants");
|
||||
const DBUtils = require("./db/utils");
|
||||
const pkg = require("./package.json");
|
||||
const getSha1 = require("./sha1-utils");
|
||||
|
||||
|
||||
const HIBP_USER_AGENT = `${pkg.name}/${pkg.version}`;
|
||||
|
||||
|
||||
const HIBP = {
|
||||
async getBreachesForEmail(email) {
|
||||
async getBreachesForEmail(sha1) {
|
||||
let foundBreaches = [];
|
||||
|
||||
const sha1 = getSha1(email);
|
||||
const sha1Prefix = sha1.slice(0, 6);
|
||||
const url = `${AppConstants.HIBP_STAGE_API_ROOT}/breachedaccount/range/${sha1Prefix}?code=${encodeURIComponent(AppConstants.HIBP_STAGE_API_TOKEN)}`;
|
||||
const headers = {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
function doXHR(aURL, aBodyObj, aAlertText, aDebug=true) {
|
||||
return new Promise((resolve) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
@ -25,42 +26,21 @@ function doXHR(aURL, aBodyObj, aAlertText, aDebug=true) {
|
|||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function addUser() {
|
||||
doXHR("/user/add",
|
||||
{ email: document.getElementById("addUserField").value })
|
||||
.then(function() {
|
||||
alert("A verification link has been emailed to the specified address.");
|
||||
});
|
||||
function isValidEmail(val) {
|
||||
// https://stackoverflow.com/a/46181
|
||||
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(String(val).toLowerCase());
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function removeUser() {
|
||||
doXHR("/user/remove",
|
||||
{ email: document.getElementById("removeUserField").value });
|
||||
function enableBtnIfEmailValid(e) {
|
||||
const emailBtn = document.getElementById("subscribe-email-btn");
|
||||
if (isValidEmail(e.target.value)) {
|
||||
emailBtn.disabled = false;
|
||||
} else {
|
||||
emailBtn.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
// function doOauth() {
|
||||
// window.open("/oauth/init");
|
||||
// }
|
||||
|
||||
// function isValidEmail(val) {
|
||||
// // https://stackoverflow.com/a/46181
|
||||
// const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
// return re.test(String(val).toLowerCase());
|
||||
// }
|
||||
|
||||
// function enableBtnIfEmailValid(e) {
|
||||
// const emailBtn = document.getElementById("subscribe-email-btn");
|
||||
// if (isValidEmail(e.target.value)) {
|
||||
// emailBtn.disabled = false;
|
||||
// } else {
|
||||
// emailBtn.disabled = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
*/
|
||||
|
||||
function showFalseDoor(){
|
||||
const falseDoorBlurb = "<div class='section-container'><h4>Thank you for trying Firefox Monitor</h4><p>FireFox Monitor is a concept we are testing. We hope to provide the service to everyone soon.</p><p>Stay up-to-date with Firefox Monitor and other new features when you sign up for the <a href='https://www.mozilla.org/newsletter/firefox/'>Firefox newsletter.</a></p><button class='button' id='close-false-door'>Close</button></div>";
|
||||
|
@ -75,9 +55,25 @@ function showFalseDoor(){
|
|||
|
||||
}
|
||||
|
||||
async function sha1(message) {
|
||||
const msgBuffer = new TextEncoder("utf-8").encode(message);
|
||||
const hashBuffer = await crypto.subtle.digest("SHA-1", msgBuffer);
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
const hashHex = hashArray.map(b => ("00" + b.toString(16)).slice(-2)).join("");
|
||||
return hashHex;
|
||||
}
|
||||
|
||||
async function hashEmailAndSend(emailFormSubmitEvent) {
|
||||
emailFormSubmitEvent.preventDefault();
|
||||
const emailForm = emailFormSubmitEvent.target;
|
||||
for (const emailInput of emailForm.querySelectorAll("input[type=email]")) {
|
||||
emailForm.querySelector("input[name=emailHash]").value = await sha1(emailInput.value);
|
||||
emailInput.value = "";
|
||||
}
|
||||
emailForm.submit();
|
||||
}
|
||||
|
||||
|
||||
document.querySelector(".email-scan").addEventListener("submit", hashEmailAndSend);
|
||||
$(document).foundation();
|
||||
|
||||
document.querySelector("#sign-up").addEventListener("click", showFalseDoor);
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>blurts-server testing UI</title>
|
||||
<script src="/js/test.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="text" id="addUserField" /><input type="submit" value="Add User" onclick="addUser()" /><br/>
|
||||
<input type="submit" value="Add User Using FxA" onclick="doOauth()"/><br/>
|
||||
<input type="text" id="removeUserField" /><input type="submit" value="Remove User" onclick="removeUser()" /><br/>
|
||||
<br/>
|
||||
<h1>Response dump:</h1>
|
||||
<div id="responseDump" />
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
|
||||
const DBUtils = require("../db/utils");
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
const urlEncodedParser = bodyParser.urlencoded({ extended: false });
|
||||
|
||||
router.get("/api/v3/breachedaccount/range/:hashPrefix", urlEncodedParser, async (req, res) => {
|
||||
const hashPrefix = req.params.hashPrefix;
|
||||
|
||||
const foundEntries = await DBUtils.getBreachesForHashPrefix(hashPrefix);
|
||||
|
||||
if (!foundEntries.length) {
|
||||
res.status(404).send("Not Found");
|
||||
} else {
|
||||
res.render("range", {foundEntries});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -12,25 +12,24 @@ const urlEncodedParser = bodyParser.urlencoded({ extended: false });
|
|||
|
||||
|
||||
router.post("/", urlEncodedParser, async (req, res) => {
|
||||
const email = req.body.email;
|
||||
const emailHash = req.body.emailHash;
|
||||
let foundBreaches = [];
|
||||
|
||||
if (!req.session.scanResults) {
|
||||
req.session.scanResults = {};
|
||||
}
|
||||
|
||||
if (email) {
|
||||
if (req.session.scanResults[email]) {
|
||||
foundBreaches = req.session.scanResults[email];
|
||||
if (emailHash) {
|
||||
if (req.session.scanResults[emailHash]) {
|
||||
foundBreaches = req.session.scanResults[emailHash];
|
||||
} else {
|
||||
foundBreaches = await HIBP.getBreachesForEmail(email);
|
||||
req.session.scanResults[email] = foundBreaches;
|
||||
foundBreaches = await HIBP.getBreachesForEmail(emailHash);
|
||||
req.session.scanResults[emailHash] = foundBreaches;
|
||||
}
|
||||
}
|
||||
|
||||
res.render("scan", {
|
||||
title: "Firefox Breach Alerts: Scan Results",
|
||||
email: email,
|
||||
foundBreaches: foundBreaches,
|
||||
});
|
||||
});
|
||||
|
|
18
server.js
18
server.js
|
@ -7,15 +7,14 @@ const hbs = require("express-hbs");
|
|||
const helmet = require("helmet");
|
||||
const sessions = require("client-sessions");
|
||||
|
||||
const EmailUtils = require("./email-utils");
|
||||
// const EmailUtils = require("./email-utils");
|
||||
const HBSHelpers = require("./hbs-helpers");
|
||||
|
||||
const DockerflowRoutes = require("./routes/dockerflow");
|
||||
const HIBPRoutes = require("./routes/hibp-stubs");
|
||||
const HomeRoutes = require("./routes/home");
|
||||
const OAuthRoutes = require("./routes/oauth");
|
||||
const ScanRoutes = require("./routes/scan");
|
||||
const UserRoutes = require("./routes/user");
|
||||
// const OAuthRoutes = require("./routes/oauth");
|
||||
// const UserRoutes = require("./routes/user");
|
||||
|
||||
|
||||
const app = express();
|
||||
|
@ -70,15 +69,14 @@ app.use(sessions({
|
|||
|
||||
app.use("/", HomeRoutes);
|
||||
app.use("/", DockerflowRoutes);
|
||||
app.use("/hibp", HIBPRoutes);
|
||||
app.use("/oauth", OAuthRoutes);
|
||||
app.use("/scan", ScanRoutes);
|
||||
app.use("/user", UserRoutes);
|
||||
// app.use("/oauth", OAuthRoutes);
|
||||
// app.use("/user", UserRoutes);
|
||||
|
||||
EmailUtils.init().then(() => {
|
||||
// EmailUtils.init().then(() => {
|
||||
const listener = app.listen(AppConstants.PORT, () => {
|
||||
console.info(`Listening on ${listener.address().port}`);
|
||||
});
|
||||
}).catch(error => {
|
||||
/* }).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}); */
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec volutpat hendrerit !nibus.
|
||||
</p>
|
||||
<br>
|
||||
<form action="/scan" method="post">
|
||||
<form action="/scan" class="email-scan" method="post">
|
||||
<div class="input-group">
|
||||
<input class="input-group-field email-to-hash" type="email" name="email">
|
||||
<input class="input-group-field email-to-hash" type="email">
|
||||
<input type="hidden" name="emailHash">
|
||||
<div class="input-group-button">
|
||||
<input id="submit-email" type="submit" class="button" value="Scan">
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<link rel="stylesheet" href="/css/app.css">
|
||||
<script type="text/javascript" src="/js/vendor/jquery.js" defer></script>
|
||||
<script type="text/javascript" src="/js/vendor/foundation.min.js" defer></script>
|
||||
<script type="text/javascript" src="/js/test.js" defer></script>
|
||||
<script type="text/javascript" src="/js/monitor.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class ="top-bar">
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
{{else}}
|
||||
<p class="demi">The first step to keeping your online accounts safe is knowing what you’re up against. Enter your email to find out if your accounts have been compromised.</p>
|
||||
{{/if}}
|
||||
<form action="/scan" method="post">
|
||||
<form action="/scan" class="email-scan" method="post">
|
||||
<div class="input-group">
|
||||
<input class="input-group-field email-to-hash" type="email" name="email" placeholder="Enter Email Address">
|
||||
<input class="input-group-field email-to-hash" type="email" placeholder="Enter Email Address">
|
||||
<input type="hidden" name="emailHash">
|
||||
<div class="input-group-button">
|
||||
<img src="img/Search.svg" alt="search icon" />
|
||||
<input id="submit-email" type="submit" class="button" value="Search">
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
<span class="bold">Subscribe</span> for alerts from Firefox Monitor to learn sooner about your compromised accounts.
|
||||
</li>
|
||||
<li>
|
||||
<form action="/scan" method="post">
|
||||
<form action="/scan" class="email-scan" method="post">
|
||||
<label class="medium">Scan another email address</label>
|
||||
<div class="input-group">
|
||||
<input class="input-group-field email-to-hash" type="email" name="email" placeholder="Enter Email">
|
||||
<input class="input-group-field email-to-hash" type="email" placeholder="Enter Email">
|
||||
<input type="hidden" name="emailHash">
|
||||
<div class="input-group-button">
|
||||
<img src="img/Search.svg" alt="search icon" />
|
||||
<input id="submit-email" type="submit" class="button" value="">
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{{#if foundEntries }}
|
||||
{{#each foundEntries }}
|
||||
{{ sha1 }}:{{#each breaches }}{{ name }}{{#unless @last}},{{/unless}}{{/each}}
|
||||
{{/each }}
|
||||
{{/if }}
|
Загрузка…
Ссылка в новой задаче