CrowdTLS/worker.js

160 lines
5.1 KiB
JavaScript
Raw Normal View History

/**
* @file worker.js
* A background script for the CrowdTLS project.
* This script is used to process web requests and retrieve security information
* for each HTTPS response.
*/
"use strict";
2023-06-07 14:36:03 -07:00
// const API_BASE = "https://crowdtls.mips.uk/api/v1";
const API_BASE = "http://127.0.0.1:8000/api/v1";
/**
* Processes the given request details to extract and log security information.
*
* @async
* @function
* @param {Object} details - The details of the request.
* @param {string} details.requestId - The unique identifier for the request.
* @throws Will throw an error if the request fails or if the security state is insecure.
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/getSecurityInfo}
*/
async function process_request(details) {
try {
2023-06-07 14:36:03 -07:00
let hostname = (new URL(details.url)).hostname;
let securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, { certificateChain: true, rawDER: true });
if (securityInfo.state !== "insecure") {
const fingerprint = securityInfo.certificates[0].fingerprint.sha256;
let fpData = localStorage.getItem(fingerprint);
const currentTime = Date.now();
if (fpData) {
fpData = JSON.parse(fpData);
// If the last request was less than 10 minutes ago, don't spam the API
if (fpData.lastCheck && currentTime - fpData.lastCheck < 10 * 60 * 1000) {
return;
}
// If the last response was less than 12 hours ago, don't check again
if (fpData.lastResponse && currentTime - fpData.lastResponse < 12 * 60 * 60 * 1000) {
return;
}
} else {
fpData = {};
}
fpData.lastCheck = currentTime;
localStorage.setItem(fingerprint, JSON.stringify(fpData));
2023-06-07 14:36:03 -07:00
await check_fingerprint(hostname, securityInfo.certificates);
}
} catch (error) {
console.error(error);
}
}
/**
* Checks the fingerprint and sends a REST GET request to the API.
*
* @async
* @function
2023-06-07 14:36:03 -07:00
* @param {string} hostname - The hostname of the website.
* @param {Array} certificates - The list of certificates.
*/
2023-06-07 14:36:03 -07:00
async function check_fingerprint(hostname, certificates) {
try {
const fingerprints = certificates.map(cert => cert.fingerprint.sha256);
const response = await fetch(`${API_BASE}/check`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
2023-06-07 14:36:03 -07:00
body: JSON.stringify({ host: hostname, fps: fingerprints }),
timeout: 5000
});
if (!response.ok) {
console.error(`Error: ${response.status}`);
return;
}
const data = await response.json();
if (data) {
if (data["send"] === true) {
2023-06-07 14:36:03 -07:00
send_certificate_chain(hostname, certificates);
}
}
}
catch (error) {
console.error(error);
}
}
/**
* Sends the certificate chain to the CrowdTLS service.
*
* @async
* @function
2023-06-07 14:36:03 -07:00
* @param {string} hostname - The hostname of the website.
* @param {Array} certificates - The list of certificates.
* @throws Will log an error to the console if the request fails.
*/
2023-06-07 14:36:03 -07:00
async function send_certificate_chain(hostname, certificates) {
try {
let chain = {};
certificates.forEach(cert => {
chain[cert.fingerprint.sha256] = cert.rawDER;
});
2023-06-07 14:36:03 -07:00
const response = await fetch(`${API_BASE}/new`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
2023-06-07 14:36:03 -07:00
body: JSON.stringify({ host: hostname, certs: chain }),
timeout: 10000
});
if (!response.ok) {
console.error(`Request returned with status ${response.status}`);
}
} catch (error) {
console.error(error);
}
}
/**
* @function
* @description Performs garbage collection on localStorage by removing objects older than 12 hours based on their "lastCheck" property.
* @returns {void}
*/
function garbage_collection() {
const now = new Date().getTime();
const twelveHoursInMillis = 12 * 60 * 60 * 1000;
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
if (value) {
const entry = JSON.parse(value);
if (entry.lastCheck && now - entry.lastCheck > twelveHoursInMillis) {
localStorage.removeItem(key);
}
}
}
}
setInterval(garbage_collection, 3600000);
/**
* The listener for HTTPS responses. Schedules the process_request() function for every response.
* Blocking is necessary to ensure that the response is not delivered before the function is completed.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onHeadersReceived}
*/
browser.webRequest.onHeadersReceived.addListener(
process_request,
{ urls: ["https://*/*"] },
["blocking"]
);