Fjerne HTML-enheter i JavaScript?
Jeg har litt JavaScript-kode som kommuniserer med en XML-RPC-backend. XML-RPC returnerer strenger av formen:
<img src='myimage.jpg'>
Men når jeg bruker JavaScript til å sette inn strengene i HTML, vises de bokstavelig. Jeg ser ikke et bilde, jeg ser strengen:
<img src='myimage.jpg'>
Jeg antar at HTML-en blir escaped over XML-RPC-kanalen.
Hvordan kan jeg av-escape strengen i JavaScript? Jeg prøvde teknikkene på denne siden, uten hell: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/
Hva er andre måter å diagnostisere problemet på?
Svar #1
De fleste svarene gitt her har en stor ulempe: hvis strengen du prøver å konvertere ikke er pålitelig, vil du ende opp med en Cross-Site Scripting (XSS) sårbarhet. For funksjonen i det aksepterte svaret, vurder følgende:
htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");
Strengen her inneholder en uescaped HTML-tagg, så i stedet for å dekode noe vil htmlDecode-funksjonen faktisk kjøre JavaScript-kode som er spesifisert inne i strengen.
Dette kan unngås ved å bruke DOMParser, som støttes i alle moderne nettlesere:
function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
console.log( htmlDecode("<img src='myimage.jpg'>") )
// "<img src='myimage.jpg'>"
console.log( htmlDecode("<img src='dummy' onerror='alert(/xss/)'>") )
// ""Denne funksjonen garanteres å ikke kjøre noen JavaScript-kode som en bivirkning. Alle HTML-tagger vil bli ignorert, kun tekstinnholdet vil bli returnert.
Kompatibilitetsmerknad: Å analysere HTML med DOMParser krever minst Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 eller Microsoft Edge. Så alle nettlesere uten støtte er langt forbi sin EOL, og fra 2017 er de eneste som fortsatt av og til kan sees i bruk noen eldre versjoner av Internet Explorer og Safari (vanligvis er disse fortsatt ikke mange nok til å være bryet verdt).
Svar #2
Trenger du å dekode alle kodede HTML-enheter eller bare & selv?
Hvis du bare trenger å håndtere &, kan du gjøre dette:
var decoded = encoded.replace(/&/g, '&');
Hvis du trenger å dekode alle HTML-enheter, kan du gjøre det uten jQuery:
var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;
Vennligst merk deg Marks kommentarer nedenfor som fremhever sikkerhetshull i en tidligere versjon av dette svaret og anbefaler å bruke textarea i stedet for div for å redusere potensielle XSS-sårbarheter. Disse sårbarhetene eksisterer enten du bruker jQuery eller ren JavaScript.
Svar #3
REDIGER: Du bør bruke DOMParser API som Wladimir foreslår, jeg redigerte mitt forrige svar siden funksjonen som ble lagt ut introduserte en sikkerhetssårbarhet.
Følgende utdrag er den gamle svar-koden med en liten endring: å bruke en textarea i stedet for en div reduserer XSS-sårbarheten, men det er fortsatt problematisk i IE9 og Firefox.
function htmlDecode(input){
var e = document.createElement('textarea');
e.innerHTML = input;
// handle case of empty input
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
htmlDecode("<img src='myimage.jpg'>");
// returns "<img src='myimage.jpg'>"
Grunnleggende oppretter jeg et DOM-element programmessig, tildeler den kodede HTML-en til dens innerHTML og henter nodeValue fra tekstnoden som ble opprettet ved innsettingen av innerHTML. Siden det bare oppretter et element men aldri legger det til, blir ingen nettside-HTML endret.
Det vil fungere på tvers av nettlesere (inkludert eldre nettlesere) og akseptere alle HTML-tegnentiteter.
REDIGER: Den gamle versjonen av denne koden fungerte ikke på IE med tomme input, som vist her på jsFiddle (vis i IE). Versjonen over fungerer med alle input.
OPPDATERING: ser ut til at dette ikke fungerer med store strenger, og det introduserer også en sikkerhetssårbarhet, se kommentarene.
Svar #4
Et mer moderne alternativ for å tolke HTML (tekst og annet) fra JavaScript er HTML-støtten i DOMParser-API-et (se her på MDN). Dette lar deg bruke nettleserens innebygde HTML-parser for å konvertere en streng til et HTML-dokument. Den har blitt støttet i nye versjoner av alle større nettlesere siden slutten av 2014.
Hvis vi bare vil dekode noe tekstinnhold, kan vi legge det som det eneste innholdet i en dokumentkropp, analysere dokumentet, og hente ut dets .body.textContent.
var encodedStr = 'hello & world';
var parser = new DOMParser;
var dom = parser.parseFromString(
'<!doctype html><body>' + encodedStr,
'text/html');
var decodedString = dom.body.textContent;
console.log(decodedString);Vi kan se i utkastspesifikasjonen for DOMParser at JavaScript ikke er aktivert for det parse dokumentet, så vi kan utføre denne tekstkonverteringen uten sikkerhetsbekymringer.
Metoden
parseFromString(str, type)må kjøre disse stegene, avhengig av type:
"text/html"Analyser str med en
HTML-parser, og returner det nylig opprettedeDocument.Skriptflagget må settes til "deaktivert".
script-elementer blir merket som ikke-eksekverbare, og innholdet inoscriptblir analysert som markup.MERKDet går utover omfanget av dette spørsmålet, men vær oppmerksom på at hvis du tar de analyserte DOM-nodene selv (ikke bare deres tekstinnhold) og flytter dem til den aktive dokument-DOM-en, er det mulig at skriptingen deres blir aktivert på nytt, og det kan oppstå sikkerhetsmessige bekymringer. Jeg har ikke undersøkt det, så vær forsiktig.
Svar #5
Matthias Bynens har et bibliotek for dette: https://github.com/mathiasbynens/he
Eksempel :
console.log( he.decode("Jörg & Jürgen rocked to & fro ") ); // Logs "Jörg & Jürgen rocked to & fro"Jeg foreslår å foretrekke det fremfor hacks som innebærer å sette et elements HTML-innhold og deretter lese tilbake dets tekstinnhold. Slike tilnærminger kan fungere, men er bedrarisk farlige og gir XSS-muligheter hvis de brukes på uautorisert brukerinput.
Hvis du virkelig ikke tåler å laste inn et bibliotek, kan du bruke
textarea-trikset beskrevet i dette svaret på et nesten-duplikat spørsmål, som, i motsetning til ulike lignende tilnærminger som har blitt foreslått, ikke har noen sikkerhetshull så vidt jeg vet:function decodeEntities(encodedString) { var textArea = document.createElement('textarea'); textArea.innerHTML = encodedString; return textArea.value; } console.log(decodeEntities('1 & 2')); // '1 & 2'Men merk deg sikkerhetsproblemene, som påvirker lignende tilnærminger som denne, som jeg lister i det lenkede svaret! Denne tilnærmingen er en hack, og fremtidige endringer i hva som er tillatt innhold i en
textarea(eller feil i bestemte nettlesere) kan føre til at kode som er avhengig av den plutselig får et XSS-hull en dag.Svar #6
Hvis du bruker jQuery:
function htmlDecode(value){ return $('<div/>').html(value).text(); }Ellers, bruk Strictly Softwares Encoder-objekt, som har en utmerket
htmlDecode()-funksjon.Svar #7
Du kan bruke Lodash sin unescape/escape-funksjon https://lodash.com/docs/4.17.5#unescape
import unescape from 'lodash/unescape'; const str = unescape('fred, barney, & pebbles');str vil bli
'fred, barney, & pebbles'Svar #8
var htmlEnDeCode = (function() { var charToEntityRegex, entityToCharRegex, charToEntity, entityToChar; function resetCharacterEntities() { charToEntity = {}; entityToChar = {}; // add the default set addCharacterEntities({ '&' : '&', '>' : '>', '<' : '<', '"' : '"', ''' : "'" }); } function addCharacterEntities(newEntities) { var charKeys = [], entityKeys = [], key, echar; for (key in newEntities) { echar = newEntities[key]; entityToChar[key] = echar; charToEntity[echar] = key; charKeys.push(echar); entityKeys.push(key); } charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g'); entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g'); } function htmlEncode(value){ var htmlEncodeReplaceFn = function(match, capture) { return charToEntity[capture]; }; return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn); } function htmlDecode(value) { var htmlDecodeReplaceFn = function(match, capture) { return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10)); }; return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn); } resetCharacterEntities(); return { htmlEncode: htmlEncode, htmlDecode: htmlDecode }; })();Dette er fra ExtJS-kildekoden.
Svar #9
Trikset er å bruke nettleserens kraft til å dekode de spesielle HTML-tegnene, men ikke la nettleseren utføre resultatene som om det var faktisk HTML... Denne funksjonen bruker et regex for å identifisere og erstatte kodede HTML-tegn, ett tegn om gangen.
function unescapeHtml(html) { var el = document.createElement('div'); return html.replace(/\&[#0-9a-z]+;/gi, function (enc) { el.innerHTML = enc; return el.innerText }); }Svar #10
element.innerTextgjør også susen.Svar #11
Hvis du leter etter det, som meg - i mellomtiden finnes det en fin og sikker JQuery-metode.
https://api.jquery.com/jquery.parsehtml/
Du kan f.eks. skrive dette i konsollen din:
var x = "test &"; > undefined $.parseHTML(x)[0].textContent > "test &"Så $.parseHTML(x) returnerer et array, og hvis du har HTML-merknad i teksten din, vil array.length være større enn 1.
Svar #12
jQuery vil kode og dekode for deg. Du må imidlertid bruke en textarea-tagg, ikke en div.
var str1 = 'One & two & three'; var str2 = "One & two & three"; $(document).ready(function() { $("#encoded").text(htmlEncode(str1)); $("#decoded").text(htmlDecode(str2)); }); function htmlDecode(value) { return $("<textarea/>").html(value).text(); } function htmlEncode(value) { return $('<textarea/>').text(value).html(); }<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <div id="encoded"></div> <div id="decoded"></div>
