ถอดรหัส & amp; กลับไปที่ & ใน JavaScript


229

ฉันมีสตริงเหมือน

var str = 'One & two & three';

แสดงผลเป็น HTML โดยเว็บเซิร์ฟเวอร์ ฉันต้องการแปลงสตริงเหล่านั้นให้เป็น

'One & two & three'

ขณะนี้นั่นคือสิ่งที่ฉันกำลังทำ (ด้วยความช่วยเหลือของ jQuery):

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

อย่างไรก็ตามฉันมีความรู้สึกไม่มั่นคงที่ฉันทำผิด ฉันเหนื่อย

unescape("&")

แต่ดูเหมือนว่ามันจะใช้งานไม่ได้และไม่ถอดรหัส / ถอดรหัสรหัสส่วนประกอบ

มีวิธีอื่นที่เป็นชนพื้นเมืองและหรูหรากว่านี้หรือไม่?


ฟังก์ชั่นขนาดใหญ่ที่รวมอยู่ในบทความนี้ดูเหมือนว่าจะทำงานได้ดี: blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspxฉันไม่คิดว่ามันเป็นทางออกที่ฉลาดที่สุด แต่ใช้งานได้
Matias

1
เนื่องจากสตริงที่มีเอนทิตี HTML นั้นเป็นสิ่งที่แตกต่างจากสตริงที่เข้ารหัสescape d หรือURIฟังก์ชันเหล่านั้นจะไม่ทำงาน
Marcel Korpel

1
@Matias ทราบว่าหน่วยงานที่ตั้งชื่อใหม่ได้รับการเพิ่ม HTML (เช่นผ่านทาง HTML 5 ข้อมูลจำเพาะ) ตั้งแต่ฟังก์ชั่นที่ได้รับการประพันธ์ในปี 2003 - 𝕫เช่นมันไม่รู้จัก นี่เป็นปัญหาของสเปคที่พัฒนาขึ้น ดังนั้นคุณควรเลือกเครื่องมือที่ได้รับการดูแลรักษาเพื่อแก้ไขด้วย
Mark Amery

1
@ Markymery ใช่ฉันเห็นด้วยทั้งหมด! เป็นประสบการณ์ที่ดีที่ได้กลับมาที่คำถามนี้หลังจากสองสามปีขอบคุณ!
Matias

คำตอบ:


104

ตัวเลือกที่ทันสมัยกว่าสำหรับการตีความ HTML (ข้อความและอื่น ๆ ) จาก JavaScript คือการสนับสนุน HTML ในDOMParserAPI ( ดูที่นี่ใน 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 JavaScript ที่ไม่ได้เปิดใช้งานสำหรับเอกสารที่แยกวิเคราะห์ดังนั้นเราสามารถดำเนินการแปลงข้อความนี้โดยไม่ต้องกังวลด้านความปลอดภัย

parseFromString(str, type)วิธีการต้องเรียกใช้ขั้นตอนเหล่านี้ขึ้นอยู่กับประเภท :

  • "text/html"

    การแยกวิเคราะห์STRกับและกลับที่สร้างขึ้นใหม่HTML parserDocument

    ต้องตั้งค่าสถานะสคริปต์เป็น "ปิดใช้งาน"

    บันทึก

    scriptองค์ประกอบได้รับการทำเครื่องหมายไม่สามารถดำเนินการได้และเนื้อหาของการnoscriptแยกวิเคราะห์เป็นมาร์กอัป

อยู่นอกเหนือขอบเขตของคำถามนี้ แต่โปรดทราบว่าหากคุณใช้โหนด DOM แยกวิเคราะห์ตัวเอง (ไม่ใช่แค่เนื้อหาข้อความ) และย้ายพวกเขาไปที่ DOM เอกสารสดเป็นไปได้ว่าสคริปต์ของพวกเขาจะถูกเปิดใช้งานอีกครั้งและอาจ เป็นกังวลด้านความปลอดภัย ฉันยังไม่ได้ทำการวิจัยดังนั้นโปรดใช้ความระมัดระวัง


5
ทางเลือกอื่นสำหรับ NodeJs?
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;

โปรดรับทราบความคิดเห็นของ Mark ด้านล่างซึ่งเน้นถึงช่องโหว่ด้านความปลอดภัยในคำตอบก่อนหน้านี้และแนะนำให้ใช้textareaแทนdivการลดช่องโหว่ XSS ที่อาจเกิดขึ้น ช่องโหว่เหล่านี้มีอยู่ไม่ว่าคุณจะใช้ jQuery หรือ JavaScript ธรรมดา


16
ระวัง! สิ่งนี้อาจไม่ปลอดภัย หากencoded='<img src="bla" onerror="alert(1)">'ข้อมูลโค้ดด้านบนจะแสดงการแจ้งเตือน ซึ่งหมายความว่าหากข้อความที่เข้ารหัสของคุณมาจากอินพุตของผู้ใช้การถอดรหัสด้วยตัวอย่างนี้อาจแสดงช่องโหว่ XSS
Mark Amery

@MarkAmery ฉันไม่ใช่ผู้เชี่ยวชาญด้านความปลอดภัย แต่ดูเหมือนว่าคุณจะตั้งค่า div เป็นnullทันทีหลังจากได้รับข้อความการแจ้งเตือนใน img ไม่ได้ถูกไล่ออก - jsfiddle.net/Mottie/gaBeb/128
Mottie

4
บันทึก @Mottie แน่ใจว่าเบราว์เซอร์ที่ทำงานให้คุณใน แต่alert(1)ไฟยังคงสำหรับฉันใน Chrome บน OS X ได้หากคุณต้องการเป็นตัวแปรที่ปลอดภัยของสับนี้ให้ลองใช้ textarea
Mark Amery

+1 สำหรับ regexp แบบง่ายแทนที่ทางเลือกสำหรับเอนทิตี html เพียงชนิดเดียว ใช้สิ่งนี้หากคุณคาดหวังว่าข้อมูล html จะถูกสอดแทรกจากแอพ python flask ไปยังเทมเพลต
OzzyTheGiant

วิธีการทำเช่นนี้บนเซิร์ฟเวอร์โหนด
Mohammad Kermani

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นั้นยอดเยี่ยมมาก! ขอบคุณมากสำหรับคำแนะนำ!
เปโดร 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


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
น่าจะดีกว่าที่จะทำ "import _unescape จาก 'lodash / unescape';" ดังนั้นจึงไม่ขัดแย้งกับฟังก์ชั่นจาวาสคริปต์ที่เลิกใช้ในชื่อเดียวกัน: unescape
Rick Penabella

14

ในกรณีที่คุณกำลังมองหามันเช่นฉัน - ในขณะที่มีวิธีการ JQuery ที่ดีและปลอดภัย

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

คุณสามารถ f.ex พิมพ์สิ่งนี้ในคอนโซลของคุณ:

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

ดังนั้น $ .parseHTML (x) จะส่งกลับอาร์เรย์และถ้าคุณมีมาร์กอัพ HTML ภายในข้อความอาเรย์จะมีความยาวมากกว่า 1


ทำงานอย่างสมบูรณ์แบบสำหรับฉันนี่คือสิ่งที่ฉันกำลังมองหาขอบคุณ
Jonathan Nielsen

1
หากxมีค่า<script>alert('hello');</script>ข้างต้นจะผิดพลาด ใน jQuery ปัจจุบันจะไม่พยายามเรียกใช้สคริปต์ แต่[0]จะให้ผลundefinedดังนั้นการเรียกtextContentจะล้มเหลวและสคริปต์ของคุณจะหยุดอยู่ที่นั่น $('<div />').html(x).text();ดูปลอดภัยยิ่งขึ้น - ผ่านgist.github.com/jmblog/3222899
Andrew Hodgkinson

@AndrewHodgkinson ใช่ แต่คำถามคือ "Decode & amp; กลับไปที่ & ใน JavaScript" - ดังนั้นคุณจะทดสอบเนื้อหาของ x ก่อนหรือให้แน่ใจว่าคุณใช้มันในกรณีที่ถูกต้องเท่านั้น
cslotty

ฉันไม่เห็นวิธีการดังต่อไปนี้ รหัสข้างต้นใช้งานได้ในทุกกรณี และคุณจะ "ทำให้แน่ใจ" ว่ามูลค่าของ x จำเป็นต้องได้รับการแก้ไขอย่างไร และจะเกิดอะไรขึ้นถ้าตัวอย่างสคริปต์ด้านบนแจ้งเตือน '& amp;' แล้วมันต้องการการแก้ไขจริงเหรอ? เราไม่รู้ว่าสายของ OP มาจากไหนจึงต้องพิจารณาอินพุตที่เป็นอันตราย
Andrew Hodgkinson

@AndrewHodgkinson ฉันชอบการพิจารณาของคุณ แต่นั่นไม่ใช่คำถามที่นี่ แต่อย่าลังเลที่จะตอบคำถามนั้น ฉันเดาว่าคุณสามารถลบแท็กสคริปต์ f.ex
cslotty

8

jQuery จะเข้ารหัสและถอดรหัสให้คุณ อย่างไรก็ตามคุณต้องใช้แท็ก textarea ไม่ใช่ div

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 นั้นบ้าและแย่มาก)
Mark 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

โซลูชัน javascript ที่ดึงดูดคนทั่วไป:

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ầnQuốcHoà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://stackoverflow.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.