web-dev-qa-db-ja.com

入力フィールドから属性を読み取るとHTMLエンコードが失われる

JavaScriptを使用して、隠しフィールドから値を取り出してテキストボックスに表示します。隠しフィールドの値はエンコードされています。

例えば、

<input id='hiddenId' type='hidden' value='chalk &amp; cheese' />

引き込まれる

<input type='text' value='chalk &amp; cheese' />

隠しフィールドから値を取得するためにjQueryを使用します(この時点でエンコーディングを失います)。

$('#hiddenId').attr('value')

問題は、隠しフィールドからchalk &amp; cheeseを読むと、JavaScriptがエンコーディングを失っているようです。値をchalk & cheeseにしたくありません。リテラルamp;を保持したいです。

文字列をHTMLエンコードするJavaScriptライブラリまたはjQueryメソッドはありますか。

712
AJM

編集: この回答はかなり前に投稿されたもので、htmlDecode関数にXSSの脆弱性が導入されました。一時要素をdivからtextareaに変更してXSSの可能性を減らしました。しかし、今日では、 other anwswer で提案されているようにDOMParser APIを使用することをお勧めします。


私はこれらの機能を使う:

function htmlEncode(value){
  // Create a in-memory element, set its inner text (which is automatically encoded)
  // Then grab the encoded contents back out. The element never exists on the DOM.
  return $('<textarea/>').text(value).html();
}

function htmlDecode(value){
  return $('<textarea/>').html(value).text();
}

基本的にdiv要素はメモリ内に作成されますが、文書に追加されることはありません。

htmlEncode関数で、要素のinnerTextを設定し、エンコードされたinnerHTMLを取得します。 htmlDecode関数で、要素のinnerHTML値を設定し、innerTextを取得します。

実行中の例を確認してください ここ

1052
CMS

JQueryのトリックは引用符をエンコードしません、そしてIEではあなたの空白を取り除きます。

Djangoの escape templateタグに基づいていますが、これはすでによく使われテストされていると思いますので、必要なことをするこの関数を作りました。

空白除去の問題に対する回避策のどれよりも間違いなく簡単で(そしておそらくもっと速い) - そしてそれは引用符をエンコードします。これは例えば属性値の中で結果を使うつもりなら不可欠です。

function htmlEscape(str) {
    return str
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

// I needed the opposite function today, so adding here too:
function htmlUnescape(str){
    return str
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, "'")
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&');
}

更新2013-06-17:
最速のエスケープを探すために、このreplaceAllメソッドの実装を見つけました。
http://dumpsite.com/forum/index.php?topic=4.msg29#msg29
(ここでも参照されています: 文字列内の文字のすべてのインスタンスを置き換えるための最速の方法
ここでいくつかのパフォーマンス結果が得られます。
http://jsperf.com/htmlencoderegex/25

上記の組み込みのreplaceチェーンと同じ結果文字列を返します。誰かがなぜそれが速いのかを説明できたらうれしいです!?

更新2015-03-04:
AngularJSがまさに上記の方法を使用していることに気付きました。
https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435

彼らは、いくつかの改良点を追加しました - それらは あいまいなUnicodeの問題を処理しているようです およびすべての非英数字をエンティティに変換すること。あなたの文書にUTF8文字セットが指定されている限り、後者は必要ではないという印象を受けました。

(4年後)Djangoはまだこれらのことのどちらもしていないことに注意してください。だから、それらがどれほど重要かはわかりません。
https://github.com/Django/django/blob/1.8b1/Django/utils/html.py#L44

更新2016-04-06:
スラッシュ/をエスケープすることもできます。これは正しいHTMLエンコーディングには必要ありませんが、 OWASPによって推奨されています XSSに対する安全対策として。 (コメントでこれを示唆してくれた@JNFに感謝します)

        .replace(/\//g, '&#x2F;');
542
Anentropic

これは、jQueryの.html()バージョンと.replace()バージョンの両方よりかなり速い、非jQueryバージョンです。これはすべての空白を保存しますが、jQueryバージョンのように引用符を処理しません。

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

スピード: http://jsperf.com/htmlencoderegex/17

speed test

デモ:  jsFiddle

出力:

output

スクリプト:

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

function htmlDecode( html ) {
    var a = document.createElement( 'a' ); a.innerHTML = html;
    return a.textContent;
};

document.getElementById( 'text' ).value = htmlEncode( document.getElementById( 'hidden' ).value );

//sanity check
var html = '<div>   &amp; hello</div>';
document.getElementById( 'same' ).textContent = 
      'html === htmlDecode( htmlEncode( html ) ): ' 
    + ( html === htmlDecode( htmlEncode( html ) ) );

HTML:

<input id="hidden" type="hidden" value="chalk    &amp; cheese" />
<input id="text" value="" />
<div id="same"></div>
78
ThinkingStiff

私はこれが古いものであることを知っています、しかし私は/のバリエーションを投稿したいと思いました - 受け入れられた答え は行を削除せずに動作します IE

function multiLineHtmlEncode(value) {
    var lines = value.split(/\r\n|\r|\n/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('\r\n');
}

function htmlEncode(value) {
    return $('<div/>').text(value).html();
} 
32
boca

アンダースコア_.escape() および _.unescape() を提供するメソッドを提供します。

> _.unescape( "chalk &amp; cheese" );
  "chalk & cheese"

> _.escape( "chalk & cheese" );
  "chalk &amp; cheese"
28
TJ VanToll

いい答えです。 jQuery 1.4.2でエンコードする値がundefinedまたはnullの場合、次のようなエラーが発生する可能性があります。

jQuery("<div/>").text(value).html is not a function

OR

Uncaught TypeError: Object has no method 'html'

解決策は、実際の値をチェックするように関数を修正することです。

function htmlEncode(value){ 
    if (value) {
        return jQuery('<div/>').text(value).html(); 
    } else {
        return '';
    }
}
12
leepowers

普通のJavaScriptを好む人のために、これが私がうまく使った方法です:

function escapeHTML (str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}
11

プロトタイプStringクラス が組み込まれています。もしあなたがPrototypeを使っているのであれば、Prototypeを使うようにしてください。

'<div class="article">This is an article</div>'.escapeHTML();
// -> "&lt;div class="article"&gt;This is an article&lt;/div&gt;"
5
Sinan Taifour

確かに、エンコーディングは失われていません。エンコードは、ページのロード中にマークアップパーサー(ブラウザ)によって使用されます。ソースが読み取られて解析され、ブラウザによってDOMがメモリにロードされると、エンコードはそれが表すものに解析されました。つまり、JSがメモリ内のすべてのものを読み取るために実行されるときには、取得される文字はエンコーディングが表すものになります。

ここでは厳密に意味論を扱っているかもしれませんが、エンコードの目的を理解してほしいと思いました。 「失われた」という言葉は、あるべきことがうまくいっていないように聞こえます。

5
JAAulde

Jqueryなしでより速く。あなたはあなたの文字列の中のすべての文字をエンコードすることができます:

function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}

あるいは、気にする主な文字(&、普通の文字、<、>、 "、 ')をターゲットにするだけです。

function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!\n\n"Safe" escape <script id=\'\'> & useful in <pre> tags!');

testing.innerHTML=test.value;

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
<textarea id=test rows="9" cols="55"></textarea>

<div id="testing">www.WHAK.com</div>
5
Dave Brown

これが簡単なJavaScriptソリューションです。 Stringオブジェクトを "HTMLEncode"メソッドで拡張しています。このメソッドは、パラメータなしで、またはパラメータ付きのオブジェクトに対して使用できます。

String.prototype.HTMLEncode = function(str) {
  var result = "";
  var str = (arguments.length===1) ? str : this;
  for(var i=0; i<str.length; i++) {
     var chrcode = str.charCodeAt(i);
     result+=(chrcode>128) ? "&#"+chrcode+";" : str.substr(i,1)
   }
   return result;
}
// TEST
console.log("stetaewteaw æø".HTMLEncode());
console.log("stetaewteaw æø".HTMLEncode("æåøåæå"))

要旨 "javascript用のHTMLEncodeメソッド" を作成しました。

4
Netsi1964

角度のサニタイズ ...に基づく(es6モジュール構文)

// ref: https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js
const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
const NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;

const decodeElem = document.createElement('pre');


/**
 * Decodes html encoded text, so that the actual string may
 * be used.
 * @param value
 * @returns {string} decoded text
 */
export function decode(value) {
  if (!value) return '';
  decodeElem.innerHTML = value.replace(/</g, '&lt;');
  return decodeElem.textContent;
}


/**
 * Encodes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} encoded text
 */
export function encode(value) {
  if (value === null || value === undefined) return '';
  return String(value).
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, value => {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, value => {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

export default {encode,decode};
3
Tracker1

私のpure-JS関数:

/**
 * HTML entities encode
 *
 * @param {string} str Input text
 * @return {string} Filtered text
 */
function htmlencode (str){

  var div = document.createElement('div');
  div.appendChild(document.createTextNode(str));
  return div.innerHTML;
}

JavaScript HTMLエンティティのエンコードとデコード

2
Nick Tsai

javaScriptには、単純なHTMLのEncode/Decodeメソッドはありません。

しかし、できることは、JSを使用して任意の要素を作成し、それを内部テキストに設定してからinnerHTMLを使用して読み取ることです。

たとえば、jQueryではこれでうまくいくはずです。

var helper = $('chalk & cheese').hide().appendTo('body');
var htmled = helper.html();
helper.remove();

またはこれらの線に沿って何か

2
Ken Egozi

ある入力フィールドから別の入力フィールドに値を移動するために値をエスケープ/エンコードする必要はありません。

<form>
 <input id="button" type="button" value="Click me">
 <input type="hidden" id="hiddenId" name="hiddenId" value="I like cheese">
 <input type="text" id="output" name="output">
</form>
<script>
    $(document).ready(function(e) {
        $('#button').click(function(e) {
            $('#output').val($('#hiddenId').val());
        });
    });
</script>

JSは生のHTMLやその他何も挿入しません。 DOMにvalueプロパティ(または属性、不確実)を設定するように指示するだけです。いずれにせよ、DOMはあなたのためにあらゆるエンコードの問題を処理します。 document.writeevalを使うような変なことをしているのでなければ、HTMLエンコーディングは事実上透明です。

結果を保持するために新しいテキストボックスを生成することについて話しているのであれば...それはまだ同じくらい簡単です。 HTMLの静的部分をjQueryに渡してから、返されるオブジェクトの残りのプロパティ/属性を設定するだけです。

$box = $('<input type="text" name="whatever">').val($('#hiddenId').val());
2
cHao

私は同様の問題を抱えており、JavaScriptからの関数encodeURIComponentを使用してそれを解決します( documentation

たとえば、あなたの場合あなたが使用するなら:

<input id='hiddenId' type='hidden' value='chalk & cheese' />

そして

encodeURIComponent($('#hiddenId').attr('value'))

あなたはchalk%20%26%20cheeseを得るでしょう。スペースも保持されます。

私の場合は、バックスラッシュを1つエンコードする必要があり、このコードは完全に機能します。

encodeURIComponent('name/surname')

そして私はname%2Fsurnameを得ました

2
Dmyan

私は私のDomain\User文字列のバックスラッシュに関するいくつかの問題に出くわしました。

これをAnentropicの答えからの他の脱出に追加しました

.replace(/\\/g, '&#92;')

私はここで見つけた: どのようにJavaScriptでバックスラッシュをエスケープするには?

1
spacebread
<script>
String.prototype.htmlEncode = function () {
    return String(this)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');

}

var aString = '<script>alert("I hack your site")</script>';
console.log(aString.htmlEncode());
</script>

出力します:&lt;script&gt;alert(&quot;I hack your site&quot;)&lt;/script&gt;

.htmlEncode()は一度定義されるとすべての文字列でアクセス可能になります。

1
Stuart Eske

Htmlは与えられた値をエンコードします

  var htmlEncodeContainer = $('<div />');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }
1
Sky Yip

純粋なJavaScriptで書かれた、MicrosoftのASPからのServer.HTMLEncode関数をエミュレートする、ちょっとだけです:

function htmlEncode(s) {
  var ntable = {
    "&": "amp",
    "<": "lt",
    ">": "gt",
    "\"": "quot"
  };
  s = s.replace(/[&<>"]/g, function(ch) {
    return "&" + ntable[ch] + ";";
  })
  s = s.replace(/[^ -\x7e]/g, function(ch) {
    return "&#" + ch.charCodeAt(0).toString() + ";";
  });
  return s;
}

結果{しないはアポストロフィをエンコードしますが、他のHTMLスペシャルと0x20-0x7eの範囲外の文字はエンコードします。

1
ReWrite

JQueryを使いたい場合私はこれを見つけました:

http://www.jquerysdk.com/api/jQuery.htmlspecialchars

(jQuery SDKが提供するjquery.stringプラグインの一部)

Prototypeの問題点は、それがJavaScriptの基本オブジェクトを拡張することであり、あなたが使ったかもしれないどんなjQueryとも互換性がないということです。もちろん、既にPrototypeを使用していて、jQueryを使用していないのであれば、問題にはなりません。

編集:また、これは、jQueryのためのプロトタイプの文字列ユーティリティのポートです:

http://stilldesigning.com/dotstring/ /

1
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のソースコードからのものです。

1
WaiKit Kung

ここで他のいくつかの答えを使用して、私は個別のエンコードされた文字の数(replace()への1回の呼び出し)に関係なく1回のパスですべての関連文字を置き換えるバージョンを作りました。

DOM APIの存在や他のライブラリに依存しません。

window.encodeHTML = (function() {
    function escapeRegex(s) {
        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    }
    var encodings = {
        '&'  : '&amp;',
        '"'  : '&quot;',
        '\'' : '&#39;',
        '<'  : '&lt;',
        '>'  : '&gt;',
        '\\' : '&#x2F;'
    };
    function encode(what) { return encodings[what]; };
    var specialChars = new RegExp('[' +
        escapeRegex(Object.keys(encodings).join('')) +
    ']', 'g');

    return function(text) { return text.replace(specialChars, encode); };
})();

一度走れば、あなたは今電話することができます

encodeHTML('<>&"\'')

&lt;&gt;&amp;&quot;&#39;を取得する

0
Hashbrown

Prototype.jsでescapeHTML()がしていることを選ぶ

このスクリプトを追加すると、HTMLをエスケープするのに役立ちます。

String.prototype.escapeHTML = function() { 
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
}

次のように、スクリプト内の文字列に対してescapeHTMLメソッドを呼び出すことができます。

var escapedString = "<h1>this is HTML</h1>".escapeHTML();
// gives: "&lt;h1&gt;this is HTML&lt;/h1&gt;"

Prototype.js全体を含めなくても、シンプルなソリューションを探している人に役立つことを願っています。

0
new_user
function encodeHTML(str) {
    return document.createElement("a").appendChild( 
        document.createTextNode(str)).parentNode.innerHTML;
};

function decodeHTML(str) {
    var element = document.createElement("a"); 
    element.innerHTML = str;
    return element.textContent;
};
var str = "<"
var enc = encodeHTML(str);
var dec = decodeHTML(enc);
console.log("str: " + str, "\nenc: " + enc, "\ndec: " + dec);
0
Israel