3

Motivation: I want to make a browser-based hashing utility so users can compute file hashes without installing software.

The approach I'm considering is a static page with "a file upload button" (except no upload takes place): the user picks a file, and the script computes and displays its hash.

So let's say we have this element on the page:

<input id="file-hasher" type="file" />

This creates a button that allows the users of the web page to select a file via an OS "File open..." dialog in the browser.

Let's say the user clicks said button, selects a file in the dialog, then clicks the "Ok" button to close the dialog.

The selected file name is now stored in:

document.getElementById("file-hasher").value

Here, I'm hoping to use a library like https://github.com/bitwiseshiftleft/sjcl/ to compute the hash of the chosen file. Is there a way to do this or does the browser's security model get in the way?

4
  • 1
    Do it and you will see. BTW: geraintluff.github.io/sha256 Commented Mar 9, 2020 at 9:27
  • Thanks for pointing me to your hash implementation. Looks clean. The issue here tho is how to read/stream the chosen file's contents (without uploading it) Commented Mar 9, 2020 at 20:56
  • Ahh.. I didn't realize the security model is specifically designed to allow this. Here's a good article humanwhocodes.com/blog/2012/05/08/… Commented Mar 11, 2020 at 0:54
  • 2
    Does this answer your question? How can I hash a string with SHA256 in JS? see my answer Commented Jul 28, 2021 at 2:28

2 Answers 2

8

Yes, you can select a file using the file element, and take a hash of the file locally, 'in-browser', using javascript. The browser's security model does not prevent this; and the hash function from the native Web Crypto API can be used, so there is no need for any external crypto libraries.

Here is a working example:

function hashfile() {
  readbinaryfile(fileselector.files[0])
    .then(function(result) {
      result = new Uint8Array(result);
      return window.crypto.subtle.digest('SHA-256', result);
    }).then(function(result) {
      result = new Uint8Array(result);
      var resulthex = Uint8ArrayToHexString(result);
      divresult.innerText = 'result: ' + resulthex;
    });
}

function readbinaryfile(file) {
  return new Promise((resolve, reject) => {
    var fr = new FileReader();
    fr.onload = () => {
      resolve(fr.result)
    };
    fr.readAsArrayBuffer(file);
  });
}

function Uint8ArrayToHexString(ui8array) {
  var hexstring = '',
    h;
  for (var i = 0; i < ui8array.length; i++) {
    h = ui8array[i].toString(16);
    if (h.length == 1) {
      h = '0' + h;
    }
    hexstring += h;
  }
  var p = Math.pow(2, Math.ceil(Math.log2(hexstring.length)));
  hexstring = hexstring.padStart(p, '0');
  return hexstring;
}
<h2>File Hash</h2>
<div>
  Select file to take hash of:
  <br/>
  <input type="file" id="fileselector" onchange="javascript:hashfile();">
</div>
<br/>
<div id="divresult"></div>

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for sharing this! I like how it uses no external deps. In the event, I ended up with a streaming solution (need to gracefully handle multi GB files).. crums.io I'm sure there must be a way with the builtin Web Crypto API you suggest to incrementally update the digest. Works for now, but I'll revisit :D
@BabakFarhang Actually, it's funny you should ask about streaming / chunking large files. I worked on something similar to this for tails.org (see tails.boum.org/news/verification_extension_deprecation/…). I don't think it's possible to stream / chunk the file with the web crypto api window.crypto.subtle.digest() function. We ended up using the Forge library instead, for that reason. See tails.boum.org/contribute/design/download_verification for more info (near the bottom of the page).
cool. Tangential: that Tails project is interesting. Worked on a portable OS called FaunOS ..(bootable from USB but could still save state). PS sorry for my clumsy edits
0

The standard browser security model allows you to have the user pick a file and do what you will with it. I'm an older guy and thought surely this kinda mingling with a user's parts would require additional hoops/consent. So @ceving 's answer was best: "Do it and you will see."

Here's a link to a good article: https://humanwhocodes.com/blog/2012/05/08/working-with-files-in-javascript-part-1/

Apologies for not trying first before posting.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.