Validate a base64 decoded image in laravel
Asked Answered
B

3

15

Im trying to get a image from a PUT request for update a user picture(using postman), and make it pass through a validation in Laravel 5.2, for making the call in postman use the following url:

http://localhost:8000/api/v1/users?_method=PUT

and send the image string in the body, using a json like this:

{
    "picture" : "data:image/png;base64,this-is-the-base64-encode-string"
}

In the controller try a lot of differents ways for decode the image and try to pass the validation:

  1. First I tried this:

    $data = request->input('picture');
    $data = str_replace('data:image/png;base64,', '', $data);
    $data = str_replace(' ', '+', $data);
    $image = base64_decode($data);
    $file = app_path() . uniqid() . '.png';
    $success = file_put_contents($file, $image);
    
  2. Then I tried this:

    list($type, $data) = explode(';', $data);
    list(, $data) = explode(',', $data);
    $data = base64_decode($data);
    $typeFile = explode(':', $type);
    $extension = explode('/', $typeFile[1]);
    $ext = $extension[1];
    Storage::put(public_path() . '/prueba.' . $ext, $data);
    $contents = Storage::get(base_path() . '/public/prueba.png');
    
  3. Try to use the intervention image library (http://image.intervention.io/) and don't pass:

    $image = Image::make($data);
    $image->save(app_path() . 'test2.png');
    $image = Image::make(app_path() . 'test1.png');
    

This is the validation in the controller:

    $data = [
        'picture' => $image,
        'first_name' => $request->input('first_name'),
        'last_name' => $request->input('last_name')
    ];

    $validator = Validator::make($data, User::rulesForUpdate());
    if ($validator->fails()) {
        return $this->respondFailedParametersValidation('Parameters failed validation for a user picture');
    } 

this is the validation in the User-model:

public static function rulesForUpdate() {
    return [
        'first_name' => 'max:255',
        'last_name' => 'max:255',
        'picture' => 'image|max:5000|mimes:jpeg,png'
    ];
}   
Brookhouse answered 19/8, 2016 at 15:26 Comment(2)
I'm not following. What is the issue you're facing?Sibby
Cant pass the validation for a image created from a base64 stringBrookhouse
F
23

You can extend the Validator class of Laravel.

Laravel Doc

But anyway try this

Validator::extend('is_png',function($attribute, $value, $params, $validator) {
    $image = base64_decode($value);
    $f = finfo_open();
    $result = finfo_buffer($f, $image, FILEINFO_MIME_TYPE);
    return $result == 'image/png';
});

Don't forget the rules:

$rules = array(
   'image' => 'is_png'
);
Finnic answered 24/8, 2016 at 12:11 Comment(3)
if the hacker put a string start with "/9j" (e.g: "/9j{{what_ever_additional_string_to_hack_here}}") in the input, it is still detect as image/jpeg .... so this validation is not securedPrecipitous
the $result variable return "application/octet-stream" instead of "image/png" or "image/jpg"Esquiline
Any way to validate the dimensions of the image?Afghan
F
32

If you are using Intervention anyways, then you can leverage it for a custom validation rule. I have one called "imageable". It basically makes sure that the input given will be able to be converted to an Intervention image. Base64 data image strings will pass. A string of "foo" will not.

Validator::extend('imageable', function ($attribute, $value, $params, $validator) {
    try {
        ImageManagerStatic::make($value);
        return true;
    } catch (\Exception $e) {
        return false;
    }
});

This obviously just checks that the input is able to be converted to an image. However, it should show you as a use-case how you can leverage Intervention and any of it's methods to create a custom rule.

Flashback answered 12/9, 2016 at 2:59 Comment(4)
I have added your code in the boot method of the AppServiceProvider. But the base64 string doesn't pass the validation. Am I missing something? Where the extension is defined?Fatness
@JCarlos I place mine in the boot method as well. You must have something else wrong.Flashback
This really helped me out in my current app, thanks a million!!Princess
Dont forget to import use Illuminate\Support\Facades\Validator; use Intervention\Image\ImageManagerStatic;Biquadrate
F
23

You can extend the Validator class of Laravel.

Laravel Doc

But anyway try this

Validator::extend('is_png',function($attribute, $value, $params, $validator) {
    $image = base64_decode($value);
    $f = finfo_open();
    $result = finfo_buffer($f, $image, FILEINFO_MIME_TYPE);
    return $result == 'image/png';
});

Don't forget the rules:

$rules = array(
   'image' => 'is_png'
);
Finnic answered 24/8, 2016 at 12:11 Comment(3)
if the hacker put a string start with "/9j" (e.g: "/9j{{what_ever_additional_string_to_hack_here}}") in the input, it is still detect as image/jpeg .... so this validation is not securedPrecipitous
the $result variable return "application/octet-stream" instead of "image/png" or "image/jpg"Esquiline
Any way to validate the dimensions of the image?Afghan
D
0

inside extend function add this

$res= mime_content_type($value);
if ($res == 'image/png' || $res == 'image/jpeg') {
    return $res;
}
Dempsey answered 10/6, 2019 at 16:39 Comment(3)
This only works if you upload a real file and not a data url or data base64Contactor
this work if you pass a base64 I tried it many times plz give it a tryDempsey
yes this code will workOvertime

© 2022 - 2024 — McMap. All rights reserved.