8

Let's say I am creating a REST API with Node/Express and data is exchanged between the client and server via JSON.

A user is filling out a registration form and one of the fields is an image input to upload a profile image. Images cannot be sent through JSON and therefore must be converted to a base64 string.

How do I validate this is indeed a base64 string of an image on the serverside? Or is it best practice not to send the profile image as a base64?

Hayden
  • 779
  • 10
  • 18
  • 1
    Possible duplicate of [How to check if a string is plaintext or base64 format in Node.js](https://stackoverflow.com/questions/32491681/how-to-check-if-a-string-is-plaintext-or-base64-format-in-node-js) – SanSolo Dec 13 '18 at 15:32
  • 1
    @SanSolo the other did not answer my question – Hayden Dec 13 '18 at 18:10
  • 2
    Why are you not uploading it standard way...sending is as file ? – Maielo Dec 13 '18 at 20:50

3 Answers3

7

You could start by checking if the string is a base64 image, with a proper mime type.
I found this library on npm registry doing exactly that (not tested).

const isBase64 = require('is-base64');
let base64str_img = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAoHBwgHBgoIC...ljA5GC68sN8AoXT/AF7fw7//2Q==';

console.log(isBase64(base64str_img, { mime: true })); // true

Then you can verify if the mime type is allowed within your app, or make other verifications like trying to display the image file and catch possible error.

Anyway, If you want to be really sure about user input, you have to handle it by yourself in the first place. That is the best practice you should care about.

TGrif
  • 5,725
  • 9
  • 31
  • 52
7

The Base64 value it is a valid image only if its decoded data has the correct MIME type, and the width and height are greater than zero. A handy way to check it all, is to install the jimp package and use it as follows:

var b64 = 'R0lGODdhAQADAPABAP////8AACwAAAAAAQADAAACAgxQADs=',
buf = Buffer.from(b64, 'base64');

require('jimp').read(buf).then(function (img) {
    if (img.bitmap.width > 0 && img.bitmap.height > 0) {
        console.log('Valid image');
    } else {
        console.log('Invalid image');
    }
}).catch (function (err) {
    console.log(err);
});
Victor
  • 5,493
  • 1
  • 27
  • 28
3

I wanted to do something similar, and ended up Googling it and found nothing, so I made my own base64 validator:

function isBase64(text) {
    let utf8 = Buffer.from(text).toString("utf8");
    return !(/[^\x00-\x7f]/.test(utf8));
}

This isn't great, because I used it for a different purpose but you may be able to build on it, here is an example using atob to prevent invalid base64 chars (they are ignored otherwise):

function isBase64(text) {
    try {
        let utf8 = atob(text);
        return !(/[^\x00-\x7f]/.test(utf8));
    } catch (_) {
        return false;
    }
}

Now, about how it works: Buffer.from(text, "base64") removes all invalid base64 chars from the string, then converts the string to a buffer, toString("utf8"), converts the buffer to a string. atob does something similar, but instead of removing the invalid chars, it will throw an error when it encounters one (hence the try...catch).

!(/[^\x00-\x7f]/.test(utf8)) will return true if all the chars from the decoded string belong in the ASCII charset, otherwise it will return false. This can be altered to use a smaller charset, for example, [^\x30-\x39\x41-\x5a\x61-\x7a] will only return true if all the characters are alphanumeric.

c8h_
  • 143
  • 1
  • 9