3

Javascript Destructing and Untrusted Data

 1 year ago
source link: http://www.petecorey.com/blog/2022/11/09/javascript-destructing-and-untrusted-data/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Javascript Destructing and Untrusted Data

While digging through a Javascript application, looking for bug bounty opportunities, I found an interesting case of destructing giving a false sense of security about the contents of an untrusted string.

Imagine you have the following function:

const extractParts = (input) => {
  if (!input || input.length < 10) return;
  let [a, b, c] = input.split("|");
  return { a, b, c };
};

This function, extractParts, is expecting an input string composed of three values, a, b, and c, concatenated together with "|" characters, like "aaaaaa|b|c".

In the application where I found this code, b and c are constants of fixed length of one character, so the implied input.length >= 10 constraint was really making assertions on the length of a. If the length of a was less than 6, there would be problems down the line.

Unfortunately, the destructing of input.split("|") on the next line doesn’t give us any guarantees about the real contents of input. If input contains only two "|" characters, our constraint holds. However, our destructing picks out the first three values of input.split("|"), even if the resulting array contains more than three elements.

Imagine input looks like this: "a|b|c|dddd". Passing this four-part input into extractParts yields a resulting object that will cause problems for our application further down the line:

extractParts("a|b|c|dddd"); // { a: "a", b: "b", c: "c" }

Where did our final "dddd" value go? It gave our input just enough length to slip past our input.length < 10 guard, but is was thrown out in the destructing step, resulting in a malformed a! Our array destructing gave us a false sense of security about the contents of our input string.

A better way of writing our assertion might look something like this:

const extractParts = (input) => {
  if (!input) return;
  let [a, b, c] = input.split("|");
  if (a.length < 6) return;
  return { a, b, c };
};

Asserting on the length of a directly prevents any unexpected inputs from slipping by unnoticed:

extractParts("a|b|c|dddd"); // undefined

Don’t let lenient destructuring give you a false sense of security about the shape and contents of untrusted data.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK