60 votes

meilleur moyen de déterminer si une URL est une image en PHP

En utilisant PHP, étant donné une URL, comment puis-je déterminer s'il s'agit d'une image ?

Il n'y a pas de contexte pour l'URL - elle se trouve juste au milieu d'un fichier texte brut, ou peut-être juste une chaîne de caractères en soi.

Je ne veux pas de frais généraux élevés (par exemple, la lecture du contenu de l'URL), car cela pourrait être appelé pour de nombreuses URL sur une page. Compte tenu de cette restriction, il n'est pas essentiel que toutes les images soient identifiées, mais j'aimerais avoir une idée assez précise.

Pour l'instant, je me contente de regarder l'extension du fichier, mais j'ai l'impression qu'il devrait y avoir un meilleur moyen.

Voici ce que j'ai actuellement :

  function isImage( $url )
  {
    $pos = strrpos( $url, ".");
    if ($pos === false)
      return false;
    $ext = strtolower(trim(substr( $url, $pos)));
    $imgExts = array(".gif", ".jpg", ".jpeg", ".png", ".tiff", ".tif"); // this is far from complete but that's always going to be the case...
    if ( in_array($ext, $imgExts) )
      return true;
    return false;
  }

Edit : Au cas où cela pourrait être utile à quelqu'un d'autre, voici la fonction finale utilisant la technique de la réponse d'Emil H :

  function isImage($url)
  {
     $params = array('http' => array(
                  'method' => 'HEAD'
               ));
     $ctx = stream_context_create($params);
     $fp = @fopen($url, 'rb', false, $ctx);
     if (!$fp) 
        return false;  // Problem with url

    $meta = stream_get_meta_data($fp);
    if ($meta === false)
    {
        fclose($fp);
        return false;  // Problem reading data from url
    }

    $wrapper_data = $meta["wrapper_data"];
    if(is_array($wrapper_data)){
      foreach(array_keys($wrapper_data) as $hh){
          if (substr($wrapper_data[$hh], 0, 19) == "Content-Type: image") // strlen("Content-Type: image") == 19 
          {
            fclose($fp);
            return true;
          }
      }
    }

    fclose($fp);
    return false;
  }

29voto

Emil H Points 24062

Vous pourriez utiliser un HTTP HEAD et vérifier le type de contenu. Cela pourrait être un bon compromis. Il peut être réalisé en utilisant PHP Streams . Wez Furlong a un article qui montre comment utiliser cette approche pour envoyer des requêtes post, mais elle peut être facilement adaptée pour envoyer des requêtes HEAD à la place. Vous pouvez récupérer les en-têtes d'une réponse http à l'aide de la commande stream_get_meta_data() .

Bien sûr, ce n'est pas vraiment 100%. Certains serveurs envoient des en-têtes incorrects. Il traitera cependant les cas où les images sont livrées via un script et où l'extension de fichier correcte n'est pas disponible. La seule façon d'être vraiment certain est de récupérer réellement l'image - soit toute l'image, soit les premiers octets, comme suggéré par thomasrutter.

0 votes

Je ne dirais pas que c'est à l'épreuve des balles. Les navigateurs ignorent le type de contenu pour les images rencontrées dans les éléments <img et reniflent le contenu à la place. Il est tout à fait possible de servir une image en tant que type non lié tel que text/plain et les navigateurs l'afficheront normalement dans un élément <img.

0 votes

Oui. Je suis d'accord. Je vais changer la langue :) Je pense que c'est le mieux que l'on puisse faire sans récupérer le contenu réel de l'url, cependant.

0 votes

(downvote enlevé) ouais je pense que c'est une option décente maintenant :)

16voto

Pedro Soares Points 130
if(is_array(getimagesize($urlImg)))
    echo 'Yes it is an image!';

16 votes

Getimagesize() télécharge l'intégralité du fichier image.

0 votes

Ce n'est probablement pas ce que vous recherchez : cela télécharge le fichier entier et ralentira beaucoup le processus

14voto

thomasrutter Points 42905

Il existe plusieurs approches différentes.

  • Renifler le contenu en recherchant un numéro magique au début du fichier. Par exemple, GIF utilise GIF87 ou GIF89 comme les cinq premiers octets du fichier (en ascii). Malheureusement, cela ne permet pas de savoir si l'image comporte une erreur ou si elle contient un contenu malveillant. Voici quelques chiffres magiques pour différents types de fichiers d'image (n'hésitez pas à les utiliser) :

    "\\xff\\xd8\\xff" => 'image/jpeg',
    "\\x89PNG\\x0d\\x0a\\x1a\\x0a" => 'image/png',
    "II\*\\x00" => 'image/tiff',
    "MM\\x00\*" => 'image/tiff',
    "\\x00\\x00\\x01\\x00" => 'image/ico',
    "\\x00\\x00\\x02\\x00" => 'image/ico',
    "GIF89a" => 'image/gif',
    "GIF87a" => 'image/gif',
    "BM" => 'image/bmp',

    Renifler le contenu de cette manière est probablement ce qui répond le mieux à vos besoins ; vous n'aurez à lire et donc à télécharger que les premiers octets du fichier (après l'en-tête).

  • Chargez l'image en utilisant la bibliothèque GD pour voir si elle se charge sans erreur. Cela peut vous indiquer si l'image est valide, sans erreur ou non. Malheureusement, cette méthode ne répond probablement pas à vos besoins car elle nécessite le téléchargement de l'image complète.

  • Si vous ne voulez vraiment pas faire de requête HTTP pour l'image, cela exclut à la fois le reniflage et l'obtention des en-têtes HTTP. Vous pouvez cependant essayer de déterminer si quelque chose est une image par le contexte dans lequel elle est liée. Un lien utilisant un attribut src dans un élément <img>est presque certainement une image (ou une tentative de XSS, mais c'est une autre histoire). Cela vous permettra de savoir si quelque chose est destiné à être une image. Il ne vous dira pas si l'image est réellement disponible, ou valide ; vous devrez récupérer au moins la première petite partie (en-tête ou numéro magique) de l'URL de l'image pour le savoir.

Malheureusement, il est possible qu'un fichier soit à la fois une image valide et un fichier ZIP contenant du contenu nuisible qui pourrait être exécuté en tant que Java par un site nuisible - voir l'exploit GIFAR . Vous pouvez presque certainement éviter cette vulnérabilité en chargeant l'image dans une bibliothèque comme GD et en effectuant un filtre non trivial sur celle-ci, comme l'adoucir ou l'accentuer un peu (par exemple en utilisant un filtre de convolution) et en l'enregistrant dans un nouveau fichier. sans en transférant toutes les métadonnées.

Essayer de déterminer si un élément est une image en se basant uniquement sur son type de contenu est assez peu fiable, presque aussi peu fiable que de vérifier l'extension du fichier. Lorsqu'ils chargent une image à l'aide d'un élément <img>, les navigateurs recherchent une chaîne magique.

0 votes

Merci pour la réponse détaillée mais pour mon application, lire des centaines d'images avant d'afficher une page sera probablement trop coûteux.

0 votes

Avez-vous lu mon troisième point ? Je ne sais pas si vous obtenez des images à partir des balises <img>, mais si c'est le cas, cela représente beaucoup moins de frais généraux que la réponse acceptée.

0 votes

Ah - je viens de voir que vous obtenez les URL des images à partir d'un fichier texte. Une sorte de requête HTTP légère, comme mon premier point, serait probablement nécessaire. Notez que cela ne représente pas une surcharge significative par rapport à la méthode HTTP HEAD.

14voto

RafaSashi Points 1492

En plus de la réponse d'Emil H.. :

Utilisation de get_headers() pour vérifier le type de contenu d'une url sans télécharger le fichier entier avec getimagesize()

    $url_headers=get_headers($url, 1);

    if(isset($url_headers['Content-Type'])){

        $type=strtolower($url_headers['Content-Type']);

        $valid_image_type=array();
        $valid_image_type['image/png']='';
        $valid_image_type['image/jpg']='';
        $valid_image_type['image/jpeg']='';
        $valid_image_type['image/jpe']='';
        $valid_image_type['image/gif']='';
        $valid_image_type['image/tif']='';
        $valid_image_type['image/tiff']='';
        $valid_image_type['image/svg']='';
        $valid_image_type['image/ico']='';
        $valid_image_type['image/icon']='';
        $valid_image_type['image/x-icon']='';

        if(isset($valid_image_type[$type])){

            //do something

        }
    }

1 votes

$url_headers['Content-Type'] pourrait très bien être un tableau, et devrait être vérifié avant d'assigner $type . De plus, faites attention à l'ordre dans lequel vous lisez le tableau (si vous n'êtes intéressé que par le type de contenu final, après toutes les redirections, vous voudrez le dernier élément du tableau).

8voto

TheMonkeyKing Points 94

Edit : Pour les images statiques avec l'extension d'image populaire.

<?php
$imgExts = array("gif", "jpg", "jpeg", "png", "tiff", "tif");
$url ='path/to/image.png';
$urlExt = pathinfo($url, PATHINFO_EXTENSION);
if (in_array($urlExt, $imgExts)) {
    echo 'Yes, '.$url.' is an Image';
}

?>

1 votes

Il s'agit d'une image contulmeu.moldcell.md/ps/selfcare_uni/crypt/ mais cette url ne passera pas votre validation ! Vous avez tort.

0 votes

Comme le souligne Jenechka, cette technique est très limitée : 1. elle suppose que tous les fichiers image ont des extensions valides ; 2. elle ne prend pas en charge les formats d'image auxquels l'auteur n'a pas pensé ou qui sont créés après l'écriture du programme.

0 votes

Ce n'est pas une solution parfaite, mais je l'aime bien, car elle est simple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X