4

Is it safe to accept POST data that is supposed to be base64-encoded image data and use it as the src attribute of an img?

<img src="data:image/png;base64,[data here]" />

Obviously, with no filtering one could easily break out of the src attribute and the img tag and insert malicious <script /> or other tags, so my idea is to

base64_decode($rawPostData)

check if it is decoded OK and then

base64_encode($decodedData)

to put it in the src attribute.

Are there any vulnerabilities (such as XSS, maybe buffer overflow?) with this approach?

Background

I need this for a page that transforms a third-party svg to canvas to base64-encoded data using JavaScript (using "canvg" to be precise). I need to have the image passed to server-side scripts to do some other tasks using the image, but also to show the image to the user / client.

bububaba
  • 2,800
  • 5
  • 21
  • 28
  • 1
    If you can `base64_decode()` it, anyone can. – BoltClock Feb 06 '12 at 08:55
  • @BoltClock Could you elaborate? I'm not intending to use `base64_encode` to encrypt/decrypt data, all I need is to get data from user-land and show it as an image. My question is of vulnerabilities such as XSS that may result from this approach. – bububaba Feb 06 '12 at 08:59
  • As an aside, why don't you receive the data non-encoded, then verify the image and encode it yourself in the back end. Base64 encoding actually increases the size of the data, and slow uploads (and downloads) suck :) – Leigh Feb 06 '12 at 09:28
  • @Leigh: that would be the obvious thing to do, however I need this for a page that transforms a third-party `svg` to `canvas` to `base64`-encoded data using JavaScript (`canvg` project) – bububaba Feb 06 '12 at 09:37

3 Answers3

4

I would accept the image as an image, then base64_encode that. It saves the quite unnecessary middle-step of you checking it was submitted as expected and also makes it impossible to cause XSS.

If you must validate base64 as in image, simply checking it only contains base64 characters would be sufficient, since you are only embedding it within an img tag (and tag breaking characters are not allowed in base64.

Use inbuilt functions:

if (base64_decode($mystring, true)) {
    // is valid
} else {
    // not valid
}
deed02392
  • 4,478
  • 2
  • 26
  • 48
  • The problem is I'm receiving base64-encoded data from my JavaScript code. It will be valid, but I want to be safe if someone was to tamper the script and/or data. – bububaba Feb 06 '12 at 09:18
  • Of course. Which is why you should check the return value of a strict `base64_decode` one you receive it. – deed02392 Feb 06 '12 at 09:25
1

It is not safe if you do not verify the contents of the data.

Example:

$data= '"/><script>alert("hi");</script>';

print '<img src="data:image/png;base64,'.$data.'" />';

It's very simple to check that $data only contains valid base64 characters. None of which will break your tag.

Edit:

The following will simply result in a corrupt image:

$data= '"/><script>alert("hi");</script>';

$data64 = base64_encode($data);

print '<img src="data:image/png;base64,'.$data64.'" />';
Community
  • 1
  • 1
Leigh
  • 12,620
  • 3
  • 37
  • 60
  • Thanks for your input. Will `$data` be harmful after you decode and re-encode it? – bububaba Feb 06 '12 at 09:16
  • Corrupt is OK for me, unless it poses a security risk – bububaba Feb 06 '12 at 09:20
  • @bububaba: Edited answer. If you supply real base64 encoded data, it will be decoded internally by the browser as image data, as it cannot break the img tag. (doesn't contain quotes, or tag endings). Check out the other SO question I linked to to verify you actually received base64 data in the first place. – Leigh Feb 06 '12 at 09:21
  • @bububaba: In theory the image will only be corrupt if someone tampered with the data in the first place, or the image format was invalid to begin with. You **should** verify your uploads anyway. In the past there have been browser exploits that rely on image processing routines (GDI+ in IE for example) – Leigh Feb 06 '12 at 09:24
-2

The main security issue is that you can not apply clean XSS to this type of data. The part of the string "data:image/png;base64," is removed if clean XSS is applied, spoiling the image.

Having this in mind I set up a solution that answered me well:

  1. We know that the beginning of a base64 image follows a pattern similar to "data:image/png;base64,", so the following code could capture this beginning

    $data = substr($imageBase64, 0, strpos($imageBase64, ",") + 1);
  2. Okay, but we're only relying on the first comma found, that does not provide any security. So let's use preg_replace() to make sure the string we've captured is really what we expect.

    $data = preg_replace('#^data:image/[^;]+;base64,#', '', $data);
  3. If everything goes as planned the result stored in $data will be an empty string. Otherwise our image is no longer valid here. Now we need to validate what is after the first comma, so we will simply use the base64_decode.

    $img = preg_replace('#^data:image/[^;]+;base64,#', '', $imageBase64);
    if(base64_decode($img, true)) {
      // is valid
    } else {
      // not valid
    }
  4. We use the same preg_replace to replace the beginning of the string, and then we test the rest to see if it matches a valid base64 data. If positive you can already store the original string in the database without worries because its string is really clean, if negative again we have an invalid image.

Pedro Souza
  • 132
  • 7
  • Why downvote? Whenever you give a negative feedback please explain the reason in the comments for the person to have a chance to correct in the future. – Pedro Souza Dec 21 '17 at 18:14