HTML 태그를 HTML 엔티티로 이스케이프하는 가장 빠른 방법은 무엇입니까?


99

나는 일을 포함하는 크롬 확장 프로그램 쓰고 있어요 많은 문자열 살균 : 다음 작업을 할 수 변환하여, HTML 태그를 포함 <, >&&lt;, &gt;그리고 &amp;각각을,.

(즉, PHP와 동일 htmlspecialchars(str, ENT_NOQUOTES)합니다. 큰 따옴표 문자를 변환 할 필요가 없다고 생각합니다.)

이것은 내가 지금까지 찾은 가장 빠른 기능입니다.

function safe_tags(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ;
}

그러나 한 번에 수천 개의 문자열을 실행해야 할 때 여전히 큰 지연이 있습니다.

누구든지 이것을 개선 할 수 있습니까? 차이가 나는 경우 대부분 10 ~ 150 자 사이의 문자열에 사용됩니다.

(내가 가지고 있던 한 가지 아이디어는보다 큼 기호를 인코딩하는 것을 귀찮게하지 않는 것이 었습니다. 이로 인해 실제 위험이 있습니까?)


2
왜? 이 작업을 수행하려는 대부분의 경우 데이터를 DOM에 삽입하려고합니다.이 경우 이스케이프를 잊어 버리고 여기에서 textNode를 만들어야합니다.
Quentin

1
@David Dorward : 아마도 그는 POST 데이터를 삭제하고 싶었고 서버가 데이터를 올바르게 왕복하지 않습니다.
Lie Ryan

4
@Lie - 그렇다면, 그 해결책은 "당신은 큰 XSS 구멍을 가지고 피트의 위해, 서버를 수정"입니다
쿠엔틴

2
@David Dorward : 그가 서버를 제어 할 수없는 경우 일 수 있습니다. 저는 최근에 제가 대학 웹 사이트에서 싫어하는 몇 가지 문제를 해결하기 위해 greasemonkey 스크립트를 작성하는 상황에 처했습니다. 내가 제어 할 수없는 서버에서 POST를 수행하고 javascript를 사용하여 POST 데이터를 삭제해야했습니다. . 웹 관리자가 웹 사이트를 수정 해 달라는 요청을 무시하고 있었기 때문에 다른 선택의 여지가 없었습니다.
Lie Ryan

1
div에 오류 메시지를 표시해야하는 사용 사례가 있습니다. 오류 메시지에는 HTML과 줄 바꿈이 포함될 수 있습니다. HTML을 이스케이프하고 개행 문자를 <br>로 바꾸고 싶습니다. 그런 다음 결과를 표시 할 div에 넣습니다.
mozey

답변:


84

콜백 함수를 전달하여 교체를 수행 할 수 있습니다.

var tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
};

function replaceTag(tag) {
    return tagsToReplace[tag] || tag;
}

function safe_tags_replace(str) {
    return str.replace(/[&<>]/g, replaceTag);
}

다음은 성능 테스트입니다. http://jsperf.com/encode-html-entitiesreplace 함수를 반복적 으로 호출하고 Dmitrij가 제안한 DOM 메서드를 사용하는 것과 비교 합니다.

당신의 길은 더 빠른 것 같습니다 ...

그래도 왜 필요합니까?


2
탈출 할 필요가 없습니다 >.

6
실제로 html 요소의 속성에 이스케이프 된 값을 넣으면> 기호를 이스케이프해야합니다. 그렇지 않으면 해당 html 요소에 대한 태그가 손상됩니다.
Zlatin Zlatev

1
일반 텍스트에서 이스케이프 문자는 드뭅니다. 최대 속도에 관심이있는 경우 필요할 때만 교체를 호출하는 것이 좋습니다.if (/[<>&"]/.test(str) { ... }
Vitaly

3
@callum : 아니요. "무언가 잘못 될 수있다"고 생각하는 경우를 열거하는 데 관심이 없습니다. 저는 표준 코딩에 관심이 있습니다 (예기치 않거나 잊혀진 사례 가 정의상 당신 해칠 수 없습니다 ). 이것이 얼마나 중요한지 강조 할 수 없습니다. >HTML의 특수 문자이므로 이스케이프하십시오. 그렇게 간단합니다. :)
궤도의 가벼운 경주

4
@LightnessRacesinOrbit 질문이 가능한 가장 빠른 방법이기 때문에 관련이 있습니다. >교체 를 건너 뛸 수 있다면 더 빨라질 것입니다.
callum

104

이를 수행 할 수있는 한 가지 방법은 다음과 같습니다.

var escape = document.createElement('textarea');
function escapeHTML(html) {
    escape.textContent = html;
    return escape.innerHTML;
}

function unescapeHTML(html) {
    escape.innerHTML = html;
    return escape.textContent;
}

여기 데모가 있습니다.


데모를 재 설계했습니다. 다음은 전체 화면 버전입니다. jsfiddle.net/Daniel_Hug/qPUEX/show/light
Web_Designer

13
어떻게 / 무엇을 / 왜인지 확실하지 않지만 이것은 천재입니다.
rob_james 2014-06-18

4
리터럴 텍스트를 이스케이프하기 위해 TextArea 요소의 기존 코드를 활용하는 것 같습니다. 아주 좋아,이 작은 트릭이 다른 집을 찾을 거라고 생각합니다.
Ajax

3
@jazkat 나는 그 기능을 사용하지 않습니다. 내가 사용하는 이스케이프 변수는 예제에서 직접 정의합니다.
Web_Designer

2
하지만 이것은 공백 등을 잃어
Andrew

31

프로토 타입 함수로서의 Martijn의 방법 :

String.prototype.escape = function() {
    var tagsToReplace = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;'
    };
    return this.replace(/[&<>]/g, function(tag) {
        return tagsToReplace[tag] || tag;
    });
};

var a = "<abc>";
var b = a.escape(); // "&lt;abc&gt;"

12
이것에 추가하면 일반적으로 String에 대한 이스케이프 String아니기 때문에 escapeHtml 이어야합니다 . 즉이 String.escapeHtml정확하지만 String.escape, 문제를 제기 "무엇을 탈출?"
Lawrence Dol 2014 년

3
그래 좋은 생각이야. 나는 충돌을 피하기 위해 요즘 프로토 타입을 확장하는 것에서 멀어졌습니다.
Aram Kocharyan 2014 년

1
브라우저에서 Symbol을 지원하는 경우 대신이를 사용하여 문자열 키 네임 스페이스를 오염시키지 않을 수 있습니다. var escape = new Symbol ( "escape"); String.prototype [escape] = function () {...}; "텍스트"[탈출] ();
Ajax

12

더 빠르고 / 짧은 솔루션은 다음과 같습니다.

escaped = new Option(html).innerHTML

이것은 Option 요소가 이러한 종류의 자동 이스케이프를 수행하는 생성자를 유지하는 JavaScript의 이상한 흔적과 관련이 있습니다.

https://github.com/jasonmoo/t.js/blob/master/t.js에 대한 크레딧


1
깔끔한 한 줄이지 만 정규식 다음으로 가장 느린 방법 입니다. 공백을 제거한 또한, 여기에 텍스트가에 따라, 할 수 있습니다 사양
ShortFuse

@ShortFuse의 "가장 느린 방법"링크를 사용하면 시스템에 RAM이 부족하고 (최대 6GB 여유 공간) firefox가 메모리가 부족하기 직전에 할당을 중지하는 것처럼 보이므로 문제가되는 프로세스를 죽이는 대신 Linux가 거기에 앉아 할 수 있도록합니다. 하드 전원 끄기.
Luc

11

AngularJS 소스 코드에는 angular-sanitize.js 내부 버전도 있습니다 .

var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
    // Match everything outside of normal chars and " (quote character)
    NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
/**
 * Escapes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} escaped text
 */
function encodeEntities(value) {
  return value.
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, function(value) {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

1
영숫자가 아닌 정규식은 강렬합니다. 나는 생각하지 않는다 | 그래도 표현이 필요합니다.
Ajax


9

올인원 스크립트 :

// HTML entities Encode/Decode

function htmlspecialchars(str) {
    var map = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        "\"": "&quot;",
        "'": "&#39;" // ' -> &apos; for XML only
    };
    return str.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function htmlspecialchars_decode(str) {
    var map = {
        "&amp;": "&",
        "&lt;": "<",
        "&gt;": ">",
        "&quot;": "\"",
        "&#39;": "'"
    };
    return str.replace(/(&amp;|&lt;|&gt;|&quot;|&#39;)/g, function(m) { return map[m]; });
}
function htmlentities(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.innerHTML;
}
function htmlentities_decode(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.value;
}

http://pastebin.com/JGCVs0Ts


나는 반대표를 던지지 않았지만 모든 정규식 스타일 바꾸기는 유니 코드를 인코딩하지 못합니다 ... 그래서 외국어를 사용하는 사람은 실망 할 것입니다. 위에서 언급 한 <textarea> 트릭은 정말 멋지고 모든 것을 빠르고 안전하게 처리합니다.
Ajax

정규식은 라틴어가 아닌 여러 유니 코드 문자로 잘 작동합니다. 나는 다른 것을 기대하지 않을 것입니다. 이것이 작동하지 않을 것이라고 생각하십니까? HTML 엔티티가 필요한 단일 바이트 코드 페이지를 생각하고 있습니까? 그것이 바로 3 번째와 4 번째 기능의 목적이며 명시 적으로 1 번째와 두번째 기능이 아닙니다. 나는 차별화를 좋아합니다.
ygoe

@LonelyPixel 나는 당신이 그를 언급하지 않으면 그가 당신의 댓글을 볼 수 없을 것이라고 생각합니다 ( "단 한 명의 추가 사용자 만 알릴 수 있습니다. 게시물 소유자는 항상 알립니다")
baptx

타겟 알림이 존재하는지 전혀 몰랐습니다. @Ajax는 위의 내 의견을 참조하십시오.
ygoe

@LonelyPixel 나는 지금 본다. 어떤 이유로 나는이 답변에 텍스트 영역 스타일 대체가 있다고 생각하지 않았습니다. 나는 실제로 Mandarin과 같은 이중 코드 포인트 큰 유니 코드 값을 생각했습니다. 내 말은, 정규식을 충분히 현명하게 만들 수는 있지만 브라우저 공급 업체가 취할 수있는 바로 가기를 살펴보면 텍스트 영역이 (완전히 유능한 정규식보다) 훨씬 빠를 것이라는 확신이들 것입니다. 누군가이 답변에 대한 벤치 마크를 게시 했습니까? 나는 하나를 보았다고 맹세했다.
Ajax

2

function encode(r) {
  return r.replace(/[\x26\x0A\x3c\x3e\x22\x27]/g, function(r) {
	return "&#" + r.charCodeAt(0) + ";";
  });
}

test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');

/*
 \x26 is &ampersand (it has to be first),
 \x0A is newline,
 \x22 is ",
 \x27 is ',
 \x3c is <,
 \x3e is >
*/
<textarea id=test rows=11 cols=55>www.WHAK.com</textarea>


1

속도에 대해 완전히 확신 할 수는 없지만 단순성을 찾고 있다면 lodash / underscore 이스케이프 기능을 사용하는 것이 좋습니다 .


0

" 를 처리하는 단일 함수로서의 Martijn의 방법 mark ( javascript에서 사용) :

function escapeHTML(html) {
    var fn=function(tag) {
        var charsToReplace = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&#34;'
        };
        return charsToReplace[tag] || tag;
    }
    return html.replace(/[&<>"]/g, fn);
}

0

XMLSerializer더미에 추가하겠습니다 . 객체 캐싱을 사용하지 않고 가장 빠른 결과를 제공합니다 (시리얼 라이저 나 텍스트 노드가 아님).

function serializeTextNode(text) {
  return new XMLSerializer().serializeToString(document.createTextNode(text));
}

추가 된 보너스는 텍스트 노드와 다르게 직렬화 된 속성을 지원한다는 것입니다.

function serializeAttributeValue(value) {
  const attr = document.createAttribute('a');
  attr.value = value;
  return new XMLSerializer().serializeToString(attr);
}

텍스트 노드속성 값 모두에 대한 사양을 확인하여 실제로 대체되는 것을 볼 수 있습니다. . 전체 문서에는 더 많은 노드 유형이 있지만 개념은 동일합니다.

성능면에서 캐시되지 않을 때 가장 빠릅니다. 캐싱을 허용 innerHTML하면 자식 Text 노드가있는 HTMLElement 를 호출 하는 것이 가장 빠릅니다. Regex는 가장 느릴 것입니다 (다른 의견에서 입증 됨). 물론 XMLSerializer는 다른 브라우저에서 더 빠를 수 있지만 내 (제한된) 테스트에서는 a innerHTML가 가장 빠릅니다.


가장 빠른 한 줄 :

new XMLSerializer().serializeToString(document.createTextNode(text));

캐싱으로 가장 빠름 :

const cachedElementParent = document.createElement('div');
const cachedChildTextNode = document.createTextNode('');
cachedElementParent.appendChild(cachedChildTextNode);

function serializeTextNode(text) {
  cachedChildTextNode.nodeValue = text;
  return cachedElementParent.innerHTML;
}

https://jsperf.com/htmlentityencode/1


-3

쇼에 조금 늦었지만 encodeURIComponent ()decodeURIComponent () 사용에 문제가 있습니까?


1
그들은 완전히 관련이없는 일을합니다
callum

1
아마도 내가 들어 본 "완전히"라는 단어의 가장 큰 남용 일 것입니다. 예를 들어, 주요 주제 질문과 관련하여 html 태그에 관계없이 html 문자열을 디코딩하는 데 사용할 수 있습니다 (분명히 저장상의 이유로), 그런 다음 필요할 때 다시 html로 쉽게 다시 인코딩 할 수 있습니다.
suncat100
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.