디코딩 & amp; JavaScript로 돌아 가기


229

나는 같은 문자열을 가지고

var str = 'One & two & three';

웹 서버에 의해 HTML로 렌더링됩니다. 그 문자열을

'One & two & three'

현재, 내가하고있는 일입니다 (jQuery의 도움으로).

$(document.createElement('div')).html('{{ driver.person.name }}').text()

그러나 나는 내가 잘못하고 있다는 불안감을 느낍니다. 나는 시도했다

unescape("&")

그러나 그것은 작동하지 않는 것 같습니다, decodeURI / decodeURIComponent도 마찬가지입니다.

다른 기본적이고 우아한 방법이 있습니까?


이 기사에 포함 된 거대한 기능은 잘 작동하는 것 같습니다 : blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx 가장 영리한 해결책이라고 생각하지는 않지만 작동합니다.
Matias

1
HTML 엔터티를 포함하는 문자열은 escaped 또는 URI 인코딩 문자열 과 다르므로 해당 기능이 작동하지 않습니다.
Marcel Korpel

1
@Matias는 2003 년에 그 기능이 작성된 이래로 새로운 명명 된 엔티티 (예 : HTML 5 스펙을 통해)가 HTML에 추가되었다는 것을 인식하고있다 𝕫. 이것은 진화하는 스펙의 문제입니다. 따라서 실제로 해결하기 위해 유지 관리되는 도구를 선택해야합니다.
Mark Amery

1
@MarkAmery 예, 전적으로 동의합니다! 몇 년 후에이 질문으로 돌아온 것은 좋은 경험입니다. 감사합니다!
Matias 2012

답변:


104

JavaScript에서 HTML (텍스트 및 기타)을 해석하기위한보다 현대적인 옵션은 DOMParserAPI 의 HTML 지원입니다 ( 여기서는 MDN 참조 ). 따라서 브라우저의 기본 HTML 파서를 사용하여 문자열을 HTML 문서로 변환 할 수 있습니다. 2014 년 말부터 모든 주요 브라우저의 새 버전에서 지원되었습니다.

텍스트 내용 만 해독하려면 문서 본문에 유일한 내용으로 넣고 문서를 구문 분석 한 다음을 꺼낼 수 있습니다 .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);

우리는 볼 수 에 대한 초안 사양DOMParser 자바 스크립트 구문 분석 된 문서를 사용할 수 없습니다 우리가 보안 문제없이이 텍스트 변환을 수행 할 수 있습니다.

parseFromString(str, type)방법에 따라 다음 단계를 실행해야합니다 유형 :

  • "text/html"

    str 을 구문 분석 HTML parser하고 새로 작성된을 리턴하십시오 Document.

    스크립팅 플래그는 "disabled"로 설정해야합니다.

    노트

    script요소는 실행 불가능으로 표시되고 컨텐츠는 noscript마크 업으로 구문 분석됩니다.

그것은이 질문의 범위를 넘어,하지만 참고하시기 바랍니다 당신은 구문 분석 DOM 노드 자체 (뿐만 아니라 자신의 텍스트 내용)을 복용하고 라이브 문서 DOM로 이동하는 경우, 그것은 그들의 스크립트가 다시 활성화 될 가능성이 있다고, 거기 수 보안 문제가됩니다. 나는 그것을 연구하지 않았으므로주의하십시오.


5
NodeJ에 대한 대안?
coderInrRain

284

모든 인코딩 된 HTML 엔터티 또는 그 &amp;자체 를 디코딩해야 합니까?

처리 해야하는 경우 &amp;다음을 수행 할 수 있습니다.

var decoded = encoded.replace(/&amp;/g, '&');

모든 HTML 엔티티를 디코딩 해야하는 경우 jQuery없이 수행 할 수 있습니다.

var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;

이 답변의 이전 버전에서 보안 허점을 강조하고 아래 에서 잠재적 XSS 취약성을 완화 textarea하기보다는 사용 하는 것이 좋습니다 div. 이러한 취약점은 jQuery를 사용하든 일반 JavaScript를 사용하든 존재합니다.


16
조심해! 이것은 잠재적으로 안전하지 않습니다. 그렇다면 encoded='<img src="bla" onerror="alert(1)">'위의 스 니펫에 경고가 표시됩니다. 즉, 인코딩 된 텍스트가 사용자 입력에서 오는 경우이 스 니펫으로 텍스트를 디코딩하면 XSS 취약점이 발생할 수 있습니다.
Mark Amery

@MarkAmery 보안 전문가는 아니지만 null텍스트를 얻은 후 즉시 div를 설정 하면 img의 경고가 발생하지 않습니다.- jsfiddle.net
Mottie

4
@Mottie 어떤 브라우저에서 당신을 위해 일했지만 alert(1)OS X의 Chrome에서 여전히 나를 위해 작동하는지 확인하십시오. 이 해킹의 안전한 변형을 원한다면을 사용하십시오textarea .
Mark Amery

단순한 regexp의 경우 +1은 한 종류의 html 엔티티에 대한 대안을 대체합니다. python flask 앱에서 템플릿으로 html 데이터가 보간되는 경우이를 사용하십시오.
OzzyTheGiant

노드 서버에서이를 수행하는 방법은 무엇입니까?
Mohammad Kermani 2016 년

44

Matthias Bynens는이를위한 라이브러리를 가지고 있습니다 : https://github.com/mathiasbynens/he

예:

console.log(
    he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ")
);
// Logs "Jörg & Jürgen rocked to & fro"

요소의 HTML 내용을 설정하고 텍스트 내용을 다시 읽는 것과 관련된 해킹보다 선호하는 것이 좋습니다. 이러한 접근 방식은 작동 할 수 있지만 신뢰할 수없는 사용자 입력에 사용되는 경우 믿을 수 없을 정도로 위험하며 XSS 기회를 제공합니다.

실제로 라이브러리에로드 할 수 없다면 이 답변textarea설명 된 해킹 을 거의 중복되는 질문에 사용할 수 있습니다.

function decodeEntities(encodedString) {
    var textArea = document.createElement('textarea');
    textArea.innerHTML = encodedString;
    return textArea.value;
}

console.log(decodeEntities('1 &amp; 2')); // '1 & 2'

그러나 이와 비슷한 접근 방식에 영향을 미치는 보안 문제에 유의하십시오. 링크 된 답변에 나열되어 있습니다! 이 접근 방식은 해킹이며 앞으로 허용되는 내용 textarea(또는 특정 브라우저의 버그)에 대한 향후 변경으로 인해 언젠가 XSS 취약점이있는 코드에 의존 할 수 있습니다.


Matthias Bynens의 도서관 he은 절대적으로 훌륭합니다! 추천 해 주셔서 감사합니다!
Pedro A

23
var htmlEnDeCode = (function() {
    var charToEntityRegex,
        entityToCharRegex,
        charToEntity,
        entityToChar;

    function resetCharacterEntities() {
        charToEntity = {};
        entityToChar = {};
        // add the default set
        addCharacterEntities({
            '&amp;'     :   '&',
            '&gt;'      :   '>',
            '&lt;'      :   '<',
            '&quot;'    :   '"',
            '&#39;'     :   "'"
        });
    }

    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
    };
})();

이것은 ExtJS 소스 코드에서 가져온 것입니다.


4
-1; 이것은 대부분의 명명 된 엔티티를 처리하지 못합니다. 예를 들어을 htmlEnDecode.htmlDecode('&euro;')반환해야 '€'하지만 대신을 반환 해야 합니다 '&euro;'.
Mark Amery

17

element.innerText 또한 트릭을 수행합니다.


15

Lodash unescape / escape 기능을 사용할 수 있습니다 https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

const str = unescape('fred, barney, &amp; pebbles');

str은 될 것이다 'fred, barney, & pebbles'


1
" 'lodash / unescape'에서 _unescape를 가져 오는 것이 좋습니다." 그래서 같은 이름의 사용되지 않는 자바 스크립트 함수와 충돌하지 않는 : 언 이스케이프
릭 Penabella

14

나와 같은 것을 찾고있는 경우 멋지고 안전한 JQuery 메소드가 있습니다.

https://api.jquery.com/jquery.parsehtml/

당신은 f.ex 할 수 있습니다. 콘솔에 이것을 입력하십시오 :

var x = "test &amp;";
> undefined
$.parseHTML(x)[0].textContent
> "test &"

따라서 $ .parseHTML (x)는 배열을 반환하며 텍스트 내에 HTML 마크 업이 있으면 array.length는 1보다 큽니다.


나를 위해 완벽하게 일했습니다. 이것은 내가 찾던 것입니다. 감사합니다.
Jonathan Nielsen

1
x의 값이 있으면 <script>alert('hello');</script>충돌합니다. 현재 jQuery에서는 실제로 스크립트를 실행하려고 시도하지 않지만 결과 [0]를 나타내 undefined므로 호출 textContent이 실패하고 스크립트가 중지됩니다. $('<div />').html(x).text();안전 보이는 -을 통해 gist.github.com/jmblog/3222899
앤드류 Hodgkinson

@AndrewHodgkinson 네,하지만 질문은 "디코딩 & JavaScript에서 다시 &로"였습니다. 따라서 x의 내용을 먼저 테스트하거나 올바른 경우에만 사용해야합니다.
cslotty

나는 그것이 어떻게 따르는 지 실제로 알지 못한다. 위의 코드는 모든 경우에 작동합니다. 그리고 x가 필요한 수정 값을 얼마나 정확하게 "확인"할 수 있습니까? 위의 스크립트 예제에서 '& amp;' 실제로 수정이 필요 했습니까? OP의 문자열이 어디에서 왔는지 알 수 없으므로 악의적 인 입력을 고려해야합니다.
Andrew Hodgkinson '12

@AndrewHodgkinson 나는 당신의 배려를 좋아하지만 여기서 질문은 아닙니다. 그래도 그 질문에 대답하십시오. 스크립트 태그 f.ex를 제거 할 수 있다고 생각합니다.
cslotty

8

jQuery가 인코딩 및 디코딩합니다. 그러나 div가 아닌 textarea 태그를 사용해야합니다.

var str1 = 'One & two & three';
var str2 = "One &amp; two &amp; 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>


2
-1 이전 jQuery 버전에 대한 놀라운 보안 허점이 있기 때문에 일부 사용자에게는 여전히 상당한 사용자 기반이있을 수 있습니다. 이러한 버전은에 전달 된 HTML에서 스크립트감지하고 명시 적으로 평가 합니다 .html(). 따라서 여기에서도 textarea보안을 보장하기에는 충분하지 않습니다. 이 작업에 jQuery를 사용하지 않고 일반 DOM API로 동등한 코드를 작성하는 것이 좋습니다 . (예, jQuery를하여 그 이전 동작 미친 무서운 것입니다.)
마크 Amery

지적 해 주셔서 감사합니다. 그러나 질문에는 스크립트 삽입을 확인하기위한 요구 사항이 포함되어 있지 않습니다. 이 질문은 특히 웹 서버에서 렌더링 된 html에 대해 묻습니다. 웹 서버에 저장된 HTML 컨텐츠는 저장하기 전에 스크립트 삽입에 대한 유효성을 검증해야합니다.
Jason Williams

4

먼저 <span id="decodeIt" style="display:none;"></span>몸 어딘가에

다음으로 innerHTML로 디코딩 할 문자열을 다음에 지정하십시오.

document.getElementById("decodeIt").innerHTML=stringtodecode

드디어,

stringtodecode=document.getElementById("decodeIt").innerText

전체 코드는 다음과 같습니다.

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

1
-1; 이것은 신뢰할 수없는 입력에 사용하는 것이 안전하지 않습니다. 예를 들어, stringtodecode와 같은 것이 포함 된 경우 어떻게되는지 고려하십시오 <script>alert(1)</script>.
Mark Amery

2

일반적인 것들을 잡는 자바 스크립트 솔루션 :

var map = {amp: '&', lt: '<', gt: '>', quot: '"', '#039': "'"}
str = str.replace(/&([^;]+);/g, (m, c) => map[c])

이것은 https://stackoverflow.com/a/4835406/2738039 의 반대입니다.


map[c] || ''인식 할 수 없는 것을 사용하면 다음 과 같이 표시되지 않습니다undefined
Eldelshell

매우 제한된 범위; -1.
Mark Amery

2
+1, 그 이상unescapeHtml(str){ var map = {amp: '&', lt: '<', le: '≤', gt: '>', ge: '≥', quot: '"', '#039': "'"} return str.replace(/&([^;]+);/g, (m, c) => map[c]|| '') }
Trần Quốc Hoài 새로운 2015 년

수동 범위. 권장하지 않습니다.
Sergio A.

2

한 줄 남자의 경우 :

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;

console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));

2

이 질문은 출처를 지정하지는 않지만 가능한 x경우 악의적 인 (또는 자체 응용 프로그램에서 예기치 않은) 입력을 방어하는 것이 좋습니다. 예를 들어 x값이 이라고 가정 &amp; <script>alert('hello');</script>합니다. jQuery에서이를 처리하는 안전하고 간단한 방법은 다음과 같습니다.

var x    = "&amp; <script>alert('hello');</script>";
var safe = $('<div />').html(x).text();

// => "& alert('hello');"

https://gist.github.com/jmblog/3222899 통해 찾을 수 있습니다 . 나는 그것이 적어도 짧게이다 주어진이 솔루션을 사용하지 않는 많은 이유를 볼 수없는 경우 어떤 대안보다 짧은 XSS 방어를 제공한다.

(나는 원래 이것을 주석으로 게시했지만 동일한 스레드에서 후속 주석이 요청한 이후 답변으로 추가하고 있습니다).


1

JSON 배열에서 & 제거하기 위해 모든 것을 시도했습니다. 위의 예제 중 어느 것도 없지만 https : //.com/users/2030321/chris는 훌륭한 해결책을 제시하여 문제를 해결했습니다.

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

JSON 데이터를 배열로 가져 오는 모달 창에 삽입하는 방법을 이해하지 못했기 때문에 사용하지 않았지만 예제를 기반으로 시도했지만 작동했습니다.

var modal = document.getElementById('demodal');
$('#ampersandcontent').text(replaceAll(data[0],"&amp;", "&"));

간단하고 작동하기 때문에 좋아하지만 왜 널리 사용되지 않는지 잘 모르겠습니다. 간단한 솔루션을 찾기 위해 하이 앤 로우를 검색했습니다. 나는 구문에 대한 이해를 계속 추구하며 이것을 사용할 위험이 있다면. 아직 아무것도 찾지 못했습니다.


첫 번째 제안은 약간 까다 롭지 만 많은 노력을 기울이지 않아도 훌륭하게 작동합니다. 반면에 두 번째는 문자를 해독하기 위해 무차별 대입 만 사용합니다. 이는 완전한 디코딩 기능을 달성하기 위해 많은 노력과 시간이 소요될 수 있음을 의미합니다. 그래서 OP의 문제를 해결하기 위해 아무도 그런 식으로 사용하지 않는 것입니다.
Sergio A.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.