Gestion du contenu HTTP dans les pages HTTPS


89

Nous avons un site qui est entièrement accessible via HTTPS, mais affiche parfois du contenu externe qui est HTTP (images de flux RSS, principalement). La grande majorité de nos utilisateurs sont également bloqués sur IE6.

Je voudrais idéalement faire les deux

  • Empêcher le message d'avertissement IE concernant le contenu non sécurisé (afin que je puisse en afficher un moins intrusif, par exemple en remplaçant les images par une icône par défaut comme ci-dessous)
  • Présentez quelque chose d'utile aux utilisateurs à la place des images qu'ils ne peuvent pas voir autrement; s'il y avait du JS, je pourrais courir pour déterminer quelles images n'ont pas été chargées et les remplacer par une de nos images à la place, ce serait génial.

Je soupçonne que le premier objectif n'est tout simplement pas possible, mais le second peut être suffisant.

Dans le pire des cas, j'analyse les flux RSS lorsque nous les importons, je récupère les images et les stocke localement pour que les utilisateurs puissent y accéder de cette façon, mais cela semble être très pénible pour un gain raisonnablement faible.

Réponses:


147

Votre pire scénario n'est pas aussi mauvais que vous le pensez.

Vous analysez déjà le flux RSS, vous disposez donc déjà des URL des images. Supposons que vous ayez une URL d'image comme http://otherdomain.com/someimage.jpg. Vous réécrivez cette URL sous la forme https://mydomain.com/imageserver?url=http://otherdomain.com/someimage.jpg&hash=abcdeafad. De cette façon, le navigateur fait toujours la demande via https, vous évitant ainsi les problèmes.

La partie suivante - créez une page proxy ou un servlet qui effectue les opérations suivantes -

  1. Lisez le paramètre url de la chaîne de requête et vérifiez le hachage
  2. Téléchargez l'image depuis le serveur et renvoyez-la au navigateur par proxy
  3. Si vous le souhaitez, mettez en cache l'image sur le disque

Cette solution présente certains avantages. Vous n'êtes pas obligé de télécharger l'image au moment de la création du html. Vous n'êtes pas obligé de stocker les images localement. De plus, vous êtes apatride; l'url contient toutes les informations nécessaires pour servir l'image.

Enfin, le paramètre de hachage est pour la sécurité; vous voulez que votre servlet ne serve des images que pour les URL que vous avez construites. Ainsi, lorsque vous créez l'url, calculez-la md5(image_url + secret_key)et ajoutez-la en tant que paramètre de hachage. Avant de servir la requête, recalculez le hachage et comparez-le à ce qui vous a été transmis. Puisque la clé secret_key n'est connue que de vous, personne d'autre ne peut construire d'URL valides.

Si vous développez en java, le servlet n'est que quelques lignes de code. Vous devriez pouvoir porter le code ci-dessous sur toute autre technologie back-end.

/*
targetURL is the url you get from RSS feeds
request and response are wrt to the browser
Assumes you have commons-io in your classpath
*/

protected void proxyResponse (String targetURL, HttpServletRequest request,
 HttpServletResponse response) throws IOException {
    GetMethod get = new GetMethod(targetURL);
    get.setFollowRedirects(true);    
    /*
     * Proxy the request headers from the browser to the target server
     */
    Enumeration headers = request.getHeaderNames();
    while(headers!=null && headers.hasMoreElements())
    {
        String headerName = (String)headers.nextElement();

        String headerValue = request.getHeader(headerName);

        if(headerValue != null)
        {
            get.addRequestHeader(headerName, headerValue);
        }            
    }        

    /*Make a request to the target server*/
    m_httpClient.executeMethod(get);
    /*
     * Set the status code
     */
    response.setStatus(get.getStatusCode());

    /*
     * proxy the response headers to the browser
     */
    Header responseHeaders[] = get.getResponseHeaders();
    for(int i=0; i<responseHeaders.length; i++)
    {
        String headerName = responseHeaders[i].getName();
        String headerValue = responseHeaders[i].getValue();

        if(headerValue != null)
        {
            response.addHeader(headerName, headerValue);
        }
    }

    /*
     * Proxy the response body to the browser
     */
    InputStream in = get.getResponseBodyAsStream();
    OutputStream out = response.getOutputStream();

    /*
     * If the server sends a 204 not-modified response, the InputStream will be null.
     */
    if (in !=null) {
        IOUtils.copy(in, out);
    }    
}

1
Très sonore, et je pense que c'est ce avec quoi je vais rouler. Nous utilisons PHP, mais l'implémentation sera également triviale. Je vais également implémenter la mise en cache de notre côté, car je ne veux pas télécharger l'image chaque fois que quelqu'un la demande (pour les performances et l'utilisation de la bande passante). Les suggestions pour l'approche de sécurité sont valables (même si nous appliquerons également notre modèle de sécurité standard ainsi que ce qui précède). Merci pour votre suggestion.
El Yobo

32
Le seul inconvénient majeur de cette approche est que vous acheminez toutes les ressources externes via vos propres systèmes. Ce qui n'est pas seulement un handicap, mais peut également devenir assez coûteux.
Tim Molendijk

J'appuie @TimMolendijk, ajoutant que cela ajoute non seulement des coûts et de la maintenance, mais élimine également tous les CDN censés être acheminés vers des serveurs proches ou équilibrés vers des serveurs inactifs.
Levente Pánczél

2
Quelle est la solution pour NodeJS?
stkvtflw

1
un autre +1 pour @TimMolendijk mais quelle serait alors la solution? le site servi sur HTTPS ne semble pas bien fonctionner avec les images livrées via HTTP
FullStackForger

15

Si vous recherchez une solution rapide pour charger des images via HTTPS, le service de proxy inverse gratuit à l' adresse https://images.weserv.nl/ peut vous intéresser. C'était exactement ce que je cherchais.

Si vous recherchez une solution payante, j'ai déjà utilisé Cloudinary.com qui fonctionne également bien mais qui est trop cher uniquement pour cette tâche, à mon avis.


Quel est le piège? Fonctionne très bien
Jack

5
@JackNicholson Je l'utilise sous une charge relativement lourde depuis 2 ans. Fonctionne très bien! Félicitations aux deux développeurs.
nullable

J'ai des liens (vidéo ou site) commençant par Http et je ne peux pas les afficher dans un Iframe sur notre site https. Comme il s'agit d'un lien non sécurisé, il ne fonctionne pas. pour une image, j'ai résolu le problème en utilisant le cache d'image. N'importe qui a une idée
int14

@ int14 Vous devrez configurer un proxy inverse pour le site http, vous pouvez le faire avec quelque chose comme AWS API Gateway.
nullable le

3

Je ne sais pas si cela conviendrait à ce que vous faites, mais comme solution rapide, je "envelopperais" le contenu http dans un script https. Par exemple, sur votre page qui est servie via https, j'introduirais un iframe qui remplacerait votre flux rss et dans le src attr de l'iframe, mettriez une URL d'un script sur votre serveur qui capture le flux et génère le html. le script lit le flux via http et le sort via https (donc "wrapping")

Juste une pensée


Il me semble que cela me laisserait dans la même situation que je suis actuellement; Je montre déjà le contenu dans une page HTTPS - le problème est qu'il y a des balises <img> dans le contenu avec des valeurs http: // src - qui ne sont pas affichées et provoquent un message ennuyeux.
El Yobo

eh bien, oui, si vous conservez les liens originaux vers les images, il n'y a aucun moyen d'éviter le problème. Le script wrapper devrait analyser le contenu du flux rss à la recherche d'images et les supprimer. Comme vous l'avez mentionné dans un autre commentaire, vous ne voulez pas charger le contenu à l'origine du popup et afficher quelque chose d'informatif à la place. C'est la raison du "script au milieu"
hndcrftd

Vous pouvez même faire cela sans l'iframe, directement dans votre script principal, mais dans ce cas, vous attendez que le flux rss revienne avant d'être traité et sorti sur une page. Je ferais un iFrame pour que votre page se charge de manière asynchrone avec le flux rss. Il y a aussi l'option ajax si vous voulez y aller pour éviter l'iframe. Juste curieux - quelle est votre plate-forme backend?
hndcrftd

2

En ce qui concerne votre deuxième exigence, vous pourrez peut-être utiliser l'événement onerror, c'est-à-dire. <img onerror="some javascript;"...

Mettre à jour:

Vous pouvez également essayer d'itérer document.imagesdans le dom. Il existe une completepropriété booléenne que vous pourrez peut-être utiliser. Je ne sais pas avec certitude si cela conviendra, mais cela vaut peut-être la peine d'être étudié.


Intéressant, je ne savais même pas qu'il y avait un événement onerror. Je devrais réécrire le HTML (car il provient d'une source externe), mais il est déjà en cours de désinfection avec un purificateur HTML, donc l'ajouter en tant que filtre peut être possible.
El Yobo le

Aucun avertissement de sécurité du navigateur ne se produira-t-il avant que JavaScript n'ait pu faire quoi que ce soit?
MrWhite

0

Il serait préférable d'avoir simplement le contenu http sur https


5
Si je n'ai pas précisé cela dans ma question, le contenu HTTP se trouve sur le serveur d'autres personnes, ce n'est pas le mien. Plus précisément, ce sont les liens <img> en HTML que j'ai récupérés à partir des flux RSS. Je l'ai souligné maintenant dans la question.
El Yobo le

Oh ok, est-ce que webproworld.com/webmaster-forum/threads/ ... aiderait du tout?
Daniel

0

Parfois, comme dans les applications Facebook, nous ne pouvons pas avoir de contenu non sécurisé dans une page sécurisée. nous ne pouvons pas non plus rendre ces contenus locaux. par exemple une application qui se chargera dans iFrame n'est pas un simple contenu et on ne peut pas la rendre locale.

Je pense que nous ne devrions jamais charger le contenu http en https, nous ne devrions pas non plus replier la page https vers la version http pour éviter les dialogues d'erreur.

le seul moyen d'assurer la sécurité de l'utilisateur est d'utiliser la version https de tous les contenus, http://developers.facebook.com/blog/post/499/


3
Cela peut être possible avec Facebook, mais pas pour tout le contenu et cette question ne concernait pas Facebook.
El Yobo

0

La réponse acceptée m'a aidé à mettre à jour cela à la fois vers PHP et CORS, j'ai donc pensé inclure la solution pour les autres:

PHP / HTML pur:

<?php // (the originating page, where you want to show the image)
// set your image location in whatever manner you need
$imageLocation = "http://example.com/exampleImage.png";

// set the location of your 'imageserve' program
$imageserveLocation = "https://example.com/imageserve.php";

// we'll look at the imageLocation and if it is already https, don't do anything, but if it is http, then run it through imageserve.php
$imageURL = (strstr("https://",$imageLocation)?"": $imageserveLocation . "?image=") . $imageLocation;

?>
<!-- this is the HTML image -->
<img src="<?php echo $imageURL ?>" />

javascript / jQuery:

<img id="theImage" src="" />
<script>
    var imageLocation = "http://example.com/exampleImage.png";
    var imageserveLocation = "https://example.com/imageserve.php";
    var imageURL = ((imageLocation.indexOf("https://") !== -1) ? "" : imageserveLocation + "?image=") + imageLocation;
    // I'm using jQuery, but you can use just javascript...        
    $("#theImage").prop('src',imageURL);
</script>

imageserve.php voir http://stackoverflow.com/questions/8719276/cors-with-php-headers?noredirect=1&lq=1 pour plus d'informations sur CORS

<?php
// set your secure site URL here (where you are showing the images)
$mySecureSite = "https://example.com";

// here, you can set what kinds of images you will accept
$supported_images = array('png','jpeg','jpg','gif','ico');

// this is an ultra-minimal CORS - sending trusted data to yourself 
header("Access-Control-Allow-Origin: $mySecureSite");

$parts = pathinfo($_GET['image']);
$extension = $parts['extension'];
if(in_array($extension,$supported_images)) {
    header("Content-Type: image/$extension");
    $image = file_get_contents($_GET['image']);
    echo $image;
}

-2

Simplement: NE PAS LE FAIRE. Le contenu HTTP dans une page HTTPS est intrinsèquement non sécurisé. Point. C'est pourquoi IE affiche un avertissement. Se débarrasser de l'avertissement est une approche stupide.

Au lieu de cela, une page HTTPS ne doit avoir contenu HTTPS. Assurez-vous que le contenu peut également être chargé via HTTPS et référencez-le via https si la page est chargée via https. Pour le contenu externe, cela impliquera de charger et de mettre en cache les éléments localement afin qu'ils soient disponibles via https - bien sûr. Pas moyen de contourner cela, malheureusement.

L'avertissement est là pour une bonne raison. Sérieusement. Passez 5 minutes à réfléchir à la façon dont vous pourriez prendre en charge une page affichée en https avec un contenu personnalisé - vous serez surpris.


3
Facile là, je suis conscient qu'il y a une bonne raison à cela; Je pense que le comportement d'IE est meilleur que FF à cet égard. Ce que je vise, ce n'est pas de charger le contenu; Je veux juste éviter l'avertissement de style popup intrusif et montrer quelque chose d'informatif à la place du contenu.
El Yobo du

2
Aucune chance pour cela - à moins que vous ne réécriviez le HTML en sortant. Toute tentative de post-chargement javascript a déjà affiché la boîte de dialogue.
TomTom le

Il posait juste des questions sur les images et il ne demande aucun texte ou script non sécurisé afin que nous puissions contourner l'avertissement en réécrivant les URL.
Jayapal Chandran

1
Aucun changement à la réponse. Les images peuvent également ne pas être sécurisées. C'est une chose générale - soit elle provient de la source sécurisée, soit elle peut être remplacée par un homme au milieu de l'attaque.
TomTom

8
Évalué parce que cette «réponse» ne répondait pas à la manière d'atteindre l'objectif du PO.
MikeSchinkel

-3

Je me rends compte qu'il s'agit d'un ancien fil, mais une option consiste simplement à supprimer la partie http: de l'URL de l'image afin que ' http: //some/image.jpg » devienne «//some/image.jpg». Cela fonctionnera également avec les CDN


7
Cela fonctionnera parfois et parfois non; cela dépend si le contenu en amont est disponible via HTTPS. Sinon, il se cassera simplement.
El Yobo

-3

La meilleure façon de travailler pour moi

<img src="/path/image.png" />// this work only online
    or
    <img src="../../path/image.png" /> // this work both
    or asign variable
    <?php 
    $base_url = '';
    if($_SERVER['HTTP_HOST'] == 'localhost')
    {
         $base_url = 'localpath'; 
    }
    ?>
    <img src="<?php echo $base_url;?>/path/image.png" /> 
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.