Encryption in javascript frontend (browser env)

73 Views Asked by At

I am trying to encrypt my password in the frontend (javascript)before I send it to the backend (fastapi, python) Although many Google searches said crypto can be used, I saw that it works only in the nodejs environment and not in the browser environment

Is there anyway I can achieve encryption in my js frontend so that decryption can be done in the backend?

I see some articles on crypto-js which requires me to install it separately, but apparently we do not have permission to use cryptojs so that had to be ruled out.

Tried using crypto but that did not seem to work

2

There are 2 best solutions below

0
Olaf Kock On

Use https, problem solved.

The magic in encryption is not the encryption itself, but trusting that you encrypt for the expected receiver. In-browser-encryption can not add any more trust in using the right key than what https provides: the server would need to provide its public key and you have no way to trust it to be the correct key.

Not what you wanted to hear, but that time/work is better spent where it makes a difference.

7
guest271314 On

You can use Web Cryptography API.

Evidently people on this board are not generally aware of the fact that secure curves using subtle is implemented in browsers. See Secure Curves in the Web Cryptography API and Secure Curves in WebCrypto for usage.

You can use the same code in the browser by substituting writing and reading files with <input type="file">, <a> element or other options such as WICG File System Access API for reading and writing files locally, and/or use fetch(), WebTransport, WebRTC Data Channels, Web Torrent or other means to send and receive encrypted and decrypted signals over the network.

The below JavaScript run in Chromium 124 generates private and public keys, exports to "jwk" format, signs some arbitrary data using the private key, then imports the keys as JSON, and verifies the data signed.

<!DOCTYPE html>

<html>
  <head> </head>

  <body>
    <h1>
      Web Cryptography API - Generate, export, import private and publick keys
    </h1>
    <button id="generate-keys">Generate, export</button>
    <button id="import-keys">Import</button>
    <script type="module">
      const generateKeys = document.querySelector("[id='generate-keys']");
      const importKeys = document.querySelector("[id='import-keys']");
      const algorithm = { name: 'Ed25519' };
      const encoder = new TextEncoder();
      const decoder = new TextDecoder();
      // Some message to be signed.
      const data = encoder.encode('Some message');
      let privateKey, publicKey, signature, verified;
      generateKeys.addEventListener('click', async (e) => {
        try {
          const cryptoKey = await crypto.subtle.generateKey(
            algorithm,
            true /* extractable */,
            ['sign', 'verify']
          );
          // Export public and private keys as JSON
          privateKey = JSON.stringify(
            await crypto.subtle.exportKey('jwk', cryptoKey.privateKey)
          );
          publicKey = JSON.stringify(
            await crypto.subtle.exportKey('jwk', cryptoKey.publicKey)
          );

          // Pass a string or an object with only the name property, as above.

          signature = await crypto.subtle.sign(
            algorithm.name,
            cryptoKey.privateKey,
            data
          );

          console.log(privateKey, publicKey, signature);
        } catch (e) {
          console.error(e);
          console.trace();
        }
      });
      importKeys.addEventListener('click', async (e) => {
        try {
          // https://github.com/tQsW/webcrypto-curve25519/blob/master/explainer.md
          const cryptoKey = {
            privateKey: await crypto.subtle.importKey(
              'jwk',
              JSON.parse(privateKey),
              algorithm.name,
              true,
              ['sign']
            ),
            publicKey: await crypto.subtle.importKey(
              'jwk',
              JSON.parse(publicKey),
              algorithm.name,
              true,
              ['verify']
            ),
          };

          verified = await crypto.subtle.verify(
            algorithm.name,
            cryptoKey.publicKey,
            signature,
            data
          );

          console.log(cryptoKey, verified);
        } catch (e) {
          console.error(e);
          console.trace();
        }
      });
    </script>
  </body>
</html>

I have generated, exported, and imported using the same code in node, deno, and bun, and in the browser.

Make sure you understand node:crypto is not Web Cryptography API, it's Node.js' internal implementation that cannot be polyfilled. To access webcrypto in node, deno, and bun, use something like import {webcrypto} from node:crypto.