Yoan Blanc’s weblog

Another lost swiss guy

Safer uploads or don’t trust a browser

Yoan BlancThu, 31 Mar 2011, , ,

We did tell you to no trust the browser right? Or the user/bot behind it. And to filter, check, sanitize everything that comes from him, her or it.

So you check the basics like GET and POST arguments. And maybe you check the values coming for the cookies as well. But do you check the file uploading? You know that type arguments sent. This how a request look like:

POST /…
…
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="key"

value
--boundary
Content-Disposition: form-data; name="file"; filename="image.png"
Content-Type: image/png

…Your PNG image here…
--boundary--

The Content-Type field is guessed by the browser and most of the time on the file extension. But it can decide to send whatever it wants like application/octet-stream which just says that it’s a binary file.

So, you shall not trust it and always verify what you get using the proper tool, like the UNIX command file PHP ships with it and Python has a wrapper called magic. That how you use it in Python in a WSGI application (using WebOb).

import magic
from webob import Request

def application(environ, start_response):
 req = Request(environ, charset="utf-8")
 # sent from the browser
 uploaded_file = req.POST.get("file")
 # what we actually got
 guessed_type = uploaded_file.type
 magic_type = magic.from_buffer(uploaded_file.read(1024),
                                mime=True)
 uploaded_file.seek(0)
 "… whatever else …"

This way you can really white list the file you users are uploading and prevent them for sending you .mov, .doc or whatever when they think they are uploading a new avatar for their profile. True story.

The code is on github

On vous a certainement dit et répété de vérifier tout ce qui provient de vos utilisateurs, juste ? Alors vous vérifiez les GET et les POST, et parfois aussi les cookies mais avez-vous pensé aux envois de fichiers ?

Aha, non ! C’est bien ce que je pensais. En gros, pour faire simple on peut vous faire parvenir n’importe quoi en vous disant que c’est n’importe quoi d’autre. Tiens une image alors que je t’envoie un document LibreOffice (c’est comme Office, si si). Et ça se fait très certainement sans en avoir conscience car c’est le navigateur qui rempli cette information et de manière générale il la devine à partir de l’extension du fichier.

C’est pourquoi il est important d’exclure cette donnée et d’aller la repêcher par ses propres moyens. En PHP, par exemple, ça se fait avec finfo qui est très proche de la commande UNIX file, bien connue. Los.

<?php
$file = $_FILES['file'];
// ce que nous donne le navigateur
$guessed_type = $file['type'];
// ce que file nous donne
$finfo = new finfo(FILEINFO_MIME);
$magic_type = $finfo->file($file['tmp_name']);

Un petit cas concret de problème que nous avons rencontré. Une gif animée enregistrée en tant que jpg dont on désire obtenir une miniature avec Imagick risque de vous donner autant d’images qu’il y a de frames (si bien entendu vous supportez aussi les gif animées, ce que nous faisons).

Et vous éviterez aussi de retrouver trop de bordel dans les avatars de vos utilisateurs.

Le code se trouve sur github

About

meYoan Blanc is a web developer that lives in Switzerland (rue des Fahys 15, 2000 Neuchâtel) and works as a freelance.

Get my vCard or contact me by phone (skype:yoan.blanc) or email ().

Misc

RSS, list.blogug.ch

This site reflects only my opinion and is not affiliated with anyone else.

copyright 2006-2010 — doSimple.ch