Cryptography and how to deal with man-in-the-middle attacks in JavaScript



This is post # 24 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript application that needs to be robust and high-performing to help companies optimize the digital experience of their users.

Cybersecurity is an important domain in the IT field. Numerous people communicate with each other daily from around the world via the internet. As people communicate over the internet, there’s a possibility that others can eavesdrop or even hijack information before it gets to the other parties involved. Also, there are possibilities of stolen personal data of users by hackers who exploit the vulnerabilities in computer networks.

How then can people securely send information over the internet and what role does JavaScript play? If you’re curious to find out, then this article is for you.

In this article, you will learn what cryptography is, how it works in JavaScript, and how to deal with man in the middle attack (MitM).

Cryptography is the process of securing information and communication so that only the sender and intended receiving party can access them. Cryptography constitutes various technologies to secure communication properly. These technologies can include encryption and decryption with the use of ciphers and deciphers, the use of various algorithms to hash communication processes, or signature generation and verification.

Since a lot of people converse over the internet via mobile applications built with JavaScript, there’s a need to understand how cryptography works in JavaScript. In the next section, we’ll be looking at JavaScript’s Web Cryptography API and how it supports cryptography.

Since ensuring a secure communication over the internet is important, some web browsers have implemented the crypto interface. However, this interface isn’t well defined or cryptographically sound. JavaScript’s web cryptography API provides a well-defined interface called SubtleCrypto.

JavaScript’s web cryptography API allows developers to incorporate basic cryptographic functions into their application without the need for a third-party library. You can sign documents, perform authentication and perform an overall integrity check of a communication.

For example, you can obtain cryptographically secure random data for an array of 8-bit unsigned integers by running the following codes:

You can run this code on your web console. For instance, if I run this code on Chrome’s console, I’ll get an output of ten 8-bit randomly generated numbers that are unsigned.

Let’s look at how JavaScript’s web cryptography API works and how we were able to pull this off from our web console.

With JavaScript’s web cryptography API in place, the server can’t see data since it’s cryptographically secure. Only the sender and receiver have access to communication data.

From the diagram above, you can see that data from the sender is encrypted with the API. The receiver uses a key to decrypt data, the server and database can’t decipher encrypted data. You can perform basic cryptographic operations such as Hashing, Signature Generation and Verification, Encrypting, and Decrypting which will be discussed further in this article.

There are numerous cryptographic functions that you can perform with JavaScript’s web cryptography API. In the section, we’ll be looking at basic cryptographic functions like Hashing, Signature Generation and Verification, Encrypting, and Decrypting.


Encrypting is one of the basic cryptographic functions. In encryption, a message in human language (plaintext) is converted to computer language (ciphertext) with a key. For the receiver to understand the message from the sender, they must use a key.

The encryption process in JavaScript’s web cryptography API uses the encrypt method. The encrypt method has the following syntax:

The encrypt method will return a Promise that fulfills with an ArrayBuffer containing the “ciphertext”. It is better to return a new Promise rejected with the result of normalizing an algorithm if an error occurs during encryption. To understand this better, we’ll encrypt a plain text with the AES-GCM key and algorithm. Copy the code below in your web browser and notice that the output is a ciphered text.

From the code,

specifies the key, while

specifies the algorithm. function strToArrayBuffer converts plaintext to ciphertext since the promise to be fulfilled needs to be an ArrayBuffer with the ciphertext.

Decrypting is the reverse of encryption. In decryption, the ciphertext is converted back to plain text. To do this, a key (crypto) from an authorized user is required. This is like trying to access a locked building with a key. You won’t be let into the building just because you have some bunch of keys. The only way you’ll be allowed in is if the key is a match (can open the building).

The decryption process in JavaScript’s web cryptography API uses the decrypt method. Below is the decrypt syntax.

So, the decrypting syntax from our example ciphertext is shown below.

However, we’ll have to encrypt and decrypt our text as these methods go together. The complete code for encrypting and decrypting our sample text is shown below.

When you run this program, it’ll output the plaintext that was previously encrypted.

From the code,

generates a secret key to decrypt the message.

The encryption process is broadly divided into symmetrical and asymmetrical encryption. This division is dependent on the type of key used for decryption. For symmetric encryption, the same key is used for both encryption and decryption. While, for asymmetric encryption, different key pairs are used for encryption and decryption. A public key that is shared by authorized users is used for encryption whereas a secret key from the recipient is used for decrypting. The secret key is kept secret by their owners.

Hashing is a cryptographic function, that allows you to map data of arbitrary size to an array of fixed size. The cryptographic hash function converts data from plaintext into a unique string of numbers and alphabets. Hashing isn’t like encryption, it is a one-way function. This means it is difficult and almost impossible to get the original text from a hashed value.

Hashing makes use of a mathematical algorithm to convert plain text into hashed values. There’s no key that deciphers a hashed value like in encryption. The cryptographic hash function is mostly used for authentication. For example, in registration/login. When users register, their passwords are hashed before storing them in the database. Whenever the user tries to log in, their password is hashed and compared with the hashed values in the database to ensure it matches. With this method, if an attacker hijacks the database of a software firm, user’s login details are useless for them as they can’t decode or understand the passwords.

JavaScript’s web cryptography API provides the crypto.subtle.digest function which allows you to perform a cryptographic hash function. With the crypto.subtle.digest function, you can hash plaintext with SHA-1, SHA-384 or, SHA-512 algorithm, using the following syntax.

To understand how to hash messages with the crypto.subtle.digest function, let’s look at the example below:

From the example program above, we hashed the text This is a secure message from Mary with the SHA-256 algorithm and converted the bytes to hex string. The algorithm SHA-1 is now vulnerable; therefore, it’s advised not to use it in production mode.

Signature Generation and Verification

This is another cryptographic function that you can perform with JavaScript’s Web Cryptography API. With the sign and verify function, you can sign a document with a key. The receiver uses their key to validate the authentication of the message.

Let’s look at a scenario when you want to send a document over to your friend. For the document to be authentic, you need to sign it. Your friend who receives the message realizes that it’s from you when they see the signature.

To sign and verify documents with the sign and veryify function, use the following syntax.

Computers don’t have a mind of their own. Therefore, whenever we want computers to execute some tasks, we’ll let them know how to do it. For example, if you want your computer to perform basic calculations, you’ll have to tell it what numbers to add and how to do it.

Algorithms are a set of well-defined computer-implementable instructions for solving problems. In cryptography, you need to specify how you want your computer to perform a cryptographic function. There are different flavors of the SHA algorithm, each specifying the bit length of the hash value. In this section, we will look at the basic cryptographic functions that JavaScript’s Web Cryptography API supports.

ECDH (Elliptic Curve Diffie-Hellman)

This cryptographic algorithm is used for key generation and key agreement. Elliptic Curve Diffie-Hellman key agreement protocol allows two parties who have a private-public key pair to make secure communication. It is used for Elliptic Curve Diffie-Hellman cryptography.

Elliptic Curve Diffie-Hellman algorithm allows you to perform the following operations:

  • Key generation
  • Bits Derivation
  • Key Import
  • Key Export

SHA (Secure Hash Algorithm)

This algorithm is mostly used for performing hash functions. It condenses variable data into a fixed-size bit-string output. JavaScript’s Web Cryptography API allows you to perform hash functions with SHA-1 and SHA-2, giving support for “SHA-256”, “SHA-384”, “SHA-512” to use this algorithm, you’ll have to use the crypto.subtle.digest function.

HMAC (Hash-based Message Authentication Code)

After hashing a value with the hash function, you’ll need to verify that the incoming message equals the hash value. Remember that we can barely convert a hash value back to its original text. To compare values, the HMAC algorithm is used to sign and verify documents to ascertain its authenticity. With this algorithm, you can perform the following functions.

  • Sign
  • Verify
  • Generate Key
  • Import Key
  • Export Key
  • Get Key Length

HKDF (Hash-based Key Derivation Function)

Hash-based key derivation function is a cryptographic key derivation function based on HMAC. It uses the extraction-then-expansion approach. You can use this algorithm to convert shared secrets into keys suitable for encryption, integrity checking, or authentication. The operations you can perform with this algorithm are as follows:

  • Bits Derivation
  • Key Import
  • Get Key Length

PBKDF2 (Password-Based Key Derivation Function 2)

The PBKDF2 algorithm is used to derive cryptographic keys, using the PKCS#5 (Public Key Cryptography Standards #5) password-based key derivation function version 2. Just like the HKDF function, you can perform Bits Derivation, Key Import, and Get Key Length with this algorithm.

ECDSA (Elliptic Curve Digital Signature Algorithm)

The Elliptic Curve Digital Signature Algorithm allows developers to sign and verify documents, using elliptic curve cryptography. It offers a variant of the Digital Signature Algorithm (DSA). You can perform the following operations with the ECDSA algorithm.

  • Sign
  • Verify
  • Generate Key
  • Import Key
  • Export Key

RSA (Rivest–Shamir–Adleman) Algorithm

The RSA algorithm is used for securing information over the internet. It is an asymmetric cryptographic algorithm. Two keys are used for ciphering and deciphering process. One key is public and can be shared among authorized users, while the second key which is private must be kept private.

Rivest–Shamir–Adleman supports other algorithms with padding schemes that can be used with the RSA algorithm. JavaScript’s Web Cryptography API supports the following RSA algorithms and padding schemes.

  • RSASSA-PKCS1-v1_5

AES (Advanced Encryption Standard) Algorithm

The advanced encryption standard algorithm is mostly known by its original name Rijndael. It is used for the encryption of electronic data and was established by the U.S. National Institute of Standards and Technology (NIST) in 2001. JavaScript’s Web Cryptography API supports different encryption models that use the AES block, they are:

  • AES-KW

There are numerous applications of JavaScript’s Web Cryptography API in real-time cryptography. In this section, we’ll look at the different applications of cryptography in real-time.

Multi-factor Authentication

Sometimes, hackers can steal users’ passwords. So, even if these passwords are hashed or encrypted in the database, it can’t stop them from accessing a user’s account. To make sure that someone who’s accessing an account is the true owner, applications allow multi-factor authentication.

Rather than using transport-layer authentication, such as TLS client certificates application can use suitable client keys which may have been previously generated via the user agent like multifactor tokens.

Protected Document Exchange

One of the goals of cryptography is to secure communication over the internet in the presence of third parties. Because there are third parties that are constantly spying on other people’s conversations, JavaScript’s Web Cryptography API allows you to sign, verify and encrypt documents. Protecting documents require authorized users to have keys that will allow them access to the documents.

Cloud Storage

You can protect documents before uploading them to remote servers using JavaScript’s Web Cryptography API. The application may have a user select a private or secret key, derive an encryption key, encrypt the document, and then upload the encrypted data to the service provider using existing APIs.

Secure Messaging

Users can communicate securely over the internet with schemes such as off-the-record (OTR) messaging. The two parties involved in the communication can encrypt and decrypt messages using message authentication code (MAC) keys to prevent tampering.

JavaScript Object Signing and Encryption (JOSE)

You can interact with the structures and message formats defined by the JavaScript Object Signing and Encryption (JOSE) Working Group with JavaScript’s Web Cryptography API.

Man in the middle attack (MitM) can also be called machine in the middle attack or person in the middle attack. It is a cyberattack where an attacker eavesdrops on communication between two parties, to hijack or modify communication.

For example, a middle man may intercept information sent from one party before it gets to another party. Let’s look at a case where two people; Nancy and Joy are having a conversation that’s encrypted.

If a man in the middle (James) gets hold of Nancy’s key, he can decrypt the message, view or modify its content before sending it to Joy.

Let’s look at the various ways people deal with MitM attacks

  1. The use of tamper detection is an excellent method to show that a MitM attack has occurred. For instance, parties can check for discrepancies in response times.
  2. The use of mutual authentication is a method that can mitigate MitM attacks. Because the server and the client validate each other’s communication.
  3. Forensic analysis is another method for dealing with MitM attacks, as it determines whether or not there was an attack and determines the source of the attack.

Everyone needs to feel safe whenever they’re communicating over the internet. Insecure conservation not only means that your communication may be leaked on the internet, some attackers go as far as stealing from users, bullying users, and even tracing them to cause physical harm.

Developers must build products that are sustainable and safe for users. What happens when firms neglect user privacy and safety when building products?

Data protection regulation agencies regulate how individual data are processed over the internet. For example, GDPR a data regulatory agency, will fine up to €20 million (about £18 million) or 4% of annual global turnover — whichever is greater — for infringements. Therefore, firms must take every necessary step to ensure that user’s data is always secure.

This means that companies also have to be very careful about third-party tools and vendors they choose to work with. It’s very important that the tools that are being adopted by all organizations are secure and respect privacy. This is especially important for products that are heavy on data ingestion such as analytics, monitoring, error tracking, etc.

When building SessionStack, user privacy and security has been a top priority since the product collects a wide variety of data from the browser during user sessions. Once SessionStack is integrated into a web app, it starts collecting data such as DOM changes, user interactions, JavaScript exceptions, stack traces, network requests, and debug messages. This data is then processed and allows teams to replay user journeys as videos in order to optimize product workflows, reproduce bugs, or see where users are stuck.

We have built features into SessionStack that allow our customers to have full control over what data is being sent from the browser to our servers. There is even the option to mask any data on the page such as PII, which will be ignored and won’t leave the browser.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here:


Please enter your comment!
Please enter your name here