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


745

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メソッドはありますか?


使用しているJavascriptを表示できますか?
Sinan Taifour 2009

1
隠しフィールドから値を取得する方法を追加しました
AJM

5
一部の(私はChromeのみをテストしました)ブラウザーのようにinnerHTMLメソッドを使用しないでください(jQuery .html()メソッドはinnerHTMLを使用します)。これは引用符をエスケープしません。そのため、値を属性値に入れると、XSSの脆弱性が発生します。
James Roper

21
どのようなコンテキストでchalkcheese一緒に使用されるか0_o
d -_- b

2
@d -_- b 2つの項目を比較する場合。例。チョークやチーズと同じくらい違います;)
Anurag '18 / 06/18

回答:


1067

編集:この回答はずっと前に投稿されたものであり、このhtmlDecode機能はXSSの脆弱性をもたらしました。一時要素をaからa divに変更して変更されましたtextarea XSSの可能性を減らすために。しかし、今日では、他の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();
}

基本的に、textarea要素はメモリ内に作成されますが、ドキュメントに追加されることはありません。

上のhtmlEncode機能私はセットinnerTextの要素の、符号化された検索しますinnerHTMLhtmlDecode設定した関数でinnerHTMLの要素の値をしてinnerText取得されます。

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


95
これはほとんどのシナリオで機能しますが、htmlDecodeのこの実装は余分な空白を排除します。したがって、「input」の一部の値については、input!= htmlDecode(htmlEncode(input))と入力します。これは、一部のシナリオでは問題でした。たとえば、input = "<p> \ t Hi \ n There </ p>"の場合、往復エンコード/デコードにより "<p> Hi There </ p>"が生成されます。ほとんどの場合、これは問題ありませんが、そうでない場合もあります。:)
ペティ

7
解決策をありがとう!テキスト値の改行を%% NL %%などに置き換え、HTMLエンコードされた値を取得するために.html()を呼び出し、%% NL %%を<br /> 'に置き換えることで、余分な空白の問題を解決しましたs ...防弾機能はありませんが機能し、ユーザーは%% NL %%を入力する可能性が低かったです。
benno

1
おもしろいのは、CSSにwhite-spaceプロパティがあり、HTMLコンテンツ内のスペースをどのように処理するかを示すプロパティがあることです。プロパティの存在は、「これは事前にフォーマットされているため、スペースと改行を保持する必要がある」ことを意味します。これは、スタイルとコンテンツの分離を壊します。これは、HTMLを「きれい」に再フォーマットしようとした場合、またはこのようにエンコード/デコードサイクルを介して往復した場合、スペース/ブレークの実行が削減され、エンコーダーにないためです。white-space:pre-*;外部CSSファイル内のインジケーターを認識していないため、実行しても問題がないかどうかを知る方法!
Triynko 2011

2
このソリューションは、ページがhtmlまたはxhtmlのどちらで記述されているかに依存する可能性があるため、DOMを使用しないソリューションを推奨します。
Phil H

30
2年後に回答されましたが、以下の@Anentropicからの応答はあらゆる点で優れています。
チャド2007

559

jQueryトリックは引用符をエンコードせず、IEでは空白を削除します。

Django のエスケープテンプレートタグに基づいていますが、これは既に頻繁に使用/テストされているため、必要なことを行うこの関数を作成しました。

空白の除去の問題の回避策のどれよりも間違いなく単純です(そしておそらくより高速です)。引用符をエンコードします。これは、たとえば属性値内で結果を使用する場合に不可欠です。

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年6月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;');

3
&apos;代わりに使用することもできます&#39;
Ferruccio


5
おかげで、これ&apos;が有効なHTMLエンティティではないことに気づきませんでした。
Ferruccio 2012年

10
がない場合/g.replace()最初の一致のみが置き換えられます。
ThinkingStiff

1
@ Tracker1同意しません。関数が無効な入力を受け取った場合、エラーがスローされます。場合は、特定のユースケースでは、あなたはどちらか、その方法で無効な入力を扱う関数を呼び出す前に値を確認するかのtry / catchでの関数呼び出しをラップします。
エントロピー2016

80

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

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

速度: http : //jsperf.com/htmlencoderegex/17

スピードテスト

デモ: jsFiddle

出力:

出力

脚本:

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>

17
これは疑問を投げかけます:なぜそれはJSのグローバル関数ではないのですか?
SEoF 2013年

2
.replace()@SEoFによって最近提案された非正規表現バージョンは非常に高速であることが判明しました:jsperf.com/htmlencoderegex/22
Anentropic

@Anentropic確かに高速ですが、うまく機能していないと思います。なし/g.replace()は、最初の一致のみを実行します。
ThinkingStiff 2013年

興味深いことに、Firefoxで実行できることはreplace('a', 'b', 'g')replace(/a/g, 'b')速度も同じですが、速度も同じです
エントロピー

1
私も:)私は引用符を処理したいだけで始めて、スピードを追求することになりました...
Anentropic

32

私はこれが古いものであることを知っていますが、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();
} 


12

いい答えです。エンコードする値がjQuery 1.4.2 undefinedまたはnulljQuery 1.4.2の場合、次のようなエラーが発生する可能性があることに注意してください。

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

または

Uncaught TypeError: Object has no method 'html'

解決策は、実際の値を確認するように関数を変更することです。

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

8
jQuery('<div/>').text(value || '').html()
roufamatic

3
@roufamatic-素敵なワンライナー。ただしvalueif保存中に空でないことを確認するには、その場でDIVを作成し、その値を取得する必要があります。これはhtmlEncode、たくさん呼び出されている場合や、value空になる可能性が高い場合、パフォーマンスが大幅に向上します。
leepowers

こんにちは、βから&betaを行わない理由を知っていますか?
Dilip Rajkumar 2013

11

単純なJavaScriptを好む人のために、私がうまく使用した方法を以下に示します。

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

6

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

ここでは厳密にセマンティクスを操作している可能性がありますが、エンコードの目的を理解してほしいと思います。「失われた」という言葉は、何かが正常に機能していないように聞こえるようにします。


6

Jqueryなしで高速化。文字列のすべての文字をエンコードできます。

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

または、次のように、心配するメインキャラクター(&、inebreak、<、>、 "、 ')をターゲットにします。

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

プロトタイプには、Stringクラスが組み込まれています。したがって、Prototypeの使用を計画している場合は、次のようになります。

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

9
プロトタイプのソリューションを見た後、これですべてが完了します...とても.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); 簡単です。
Steve Wortham

5
引用符でも何かしませんか?それは良くない
エントロピー

@Anentropicなぜ引用符で何かする必要があるのか​​わかりません。引用符は、属性値の内部にある場合を除き、エスケープする必要がないためです。
アンディ

少し考えてから、コメントを元に戻します。HTMLを作成している場合は、属性値を含めてHTMLの各部分をエンコードする必要があるので、Anentropicに同意し、Prototypejs関数では十分ではないと思いますその場合。
アンディ

4

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

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メソッド」を作成しました。


3

アンギュラのサニタイズに基づいて...(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};

私はこの答えが本当に好きですが、実際には良いアプローチだと思いますが、ビットミス演算子if (value === null | value === undefined) return '';はタイプミスですか、それとも機能ですか?もしそうなら、なぜ一般的なものではなくそれを使うの||ですか?ありがとうございました!!
アレハンドロヴァレス2017年

1
@AlejandroVales私はそれがタイプミスだったと確信しています...修正されました。
Tracker1

1
まあとにかく| 0または1につながるため、実際には機能しました^^
Alejandro Vales

使用できません== nullか?undefinedがと同等になる唯一のものnullであるため、いずれにしても2つの三重等式は必要ありません
Hashbrown

それはまったく真実ではありません。nullそして0両方falsy、はい、あなただけ行う傾けるようです!valueが、全体のポイントは、==特定の物事を容易にするためです。0 == nullは偽です。undefined == null本当です。あなたはただ行うことができますvalue == null
Hashbrown

3

私の知る限り、JavaScriptには単純なHTMLエンコード/デコードメソッドはありません。

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

jQueryで、これはうまくいくとしましょう:

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

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


この回答は870以上の賛成票を持っているものとほぼ同じであり、この票の少し後に投稿されたことを考えると、反対票は少し面白いと思います。
Ken Egozi、2016

2

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

<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.writeまたはを使用するような奇妙なことをしていない限りeval、HTMLエンコーディングは事実上透過的です。

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

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

2

同様の問題がありencodeURIComponent、JavaScript の関数を使用して解決しました(ドキュメント

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

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

そして

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

あなたが得るでしょうchalk%20%26%20cheese。スペースも確保されています。

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

encodeURIComponent('name/surname')

そして私は得た name%2Fsurname


2

以下Server.HTMLEncodeは、純粋なJavaScriptで記述された、MicrosoftのASPからの機能をエミュレートするビットです。

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

jQueryを使用する場合。私はこれを見つけました:

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

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

私が信じているプロトタイプの問題は、JavaScriptの基本オブジェクトを拡張し、使用したjQueryと互換性がないことです。もちろん、jQueryではなくPrototypeを既に使用している場合は問題ありません。

編集:これもあり、これはjQuery用のPrototypeの文字列ユーティリティのポートです。

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
<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

指定された値をHtmlEncodes

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


0

escapeHTML()prototype.jsで何をしているのかを選ぶ

このスクリプトを追加すると、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

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

存在する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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.