Password Breach Check - New Feature, New Compromise Defense

Password Breach Check - New Feature, New Compromise Defense

Adding a new feature to my File Encryption App made in Electron using Pangea.

·

5 min read

You can check out this blog on how I created the app using Electron, but here, I'll discuss how I added the new feature with the help of Pangea's simple-to-use APIs.


Pangea User Intel API

The User Intel API allows you to check a large repository of breached data to see if a user's PII or credentials have been compromised. In my case, I am going to use the breached password API.

Pangea offers a convenient way to check if a password has been compromised by leveraging third-party providers like SpyCloud. SpyCloud boasts the world's largest collection of recaptured data, including user passwords, addresses, phone numbers, and more.

In my specific scenario, I only need to verify if the password used for file encryption has been compromised. Pangea simplifies this process by providing an API endpoint. This endpoint allows users to query a vast repository of breach data, checking whether a password has been compromised.

https://user-intel.aws.eu.pangea.cloud/v1/password/breached

Getting started

To get started, Create a free account on Pangea, which offers $5 in credit each month, to try out their APIs.

Pangea provides multiple APIs that users can enable based on their specific requirements. Below is what the dashboard looks like once you have signed up.

Click on User Intel (in red) to enable the service and create your token.
Once we have our token, we can test it out on the User Intel API Reference page. This is essentially the documentation and playground where you can test different APIs provided by Pangea with the help of a nice UI for various options.

To determine if a password has been exposed in a security breach, we just have to send a POST request to the API using our token.

const respone = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_TOKEN}`,
    },
    body: JSON.stringify({
      hash_type: "sha256",
      hash_prefix: "5baa6",
      raw: true, // optional
      provider: "spycloud", // optional
    }),
  });
  const data = await respone.json();

Pangea's password compromise check is conducted through the use of the hash_prefix parameter. This parameter requires the first 5 characters of the password hash, ensuring that the plaintext password is never transmitted.
The supported hash types include NTLM, SHA1, SHA256, or SHA512.

The data returned from the API shows if the password has been breached and the total number of instances of the prefix found in the breached data.

{
  "request_id": "prq_udiimi7imbnsf4q6x6k5758rnf7d5s5k",
  "request_time": "2023-11-13T10:11:49.289553Z",
  "response_time": "2023-11-13T10:11:50.113671Z",
  "status": "Success",
  "summary": "Password was found in breach dataset",
  "result": {
    "data": {
      "found_in_breach": true,
      "breach_count": 16236
    }
  }
}

Adding the Feature

The above video shows how the feature works. Let's get into the code.

First, we need to create a function that converts the password into a hash.

// app/backend/generateSHA256Hash.js
const nodeCrypto = require("crypto");

function generateSHA256Hash(password) {
  const hash = nodeCrypto.createHash("sha256").update(password).digest("hex");
  return hash;
}

module.exports.generateSHA256Hash = generateSHA256Hash;

Electron has 2 types of processes, the Main process and the Renderer process. The entry point of an Electron app is its Main process which is a NodeJS environment that has full OS access and can use the built-in NodeJS modules, while the Renderer process controls the UI using HTML, CSS and JavaScript, thus cannot access the NodeJS modules like crypto that we used above.

With the help of the Preload script, we can bridge the NodeJS code to the Renderer process and expose any functionality or APIs using contextBridge from electron, and to access it we use the window Object.

// app/electron/preload.js
const { generateSHA256Hash } = require("../backend/generateSHA256Hash.js");

contextBridge.exposeInMainWorld("generateSHA256Hash", generateSHA256Hash);

Netlify Functions to hide API keys

In an Electron app, the only way to secure your API key is to place it behind a server proxy to handle API requests on behalf of the client. This is where Netlify functions come into play, and is quite easy to set up.

First, create a new react project, and then create a functions folder in the root directory. All the functions that you write here will be available as API endpoints.

The below function is what I created that uses .env - a common practice to keep API Keys secure in your projects.

💡
If you have used Vite to create the react app, make sure to add VITE before your token name.
// getUserIntelPassword.js
export default async (event) => {
  const hashPrefix = event.url.split("").slice(-5).join("");
  const API_URL = "https://user-intel.aws.eu.pangea.cloud/v1/password/breached";
  const API_TOKEN = process.env.VITE_PANGEA_USER_INTEL_API_TOKEN;

  const respone = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_TOKEN}`,
    },
    body: JSON.stringify({
      provider: "spycloud",
      hash_type: "sha256",
      hash_prefix: hashPrefix,
      raw: true,
    }),
  });
  const data = await respone.json();

  return Response.json(data);
};

The purpose of event.url.split("").slice(-5).join("") is to extract the last 5 characters from the URL provided in the event object.
After hosting the app, if you visit the URL -
https://your-app-name.netlify.app/.netlify/functions/getUserIntelPassword?hash=jf7e9
It will extract the last 5 characters jf7e9 which will serve as the hash_prefix to be sent to the API.

Next, create a netlify.toml file to let Netlify know where your functions are located.

[build]
    functions = "functions"

Now host the project on Netlify and just add the environment variables in the Site configuration settings like below.

Now I get the same result as the User Intel API by just visiting the below link without revealing my token.

https://pangea-cloud-api.netlify.app/.netlify/functions/getUserIntelPassword?hash=5aeb6

Using the API in the app

Now that I have my API link as a proxy that requests data from Pangea's User Intel Service, it's just simple JavaScript to add the functionality at the click of a button.

import { noPasswordInput } from "./errors/noPasswordInput.js";
import { checkingAnimation } from "../js/passwordCheck/checking.js";
import { hashFoundAnimation } from "../js/passwordCheck/hashFound.js";
import { hashNotFoundAnimation } from "../js/passwordCheck/hashNotFound.js";
import { errorAnimation } from "../js/passwordCheck/error.js";

const checkPasswordBtn = document.querySelector("#check-password-btn");
const passwordInputBox = document.querySelector("#password-input");

checkPasswordBtn.addEventListener("click", async () => {
  if (passwordInputBox.value.length !== 0) {
    const sha256Hash = window.generateSHA256Hash(passwordInputBox.value);
    const API_URL =
      "https://pangea-cloud-api.netlify.app/.netlify/functions/getUserIntelPassword?hash=" +
      sha256Hash.slice(0, 5);
    checkingAnimation();

    try {
      const respone = await fetch(API_URL);
      const data = await respone.json();
      const raw_data = data?.result?.raw_data;
      const hashesFound = Object.keys(raw_data);
      const ifHashFound = hashesFound.some((hash) => hash === sha256Hash);
      if (ifHashFound) {
        hashFoundAnimation(data);
      } else {
        hashNotFoundAnimation();
      }
    } catch {
      errorAnimation();
    }
  } else {
    noPasswordInput();
  }
});

The above code is pretty self-explanatory, including animations for different stages like checking, hashFound, hashNotFound and error, based on the API response.


Conclusion

A simple and useful feature added with the help of a simple-to-use API from Pangea, and thanks to Hashnode for hosting the hackathon.
Do check out other services by Pangea, which are just an API call away, and also download my app to see the feature in action.

Did you find this article valuable?

Support Abhishek by becoming a sponsor. Any amount is appreciated!