Avec ng-bind-html-unsafe supprimé, comment injecter du HTML?

265

J'essaie d'utiliser le $sanitizefournisseur et la ng-bind-htm-unsafedirective pour permettre à mon contrôleur d'injecter du HTML dans un DIV.

Cependant, je ne peux pas le faire fonctionner.

<div ng-bind-html-unsafe="{{preview_data.preview.embed.html}}"></div>

J'ai découvert que c'est parce qu'il a été supprimé d'AngularJS (merci).

Mais sans ng-bind-html-unsafe, j'obtiens cette erreur:

http://errors.angularjs.org/undefined/$sce/unsafe

métalauréate
la source
Il existe une solution simple pour 1.2.23+, voir l'article
John Henckel

Réponses:

123
  1. Vous devez vous assurer que sanitize.js est chargé. Par exemple, chargez-le depuis https://ajax.googleapis.com/ajax/libs/angularjs/[LAST_VERSION[/angular-sanitize.min.js
  2. vous devez inclure un ngSanitizemodule sur votre app exemple:var app = angular.module('myApp', ['ngSanitize']);
  3. il vous suffit de vous lier au contenu ng-bind-htmloriginal html. Pas besoin de faire autre chose dans votre manette. L'analyse et la conversion sont effectuées automatiquement par la ngBindHtmldirective. (Lisez la How does it worksection à ce sujet: $ sce ). Donc, dans votre cas, <div ng-bind-html="preview_data.preview.embed.html"></div>ferait le travail.
p.matsinopoulos
la source
3
C'est l'option la plus propre pour le faire en toute sécurité. Il est venu avec plus de dépendances mais c'est une question de sécurité donc pas d'hésitation!
Pierre Maoui
Utiliser ceci avec ionic 1.0.0-beta.13
jasonflaherty
3
Cela ne fonctionne pas avec certaines balises, comme l'entrée. Bien sûr, il n'y a pas de moyen facile de contourner cela. Vraiment frustrant.
Casey
Moyen le plus courant et le plus sûr. Préférez-le si vous prévoyez d'utiliser bind-html dans différentes vues.
eduardobursa
350

Au lieu de déclarer une fonction dans votre portée, comme suggéré par Alex, vous pouvez la convertir en un simple filtre:

angular.module('myApp')
    .filter('to_trusted', ['$sce', function($sce){
        return function(text) {
            return $sce.trustAsHtml(text);
        };
    }]);

Ensuite, vous pouvez l'utiliser comme ceci:

<div ng-bind-html="preview_data.preview.embed.html | to_trusted"></div>

Et voici un exemple de travail: http://jsfiddle.net/leeroy/6j4Lg/1/

Leeroy Brun
la source
3
J'ai une petite collection d'outils utiles pour angular sur github , j'inclurai ce filtre dans ces outils si cela ne vous dérange pas. C'est IMHO la meilleure solution lorsque vous faites confiance au HTML.
Capaj
@Capaj Pas de problème, mais si vous ajoutez un lien vers cette réponse, cela serait grandement apprécié. :-) stackoverflow.com/a/21254635
Leeroy Brun
Très agréable. cela fonctionne comme un charme sur les répétitions imbriquées!
Jelle Verzijden
Cela semble être une bien meilleure solution que le codage pour chaque contrôleur. Juste un filtre rapide et c'est fait! Je l'ai utilisé avec des lignes de table répétitives, simple comme une tarte .... <td ng-bind-html="representative.primary | to_trusted"></td>
Phil Nicholas
2
angular.module ('myApp'). filter ('trustAsHtml', ['$ sce', function ($ sce) {return $ sce.trustAsHtml}]);
bradw2k
275

Vous avez indiqué que vous utilisez Angular 1.2.0 ... car l'un des autres commentaires indiqués ng-bind-html-unsafeest obsolète.

Au lieu de cela, vous voudrez faire quelque chose comme ceci:

<div ng-bind-html="preview_data.preview.embed.htmlSafe"></div>

Dans votre contrôleur, injectez le $sceservice et marquez le code HTML comme "approuvé":

myApp.controller('myCtrl', ['$scope', '$sce', function($scope, $sce) {
  // ...
  $scope.preview_data.preview.embed.htmlSafe = 
     $sce.trustAsHtml(preview_data.preview.embed.html);
}

Notez que vous souhaiterez utiliser 1.2.0-rc3 ou une version plus récente. (Ils ont corrigé un bogue dans rc3 qui empêchait les "observateurs" de fonctionner correctement sur du HTML de confiance.)

ijprest
la source
2
J'ai essayé d'utiliser ce qui précède mais cela casse mon code. Il semble que vous deviez ajouter «$ scope» avant la définition de la fonction - il a peut-être été «compris» à un moment donné, mais plus. Les éléments suivants devraient fonctionner:myApp.controller('myCtrl', ['$scope', '$sce', function($scope, $sce) {
Dexygen
4
Vous pouvez consulter plus d'informations sur $ sce ici juste pour susciter la curiosité! ;)
Genuinefafa
5
Notez que cela entraînera probablement un problème de sécurité XSS dans votre code. Voir la réponse suggérée ngSanitizeci-dessous ( stackoverflow.com/a/25679834/22227 ) pour une solution alternative plus sûre.
Martin Probst
Pourquoi c'est une mauvaise idée: docs.google.com/presentation/d/…
user857990
trustAsHtmlfait ce qu'il dit, il fait confiance à tout code html entrant, ce qui peut entraîner des attaques Cross-Site Scripting (XSS)
Aleksey Solovey
112

Pour moi, la solution la plus simple et la plus flexible est:

<div ng-bind-html="to_trusted(preview_data.preview.embed.html)"></div>

Et ajoutez une fonction à votre contrôleur:

$scope.to_trusted = function(html_code) {
    return $sce.trustAsHtml(html_code);
}

N'oubliez pas d'ajouter $sceà l'initialisation de votre contrôleur.

Alex
la source
Il semble plus simple que le contrôleur retourne le
code
1
Cela peut lancer une boucle infinie sur $ sce, faire quelque chose comme: $ scope.trusted = {}; $ scope.to_trusted = function (html_code) {return $ scope.trusted [html_code] || ($ scope.trusted [html_code] = $ sce.trustAsHtml (html_code)); };
AO_
1
Chaque solution qui implique de bénir le HTML comme approuvé introduit une vulnérabilité XSS. Veuillez consulter la réponse suggérant ngSanitize ci-dessous (stackoverflow.com/a/25679834/22227) pour une solution plus sûre.
Michele Spagnuolo
65

À mon avis, la meilleure solution est la suivante:

  1. Créez un filtre personnalisé qui peut être dans un fichier common.module.js par exemple - utilisé dans toute votre application:

    var app = angular.module('common.module', []);
    
    // html filter (render text as html)
    app.filter('html', ['$sce', function ($sce) { 
        return function (text) {
            return $sce.trustAsHtml(text);
        };    
    }])
  2. Usage:

    <span ng-bind-html="yourDataValue | html"></span>

Maintenant - je ne vois pas pourquoi la directive ng-bind-htmlne fait pas trustAsHtmlpartie de sa fonction - me semble un peu stupide qu'elle ne le fasse pas

Quoi qu'il en soit - c'est comme ça que je le fais - 67% du temps, ça marche toujours.

Paul
la source
Vous pouvez utiliser l'expression régulière suivante pour effectuer une recherche et un remplacement: expression régulière: ng-bind-html-unsafe = "((? :( ?!").) *) "Remplacement: ng-bind-html =" ($ 1) | html "avec le filtre ci-dessus.
George Donev
2
Chaque solution qui implique de bénir le HTML comme approuvé introduit une vulnérabilité XSS. Veuillez consulter la réponse suggérant ngSanitize ci-dessous (stackoverflow.com/a/25679834/22227) pour une solution plus sûre.
Michele Spagnuolo
7

Vous pouvez créer votre propre liaison HTML non sécurisée simple, bien sûr, si vous utilisez l'entrée utilisateur, cela pourrait constituer un risque pour la sécurité.

App.directive('simpleHtml', function() {
  return function(scope, element, attr) {
    scope.$watch(attr.simpleHtml, function (value) {
      element.html(scope.$eval(attr.simpleHtml));
    })
  };
})
Jason Goemaat
la source
Cette directive ne pourrait-elle pas également utiliser le $sce.trustAsHtml?
kontur
5

Vous n'avez pas besoin d'utiliser {{}} dans ng-bind-html-unsafe:

<div ng-bind-html-unsafe="preview_data.preview.embed.html"></div>

Voici un exemple: http://plnkr.co/edit/R7JmGIo4xcJoBc1v4iki?p=preview

L'opérateur {{}} est essentiellement un raccourci pour ng-bind, donc ce que vous essayiez équivaut à une liaison à l'intérieur d'une liaison, ce qui ne fonctionne pas.

ksimons
la source
Cependant, si je le retire, je ne reçois rien d'injecté. Et les documents sont très déroutants, utilisant un seul} docs-angularjs-org-dev.appspot.com/api/…
metalaureate
Très étrange. Je viens de le tester pour être sûr et pour moi, cela a fonctionné comme prévu. Je suis d'accord que les simples {} sont un peu déroutants dans les documents, mais ils sont censés être une représentation d'une expression, pas des littéraux dans la chaîne. J'ai mis à jour ma réponse avec un plunk fonctionnel.
ksimons
De plus, si vous utilisez déjà 1.2.0, voyez les commentaires ici car ng-bind-html-unsafe a été supprimé: docs.angularjs.org/api/ng.directive:ngBindHtml
ksimons du
2
J'utilise 1.2. :( Grrr! Comment injecter du HTML dangereux? J'obtiens
metalaureate
L' {{}}opérateur était à l'origine de mon problème d'échec de la liaison, merci pour l'astuce!
Campbeln
2

J'ai eu un problème similaire. Je n'ai toujours pas pu obtenir le contenu de mes fichiers de démarques hébergés sur github.

Après avoir configuré une liste blanche (avec un domaine github ajouté) pour $ sceDelegateProvider dans app.js, cela a fonctionné comme un charme.

Description: utiliser une liste blanche au lieu de l'habiller comme approuvé si vous chargez du contenu à partir d'une URL différente.

Documents: $ sceDelegateProvider et ngInclude (pour récupérer, compiler et inclure un fragment HTML externe)

Lahmizzar
la source
2

L'échappement contextuel strict peut être entièrement désactivé, ce qui vous permet d'injecter du HTML à l'aide de ng-html-bind. Il s'agit d'une option dangereuse, mais utile lors des tests.

Exemple de la documentation AngularJS sur$sce :

angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
  // Completely disable SCE.  For demonstration purposes only!
  // Do not use in new projects.
  $sceProvider.enabled(false);
});

Attacher la section de configuration ci-dessus à votre application vous permettra d'injecter du HTML ng-html-bind, mais comme le remarque le doc:

SCE vous offre de nombreux avantages en matière de sécurité pour une faible surcharge de codage. Il sera beaucoup plus difficile de prendre une application désactivée SCE et de la sécuriser par vous-même ou d'activer SCE ultérieurement. Il peut être judicieux de désactiver SCE dans les cas où vous avez beaucoup de code existant écrit avant l'introduction de SCE et que vous les migrez un module à la fois.

Sean Fahey
la source
Bon à savoir, mais certainement quelque chose qui doit être manipulé avec soin.
iconoclaste du
2

Vous pouvez utiliser un filtre comme celui-ci

angular.module('app').filter('trustAs', ['$sce', 
    function($sce) {
        return function (input, type) {
            if (typeof input === "string") {
                return $sce.trustAs(type || 'html', input);
            }
            console.log("trustAs filter. Error. input isn't a string");
            return "";
        };
    }
]);

usage

<div ng-bind-html="myData | trustAs"></div>

il peut être utilisé pour d'autres types de ressources, par exemple le lien source pour les iframes et autres types déclarés ici

BotanMan
la source