We help IT Professionals succeed at work.

Should I be using jquery or vanilla javascript to write code for client side image upload security

James Froggatt
James Froggatt used Ask the Experts™
on
I am in the process of creating a rock solid secure image upload system for a website I am working on.

Naturally, I want to do client side checks to validate the image file (jpg or png), before I upload it for further checks to the server (using php) - and I assume for that I will use Javascript.

I know there is quite a lot to take into consideration here, so I'm taking it one step at a time.

My question is this, SHOULD I be using jquery for the client side image verification or vanilla javascript.

I know jquery is just javascript ultimately, but I have a propensity to do this with vanilla javascript. Personally I think vanilla javascript 'reads' better as code than jquery. I would also assume it's actually faster at execution.

Online, there is almost no useful information about vanilla javascript with regards to secure image file validation (there are snippets here and there I know) - but there is lots of information about jquery. I am the sort of person who likes to do things 'raw'. So that I can understand fully what my code is doing without any wrapper.

Am I missing something? Is jquery really great at something I'm not understanding? I see jquery code, and the equivalent javascript code, and don't really understand why people are so 'into' jquery. The code lengths are generally similar, and am personally not afraid of coding a bit more if that's what it takes.

I think part of it might be that jquery is all over the internet, and vanilla javascript isn't, so grabbing code that 'works' is easier, but I'm just wondering if I'm missing the point entirely.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
It is good to use JavaScript. To make JS code more secure, you can use minification and obfuscation of JavaScript code you have written in production.
Hope you are doing validation at client end and at server end also.
Most Valuable Expert 2017
Distinguished Expert 2018
Commented:
Why use jQuery?
Firstly it makes some tasks easier. Binding an event handler to a class for instance
$('.myClass').click(function() ...);
Javascript
var nodes = document.getElementsByClassName('myClass');
[...nodes].forEach(node => node.addEventListner('click', ...));

Open in new window

Or if you have to use that browser
var nodes = document.getElementsByClassName('myClass');
for(var n = 0; n < nodes.length; n++) {
   nodes[n].addEventListener('click', function() ...);
}

Open in new window

There was also a good use case at one point due to cross browser compatibility issues.

Other reasons - there is a wealth of jQuery libraries out there that provide a myriad of functions that are useful in code. Things like AJAX requests are easier to do with jQuery than a raw  XMLHttpRequest.

Ultimately jQuery is a tool to be used when appropriate.

It is true it is over used and in many cases a few lines of plain JS can replace a bloated jQuery library - but typically a software engineer will be able to determine what is best for the application.

If you have examples in jQuery - converting those to plain JS should be pretty straight forward. Paste what you have so far and lets take it from there.

Author

Commented:
Thank you Mrunal and Julian for your input.

Mrunal, I hadn't heard of 'minification and obfuscation of JavaScript code' so will look into that going forward

Julian, what I'm going to do is strip down what I have so far (I have coded a full 5 image upload system with AJAX/ javascript/ php) down to a single image upload system so that my code is easier to read.

I'll then be back here for all the assistence you can give. The reason why I'm not happy with my current code is that somewhere a .csv file is being allowed to upload when it's clearly not an image. I also have a library of malicious images that I wish to ensure ultimately my coding DOES see as NOT a valid image.

So I'll work on stripping it down and then produce a question with my code and go from there. I'll be back soon :)

Thank you all.
James
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
Here is an extract from some code I wrote for a Drop Zone control

function addFilesToDropZone(dropZone, files) {
    if (files.length > 1) {
        alert('Only 1 file can be added');
        return false;
    }
    var exts = dropZone.dataset.ext; // defined as a custom data attribute on the control (see below)
    var regex = new RegExp(exts);

    // Check if this is a valid extension
    if (!files[0].name.toLowerCase().split('.').pop().match(regex)) {
        alert('Only the following extensions are allowed (' + exts.split('|').join(',') + ')');
        return false;
    }
    var fieldId = dropZone.dataset.name;
    dropZone.parentForm.fileList[fieldId] = files[0];
    renderFileList(dropZone, files);
}

Open in new window

Here is the HTML for the drop zone
<div class="dropzone" data-ext="docx|pdf" data-name="yourFileName">
  Drop files here<br>
  <span>(or click to open file dialog)</span>
</div>

Open in new window


We bind to the onchange on the input to call the addFilesToDropzone
fileInput.onchange = function(fu) {
  addFilesToDropZone(dropZone, fu.target.files);
}

Open in new window

Author

Commented:
Julian,

If I am correct, your code above in Javascript is only validating the file by looking at the extension. There is more we can do in Javascript to validate for example a jpg being a jpg.

What I am referring to is the embedding of malicious code (php code) within a jpg.

Is there any way using javascript to TRULY validate an image file as being a genuine, none malicious image file, i.e. with no malicious code in for example the EXIF data (or anywhere else that it may be hidden if that's possible).

Would this level of very important security usually be done with php AFTER upload? I'm trying to get as much security as I can on the client side using Javascript before upload, then further security using php on the server side.

It will just make the site overall faster if there are thorough checks on the client side (using javascript) - to weed out any bad files that javascript is capable of doing - and then futher checks using php on the server side.

Thank you
James
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
JavaScript validation is for convenience.

Server validation is for security.

First rule of web coding - never trust any input arriving at your script. Assume it has not been checked, assume it is a malicious attempt and act accordingly.

Your JavaScript checks CAN be bypassed so doing this level of checking on them is pointless as a hacker will simply go around them.

Convenience checks in JavaScript so you don't annoy your users when they make an honest mistake and have to wait while you round trip to the server to validate.

Security lives in the server script.

Commented:
JavaScript cannot access the filesystem. Period. It doesn't matter if it's vanilla or jQuery or any other framework. The only thing it can do is see the filenames that originate from the browser's file selection dialog box.

Allowing JavaScript to read bytes of data would be an insane security risk. You would have hackers stealing files left and right. That is why the file upload system requires manual file selection by the user, and why JavaScript is intentionally crippled this way.

So you cannot validate anything beyond what is in the filename using JavaScript.

If you want to validate the file contents, then you do that on the server side. That's where you want to do it anyway, since you can never trust anything coming from the client.
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
@Gr8gonzo,

Not entirely true - you can read files that users have selected in the file input. If you load a CSV into a file input - you can read that data and process it in JavaScript.

Consider this sample http://www.marcorpsa.com/ee/t3781.html
This does not use any server side processing. It uses t he FileReader() object to process the selected file.

This does not change the fact that it should be done on the serer - was just highlighting that you can do this sort of processing in JavaScript.

Commented:
I stand corrected!

Author

Commented:
@Julian Hansen

Julian,

This is a very interesting discussion to me.

Are you in reality saying that client side Javascript security checks of image files are in fact pointless, because a hacker can get around them.

In other words, using Javascript in my situation is only for none malicious and none intentional errors from the user end. Such as:

1) A user tries to upload an image file that's over 10MB and I only want to allow <10MB -  (use Javascript to check file size before upload)
2) A user tried to upload a csv file (by accident) - use Javascript to check file is has a .jpg extension similar to your code above

... but in essence, don't try or attempt to do any more security using javascript other than the 2 points above because ANY attempt to do this in Javascript CAN be bypassed by a hacker.

Please consider the code below that I found on the internet:

/**
* Load the mime type based on the signature of the first bytes of the file
* @param  {File}   file        A instance of File
* @param  {Function} callback  Callback with the result
* @author Victor www.vitim.us
* @date   2017-03-23
*/
function loadMime(file, callback) {

  //List of known mimes
  var mimes = [
    {
      mime: 'image/jpeg',
      pattern: [0xFF, 0xD8, 0xFF],
      mask: [0xFF, 0xFF, 0xFF],
    },
    {
      mime: 'image/png',
      pattern: [0x89, 0x50, 0x4E, 0x47],
      mask: [0xFF, 0xFF, 0xFF, 0xFF],
    }
    // you can expand this list @see https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
  ];

  function check(bytes, mime) {
    for (var i = 0, l = mime.mask.length; i < l; ++i) {
      if ((bytes[i] & mime.mask[i]) - mime.pattern[i] !== 0) {
        return false;
      }
    }
    return true;
  }

  var blob = file.slice(0, 4); //read the first 4 bytes of the file

  var reader = new FileReader();
  reader.onloadend = function(e) {
    if (e.target.readyState === FileReader.DONE) {
      var bytes = new Uint8Array(e.target.result);

      for (var i=0, l = mimes.length; i<l; ++i) {
        //if (check(bytes, mimes[i])) return callback("Mime: " + mimes[i].mime + " <br> Browser:" + file.type);
        if (check(bytes, mimes[i])) return callback(mimes[i].mime);

      }

      return callback("Mime: unknown <br> Browser:" + file.type);
    }
  };
  reader.readAsArrayBuffer(blob);
}

Open in new window


The above Javascript is designed to read the mime type of the image file. It seems to work (almost) in my testing. Are you saying that this client side security is in fact NOT secure because a hacker can easily bypass it?

If that is the case then I have been very much enlightened to the limitations of javascript, because I am not a hacker, though in the future would be interested to learn about 'ethical hacking' because perhaps then I would understand why the above code is in fact useless in filtering out good image uploads from back image uploads (before actual uploading).

I am really interested to hear your thoughts on my above points as you are the expert, I am not.

Thank you and I look forward to hearing from you
James

Commented:
JavaScript is optional. A hacker could look at the form action (the submission endpoint) and manually send a file to it, completely bypassing your upload/JavaScript validation page.

What everyone here is saying is that you should treat JavaScript as a means to do very simple, convenient validation to deflect simple mistakes (e.g. someone selects a CSV file instead of an image file), but it shouldn't be your primary means of validation. If you're worried that someone is going to intentionally send the wrong file by renaming the extension, then that person is going to have motivation to bypass any other client-side validation you have in place, too.

Likewise, if you have a 10 MB limit and the user wants to send you a 100 MB file, but is frustrated by the limit (and believes you can truly handle more than 10 MB), they can upload it directly to the server and bypass your JavaScript validation routines. If your server is set up to allow files over 10 MB and isn't ALSO restricting the file size during server-side processing, then the user's attempt will succeed and you'll end up with a 100 MB file on your server.

However, once the file leaves the user's computer and is being processed by the server, control is now out of the user's hands. That means that you can safely perform validation (examine file headers, etc...) and be confident in approving or rejecting the results.

Author

Commented:
@gr8gonzo

Thank you, I understand what you are saying very clearly.

Would I be correct in saying the following statement as an example.

IF a malicious user WANTS to upload a 1000 MB file, no matter what I code on the client side to limit an upload by filesize  (in Javascript), a hacker WILL be able to bypass anything I do and upload that 1000 MB file.

It is ONLY when that 1000 MB file has been uploaded to my server that I will have true control (i.e. a hacker cannot circumvent my php code) and thus I'll be able to detect a 1000 MB file has been uploaded and thus reject it.

If the above is true, doesn't that allow hackers to cause MASSIVE problems on the internet. I mean, if I was a hacker, and bored, I could circumvent any sites javascript upload checking, and upload terrabytes of massive files (not necessarily malicious ones) and just get a kick out of grinding servers to a halt? ... because as I understand it so far in this discussion, there really is NO WAY of preventing that happening.

I'm cool with that if the above is true, I would guess we are then into IP blocking and things like that to try and make a hacker targetting a website 'go away'?

Please enlighten me because I'm kind of regretting not learning about hacking now as I would already understand far more about website security.

Thank you
James

You can tell I'm not a hacker.... perhaps that's what hackers do when they're bored... I don't know.

Commented:
Normally the web server implements restrictions on the size of data being sent to it in a request. However, that size usually applies to the web site as a whole. So let's say you have multiple places in your web application where you upload files - one page lets users upload large videos up to 200 MB and another page is for uploading a profile photo of up to 1 MB.

To accommodate this, the web server has to allow up to 200 MB for the site, which means that the web server will accept a 200 MB file for the profile photo page upload. That means that if you want to restrict the upload size on the profile photo page, you have to implement a server-size restriction in your code, too.

There are some techniques to help the web server deal with these limits more effectively, but that's why we don't have massive problems like you've described.

Author

Commented:
@gr8gonzo

So am I correct in saying problems like I describe (from hackers) are prevented by the limits set in for example

php.ini file 'post_max_size' & 'upload_max_filesize'

That is what completely prevents hackers from uploading files of obscene sizes.

... and also serve time-out's presumably.

Just on a side note, if I wanted a completely non-cirumventable (by hackers) method to limit file-size uploads to <10MB. Would setting this is in 'upload_max_filesize' be sufficient, or are there other areas where a <10MB limit should be set?

Thanks
James
Commented:
Yes, although there are various limits in different places. For example, if I were running an Apache web service, I'd probably set LimitRequestBody to the desired upper limit, since Apache is the first person in that little relay race, and I'd prefer Apache to reject huge uploads before wasting PHP's valuable time.

I would normally make LimitRequestBody and PHP's "post_max_size" and "upload_max_filesize" the same values (technically there's a little difference, but not usually significant at those sizes).

If I had the resources, I'd probably have an intermediate server sitting in front of Apache with some limits, too.

To answer your last question, yes, set upload_max_filesize and post_max_size to 10 megabytes.
Most Valuable Expert 2017
Distinguished Expert 2018
Commented:
@James,

This discussion has progressed while I was sleeping but in answer to your question to my post. Yes, using JavaScript to detect malicious uploads is not really worth it. Consider this. How does your page send data to the server? It POSTS it to a URL. You can POST to that URL from anywhere - it does not have to be from your page - so any security checks on your page become pointless when a hacker goes direct, hence the rule - assume EVERYTHING entering your script is a hack.

This is not a JavaScript limitation per se - it is just the nature of the web. You cannot dictate where requests to your page originate. Think of it like this. You provide a shuttle service from the airport to your hotel. The Shuttle Bus driver checks the passengers before getting on the bus that they are in fact guests at the hotel. He drops them off outside the entrance and they enter the hotel. At this point do they go straight to their rooms or do they first have to go to the reception desk and check in?

Enter our hacker. He can get on the bus and pretend to be a guest with a forged reservation printout - the bus driver can be given a bunch of tech to see if the passengers are really guests but that is pointless because our fake guest can simply hail a taxi and make his own way to the hotel.

Ultimately anyone entering the lobby of the hotel has to go through reception before they are given a key to their room.

Author

Commented:
@gr8gonzo @Julian Hansen

You've both been really helpful. I'd like to add I was a member of experts-exchange many moons ago and was a little worried that after all that time, that perhaps the brilliant and concise help I received all those years ago would have vanished.

I am now working on my code implementing the suggestions above then will attempt to create rock-solid php coding to verify that any image uploaded is in fact an image (with no malicious code within in). I will then set to filename changes and all the other security measures that I am sure I will need.

Once again, thank you both.

I am sure I'll be back further javascript questions as I implement that part (before my php coding) and will study your code that you posted earlier Julian.

Have a nice day both of you.
James

PS. I look forward to learning 'ethical hacking' in the future as I find the whole subject of security fascinating.

Author

Commented:
@Julian Hansen, I love analogies, they work well with my head, and yours above was fantastic.
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
You are welcome.