Com o ng-bind-html-unsafe removido, como injeto HTML?

265

Estou tentando usar o $sanitizeprovedor e a ng-bind-htm-unsafediretiva para permitir que meu controlador injete HTML em um DIV.

No entanto, não consigo fazê-lo funcionar.

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

Eu descobri que é porque foi removido do AngularJS (obrigado).

Mas sem ng-bind-html-unsafe, eu recebo este erro:

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

metalaureado
fonte
Existe uma solução simples para a versão 1.2.23+, veja post
John Henckel

Respostas:

123
  1. Você precisa garantir que o sanitize.js esteja carregado. Por exemplo, carregue-o em https://ajax.googleapis.com/ajax/libs/angularjs/[LAST_VERSION}/angular-sanitize.min.js
  2. você precisa incluir o ngSanitizemódulo no seu app exemplo:var app = angular.module('myApp', ['ngSanitize']);
  3. você só precisa vincular com ng-bind-htmlo htmlconteúdo original . Não há necessidade de fazer mais nada no seu controlador. A análise e conversão são feitas automaticamente pela ngBindHtmldiretiva. (Leia a How does it workseção sobre isso: $ sce ). Então, no seu caso <div ng-bind-html="preview_data.preview.embed.html"></div>, faria o trabalho.
p.matsinopoulos
fonte
3
É a opção mais limpa para fazer isso com segurança. Ele veio com mais dependências, mas é sobre segurança, então não hesite!
Pierre Maoui
Usando esta com iônica 1.0.0-beta.13
jasonflaherty
3
Isso não funciona com algumas tags, como entrada. Claro que não há uma maneira fácil de contornar isso. Realmente frustrante.
Casey #
Maneira mais comum e segura. Prefira isso se você planeja usar o bind-html em diferentes visualizações.
Eduardobursa
350

Em vez de declarar uma função no seu escopo, conforme sugerido por Alex, você pode convertê-la em um filtro simples:

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

Então você pode usá-lo assim:

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

E aqui está um exemplo de trabalho: http://jsfiddle.net/leeroy/6j4Lg/1/

Leeroy Brun
fonte
3
Eu tenho uma pequena coleção de ferramentas úteis para angular no github , incluirei esse filtro nessas ferramentas, se você não se importar. Esta é IMHO a melhor solução quando você confia no html.
Capaj
@Capaj Não tem problema, mas se você adicionar um link para esta resposta, isso seria muito apreciado. :-) stackoverflow.com/a/21254635 #
Leeroy Brun
Muito agradável. isso funciona como um encanto em repetições aninhadas!
Jelle Verzijden
Parece uma solução MUITO melhor do que a codificação para cada controlador. Apenas um filtro rápido e pronto! Usei-o com repetição de linhas da tabela, simples como torta .... <td ng-bind-html="representative.primary | to_trusted"></td>
Phil Nicholas
2
angular.module ('myApp'). filter ('trustAsHtml', ['$ sce', função ($ sce) {return $ sce.trustAsHtml}]);
Bradw2k 11/11/2015
275

Você indicou que está usando o Angular 1.2.0 ... como um dos outros comentários indicados, ng-bind-html-unsafefoi preterido.

Em vez disso, você deve fazer algo assim:

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

No seu controlador, injete o $sceserviço e marque o HTML como "confiável":

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

Observe que você deseja usar o 1.2.0-rc3 ou mais recente. (Eles corrigiram um bug no rc3 que impedia que "observadores" funcionassem corretamente em HTML confiável.)

ijprest
fonte
2
Eu tentei usar o acima, mas ele quebra meu código. Parece que você precisa acrescentar '$ scope' antes da definição da função - talvez tenha sido "entendida" de uma vez, mas não mais. O seguinte deve funcionar:myApp.controller('myCtrl', ['$scope', '$sce', function($scope, $sce) {
Dexygen
4
Você pode procurar mais informações sobre $ sce aqui apenas para buscar curiosidade! ;)
genuinefafa
5
Observe que isso provavelmente causará um problema de segurança XSS no seu código. Veja a resposta sugerida ngSanitizeabaixo ( stackoverflow.com/a/25679834/22227 ) para uma solução alternativa e mais segura.
Martin Probst
Por que essa é uma péssima idéia: docs.google.com/presentation/d/…
user857990
trustAsHtmlfaz o que diz, ele confia em qualquer código de entrada html, o que pode resultar em Scripting (XSS) ataques Cross-Site
Aleksey Solovey
112

Para mim, a solução mais simples e flexível é:

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

E adicione função ao seu controlador:

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

Não se esqueça de adicionar $sceà inicialização do seu controlador.

Alex
fonte
Parece mais simples de ter o controlador devolveu o html confiança em $ âmbito
meffect
1
Isso pode gerar loop infinito em $ sce, faça algo como: $ scope.trusted = {}; $ scope.to_trusted = function (html_code) {return $ scope.trusted [html_code] || ($ scope.trusted [html_code] = $ sce.trustAsHtml (html_code)); };
AO_
1
Toda solução que envolve abençoar o HTML como confiável apresenta uma vulnerabilidade XSS. Consulte a resposta sugerindo ngSanitize abaixo (stackoverflow.com/a/25679834/22227) para uma solução mais segura.
Michele Spagnuolo 22/02
65

A melhor solução para isso, na minha opinião, é esta:

  1. Crie um filtro personalizado que possa estar em um arquivo common.module.js, por exemplo - usado em seu aplicativo:

    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. Uso:

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

Agora - não vejo por que a diretiva ng-bind-htmlnão faz trustAsHtmlparte de sua função - me parece um pouco tolo que não

Enfim - é assim que eu faço - 67% das vezes, funciona sempre.

Paulo
fonte
Você pode usar o seguinte regex para localizar e substituir: regex: ng-bind-html-unsafe = "((? :( ?!").) *) "Substituição: ng-bind-html =" ($ 1) | html "com o filtro acima.
George Donev 23/12/15
2
Toda solução que envolve abençoar o HTML como confiável apresenta uma vulnerabilidade XSS. Consulte a resposta sugerindo ngSanitize abaixo (stackoverflow.com/a/25679834/22227) para uma solução mais segura.
Michele Spagnuolo 22/02
7

Você pode criar sua própria ligação html insegura simples, é claro, se você usar a entrada do usuário, isso pode ser um risco à segurança.

App.directive('simpleHtml', function() {
  return function(scope, element, attr) {
    scope.$watch(attr.simpleHtml, function (value) {
      element.html(scope.$eval(attr.simpleHtml));
    })
  };
})
Jason Goemaat
fonte
Esta diretiva não poderia também usar o $sce.trustAsHtml?
kontur
5

Você não precisa usar {{}} dentro do ng-bind-html-unsafe:

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

Aqui está um exemplo: http://plnkr.co/edit/R7JmGIo4xcJoBc1v4iki?p=preview

O operador {{}} é essencialmente apenas uma abreviação para ng-bind, então o que você estava tentando equivale a uma ligação dentro de uma ligação, o que não funciona.

ksimons
fonte
No entanto, se eu removê-lo, não recebo nada injetado. E os documentos são altamente confusos, usando um único} docs-angularjs-org-dev.appspot.com/api/…
metalaureate
Muito estranho. Acabei de testá-lo para ter certeza e, para mim, funcionou como esperado. Eu concordo que o single {} seja um pouco confuso nos documentos, mas eles são feitos como uma representação de uma expressão, não como literais na string. Eu atualizei minha resposta com uma força de trabalho.
ksimons
Além disso, se você já estiver usando o 1.2.0, veja os comentários aqui como ng-bind-html-unsafe foi removido: docs.angularjs.org/api/ng.directive:ngBindHtml
ksimons
2
Estou usando o 1.2. :( Grrr! Como injetar HTML inseguro? Eu recebo esse erro sem ele: errors.angularjs.org/undefined/$sce/unsafe
metalaureate
O {{}}operador estava causando meu problema com a ligação falha, obrigado pela dica!
Campbeln
2

Eu tive um problema semelhante. Ainda não foi possível obter o conteúdo dos meus arquivos de remarcação hospedados no github.

Depois de configurar uma lista de permissões (com o domínio github adicionado) no $ sceDelegateProvider no app.js, funcionou como um encanto.

Descrição: use uma lista de permissões em vez de agrupar como confiável se você carregar conteúdo de URLs diferentes.

Docs: $ sceDelegateProvider e ngInclude (para buscar, compilar e incluir fragmento HTML externo)

Lahmizzar
fonte
2

O escape contextual estrito pode ser totalmente desativado, permitindo que você injete html usando ng-html-bind. Essa é uma opção insegura, mas útil ao testar.

Exemplo da documentação$sce do AngularJS sobre :

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

Anexar a seção de configuração acima ao seu aplicativo permitirá que você injete html ng-html-bind, mas como o documento observa:

O SCE oferece muitos benefícios de segurança por pouca sobrecarga de codificação. Será muito mais difícil usar um aplicativo desativado do SCE e protegê-lo por conta própria ou habilitar o SCE posteriormente. Pode fazer sentido desabilitar o SCE nos casos em que você tem muito código existente que foi gravado antes da introdução do SCE e você está migrando um módulo por vez.

Sean Fahey
fonte
É bom saber, mas definitivamente algo que deve ser tratado com cuidado.
Iconoclast
2

Você pode usar filtro como este

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 "";
        };
    }
]);

uso

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

pode ser usado para outros tipos de recursos, por exemplo, link de origem para iframes e outros tipos declarados aqui

BotanMan
fonte