進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

Lightning Platform アプリケーションでの XSS の防止

学習の目的

この単元を完了すると、次のことができるようになります。
  • プラットフォームで提供されている 3 つの Visualforce エンコーディング関数を挙げる。
  • コードで差し込み項目が表示されるコンテキストを特定する。
  • 場所によって、コンテキストに応じた Visualforce エンコーディング方法をコードに適用する。

プラットフォームで提供されるエンコーディング関数

プラットフォームで提供される自動 HTML エンコーディングは、多くの使用事例向けのデフォルトの優れた保護手法ですが、完璧なソリューションではありません。前の単元で見てきたように、HTML エンコーディングは、ユーザ制御入力が HTML に挿入される事例のみを保護します。

それでは、次のような事例にはどのように対処するのでしょうか?

  • スクリプトコンテキストのコード (<script> タグ間のコードなど)
  • スタイルコンテキストのコード (<style> タグ間のコードなど)
  • HTML コンテキスト内のスクリプトコンテキストのコード (HTML タグ内の JavaScript アクションハンドラなど)

また、エンコーディングが JavaScript Remoting で escape=false または escape:false によって無効化されている場合はどうでしょうか?

幸いなことに、Salesforce は Visualforce と Apex の両方でエンコーディング関数を提供しています。この単元では、サンプルコードと共にこれらを紹介し、XSS 脆弱性を緩和する方法を学びます。

Visualforce でのプラットフォームエンコーディング

Visualforce のプラットフォームには、開発者が XSS の脅威を無力化するために使用できる、HTMLENCODE、JSENCODE、JSINHTMLENCODE という 3 つの主要なエンコーディング関数があります。使用するエンコーディングの選択時には、ブラウザがどのように出力を解析しているかを考慮します。

  • 値が JavaScript パーサーによって解析される場合は、JSENCODE() を使用します。
  • 値が HTML パーサーによって解析される場合は、HTMLENCODE() を使用します。
  • 両者を組み合わせる場合は、
    • JSENCODE(HTMLENCODE())
    • または JSINHTMLENCODE() を使用します。

各エンコーディング関数を適用する場所と時期を説明するために、例を示します。

JSENCODE()

JSENCODE() は、javascript コンテキストでリフレクションを使用する前に、入力の JavaScript エンコーディングを実行するために使用できる関数です。この関数は、バックスラッシュ (\) などのエスケープ文字をアポストロフィー (') などの安全でない JavaScript 文字の前に挿入して、JavaScript で使用するテキスト文字列や差し込み項目値をエンコードします。この関数は、差し込み項目が直接 JavaScript 変数として使用される場合に使用されます。

XSS に対して脆弱な Visualforce を以下に示します。

<script>
    var x = '{!$CurrentPage.parameters.userInput}';
</script>

ユーザ入力の値を提供する攻撃者を考えてみます。

userInput'; alert(1); //

これにより、アプリケーションは文字列を抜け出して、攻撃者のコードはこのスクリプトを実行できるようになります。

<script>
    var x = 'userInput’; alert(1); //’;
</script>

安全でない文字列が適切にエンコーディングされ、JavaScript パーサーによって実行されないようにするために、JSENCODE 関数で差し込み項目をラップする必要があります。

<script>
    var x = '{!JSENCODE($CurrentPage.parameters.userInput)}';
</script>

JSENCODE() の実践

では、JSENCODE を使用して JavaScript ベースの XSS の脆弱性を修正する方法を学習しましょう。
  1. Kingdom Management 開発者組織にログインし、[クロスサイトスクリプト (XSS)] アプリケーションを選択します。
  2. [XSS Visualforce 軽減デモ] タブをクリックします。
    画像ベースの XSS がページに挿入され、ページが改ざんされることがわかります。
  3. ページ下部の Visualforce および Apex リンクをクリックして、脆弱なコードを探します。

    Apex:

    public pageReference JSXSS(){
        title = 'THEME VIOLATION!!!!\';var newHTML = document.createElement(\'div\');newHTML.innerHTML = \'<img src="https://developer.salesforce.com/resource/images/astro.png" />\';document.body.appendChild (newHTML);var x =\'x';
        return null;
    }
    

    Visualforce:

    <script>
        var vip = '{!title}';
        [...]
    </script>
                           
    [...]
    
    <apex:commandButton value="Click here to view the JavaScript-based XSS!" action="{!JSXSS}"/>
    

    行 36 でボタンをクリックすると、Apex で JSXSS 関数が呼び出されることがわかります。この関数は XSS ペイロードにタイトルを設定し、タイトルは Visualforce で行 14 で表示されます。

    行 14 の出力の周囲にエンコーディングを実装してこの攻撃を防ぐ必要があります!

  4. 次を
    var vip = '{!title}';

    以下に変更して、Visualforce ページを編集します

    var vip = ‘{!JSENCODE(title)}’;
  5. [保存] をクリックして [XSS Visualforce 軽減デモ] タブに移動します。
  6. [JavaScript ベース XSS] ボタンを再度クリックします。

画面に画像が表示されなければ、XSS は正常に緩和されました!

HTMLENCODE()

HTMLENCODE() は、HTML コンテキストでリフレクションを使用する前に、さらに入力の HTML エンコーディングを実行するために使用できる関数です。ほとんどの使用事例では、この関数を使用する必要はありませんが、デフォルトのプラットフォームエンコーディングがオフになっている場合やユーザ制御可能入力を直接 DOM に追加する場合には必要になります。

次の例を考えてみます。

<apex:outputText escape="false" value="<i>Hello {!Account.Name}</i>" />

開発者が属性 [escape][false] に設定して組み込みのプラットフォーム HTML エンコーディングを無効にしたため、この Visualforce コードは XSS に対して脆弱です。このコードは HTML コンテキストであるため、開発者は HTMLENCODE 関数で差し込み項目をラップして、値がコードではなくテキストとして表示されるようにする必要があります。

<apex:outputText escape="false" value="<i>Hello {!HTMLENCODE(Account.Name)}</i>" />

完璧です。もっと複雑な例を見てみましょう。

<div id="test"></div>
<script>
    document.querySelector('#test').innerHTML='Howdy ' + '{!Account.Name}';
</script>

ここでは、開発者は DOM を直接変更して、HTML コンテキストで差し込み項目を挿入します。ブラウザでこのコードがどのように解析されるかを考えてみましょう。まず、ページが読み込まれるときに、差し込み項目 (Account.Name) は HTML パーサーを通過します。ただし、差し込み項目はスクリプトタグ内にあるため、HTML パーサーは文字参照の置換を実行せず、代わりにスクリプトブロックのコンテンツを JavaScript パーサーに渡します。その後、JavaScript コードは HTML 解析 (および文字参照の置換) を実行する innerHTML を呼び出します。

このため、JavaScript の後に HTML という順序で解析されます。

つまり、XSS の脅威を完全に無力化するには、この差し込み項目で実際に [2 ラウンドのエンコーディング] を実行する必要があります。まず、JavaScript エンコードし、次に HTML エンコードする必要があります。

<div id="test"></div>
<script>
    document.querySelector('#test').innerHTML='Howdy ' + '{!JSENCODE(HTMLENCODE(Account.Name))}';
</script>

HTMLENCODE() の実践

Kingdom Management アプリケーションでプロファイルページの開発を開始しました。最終的な目標は、「フレンドファインダ」機能を含めることです。ユーザはプロファイルの値を制御できるため、XSS から確実に保護する必要があります。実践してみましょう。
  1. Kingdom Management 開発者組織にログインし、[クロスサイトスクリプト (XSS)] アプリケーションを選択します。
  2. [XSS Visualforce 軽減デモ] タブをクリックします。

    Visualforce ページに、ユーザプロファイルのさまざまな部分およびクロスサイトスクリプトをテストするためのボタンが表示されています。これらにより、防御をテストする時間が節約できます!

  3. [こちらをクリック] という名前が付けられた最初のボタンをクリックすると、[HTML ベース XSS] が表示されます。

    埋め込み画像形式の XSS がページに表示されます。このコードは、URL のユーザパラメータを介して挿入されます。

    https://c.[yourinstance].visual.force.com/apex/xss_visualforce_mitigations_demo?user=THEME+VIOLATION%21%21%21%21+%3Cimg+src%3D%22https%3A%2F%2Fdeveloper.salesforce.com%2Fresource%2Fimages%2Fastro.png%22%2F%3E

  4. ページ下部のリンクを使用して Visualforce コードを表示すると、次が表示されます。
    <apex:outputText value="Welcome, <b>{!$CurrentPage.Parameters.user}</b>!" escape="false"/>
    

    escape=”false” 設定により、ユーザ制御コンテンツがページ上に直接表示されます。

  5. コードを編集し、防御用のエンコーディングを次のように追加します。
    <apex:outputText value="Welcome, <b>{!HTMLENCODE($CurrentPage.Parameters.user)}</b>!" escape="false"/>
    
  6. [保存] をクリックして [XSS Visualforce 軽減デモ] タブに移動します。
  7. [HTML ベース XSS] ボタンを再度クリックします。

名前は奇妙に見えますが、テーマ違反はありません。XSS を防止しました!

JSINHTMLENCODE()

JSINHTMLENCODE は、プラットフォームが常に差し込み項目を自動的に HTML エンコードするわけではなかったときに実装された旧来の Visualforce 関数です。JSINHTMLENCODE は事実上 HTMLENCODE(JSENCODE()) の組み合わせであるため、自動 HTML エンコーディングを導入する前に、HTML 内の JavaScript イベントハンドラ (onerror、onload) に差し込み項目を挿入するときに、開発者はこの関数を呼び出していました。現在では、プラットフォームが自動 HTML エンコードするため、JSENCODE() を呼び出すだけで十分です。

次の例を考えてみます。

<div onclick="console.log('{!JSINHTMLENCODE(Account.Name)}')">Click me!</div>

ここでは、プラットフォームで JSINHTMLENCODE を使用して、アクションハンドラ (onclick) に含められた値の HTML エンコーディングが自動的に実行されるため、開発者は間違って 2 ラウンドの HTML エンコーディングを実行しました。これは厳密には脆弱ではありませんが、アプリケーションを壊す可能性があります。より適切なソリューションは以下のとおりです。

<div onclick="console.log('{!JSENCODE(Account.Name)}')">Click me!</div>

JSENCODE 関数は、エスケープ文字を安全でない JavaScript 文字の前に挿入して、HTML で予約されている文字を HTML エンティティ文字に置き換えて、HTML タグ内の JavaScript で使用するテキスト文字列や差し込み項目値をエンコードします。これは、JavaScript イベントハンドラの差し込み項目などの場合に使用されます。差し込み項目は HTML および JavaScript コンテキストの両方で反映されるためです。

JSINHTMLENCODE の実践

JSINHTMLENCODE 関数を試してみましょう。
  1. Kingdom Management 開発者組織で [XSS Visualforce 軽減デモ] タブをクリックします。
  2. [JavaScript + HTML ベース XSS] ボタンをクリックして脆弱性をデモします。

    ページを改ざんする画像が表示されます。

  3. 脆弱なコードを探すには、ページ下部にある [Visualforce] および [Apex] リンクをクリックします。

    Visualforce:

    <script>
                            
        [...]
        
        var html = '<br/><br/><b>---------------------</b>';
        html += '<br/>Personnel Name: {!JSENCODE(name)}';
        html += '<br/>Favorite color: {!JSENCODE(color)}';
        html += '<br/>Favorite animal: {!JSENCODE(animal)}';
        html += '<br/><b>---------------------</b>';
        document.getElementById('{!$Component.output2}').innerHTML = html;                        
    </script>
    
    [...]
    
    <apex:commandButton value="Click here to view the JavaScript + HTML-based XSS!" action="{!JSINHTMLXSS}"/>
    

    Apex:

    public pageReference JSINHTMLXSS(){
        color = 'THEME VIOLATION!!!! <img src="https://developer.salesforce.com/resource/images/astro.png"/>';
        return null;
    }
    

    commandButton をクリックしたときに、アプリケーションは Apex で JSINHTMLXSS 関数を呼び出しました。この関数は XSS ペイロードを color 変数に設定し、これはその後 Visualforce で処理されます。

    ただし、今回はユーザに表示する前に Visualforce ページ上で color 変数を JSENCODE でラップします。では、なぜ脆弱なままなのでしょうか?

    JSENCODE は JavaScript コンテキストに固有のエンコーダであるため、“blue’;alert(‘hi’);//” などの JavaScript ベースの XSS を防ぎます。しかし、このアプリケーションでは、color 変数は JavaScript コンテキストで使用されるだけでなく、innerHTML 呼び出しを介して直接 DOM にも書き込まれ、データが HTML として表示されます。このため、このアプリケーションを XSS から完全に保護するには、JavaScript と HTML 両方の形式のエンコーディングが必要になります。幸いなことに、プラットフォームではコンボエンコーディング方法、JSINHTMLENCODE() が用意されています。

  4. JSENCODE() ではなく JSINHTMLENCODE() を使用するように Visualforce を変更します。
  5. [保存] をクリックして [XSS Visualforce 軽減デモ] タブに戻ります。
  6. [JavaScript + HTML ベース XSS] ボタンを再度クリックすると、今回は埋め込み画像は表示されません。

重要な教訓は、コンテキストごとにエンコーディング形式があり、正しくエンコードするにはコンテキストを認識する必要があるということです。間違ったエンコーディング方法を使用すると危険です!

Apex でのプラットフォームエンコーディング

ここまで、Visualforce ページを変更して XSS を防止することに焦点を当てて説明してきました。しかし、Apex でエンコードする必要がある場合はどうすればよいでしょうか?

正直なところ、コントローラ内でのエンコーディングはお勧めしません。常に、表示するコンテキストのできるだけ近くで (たとえば Visualforce で) エンコードする必要があります。コントローラ内でエンコーディングが行われるたびに、ビューとコントローラの間で連動関係が作成されますが、コントローラはデータの表示方法に非依存である必要があります。さらに、このパターンは堅牢ではありません。Visualforce ページで同じ変数を複数の異なるコンテキストで表示したい場合もありますが、コントローラは 1 つのコンテキストでのみエンコード可能であるためです。

それでも、コントローラ内でエンコードしなければならないことがあります。たとえば、コントローラ内から動的 HTML を生成する場合です。それには、すべてのユーザ制御値をエンコードして、特殊文字がテキストではなくコードとして解釈されるのを防ぐ必要があります。Salesforce では、セキュリティエンコーディングを実行するためにパッケージ内で使用できるグローバル静的メソッドをエクスポートする Lightning Platform ESAPI を介して、さまざまな Apex エンコーディング関数を提供しています。このパッケージは、未管理パッケージとして任意の Salesforce 組織にインストールできます。

Lightning Platform ESAPI の実践

Kingdom Management 開発者組織に Lightning Platform ESAPI パッケージをインストールしました。次に、Visualforce エンコーディングの代わりにこれを使用して XSS を防止する方法を見ていきましょう!
  1. Kingdom Management 開発者組織にログインし、[クロスサイトスクリプト (XSS)] アプリケーションを選択します。
  2. [XSS Apex 軽減デモ] タブをクリックします。

    Visualforce ページには、プロファイル機能と XSS ボタンが表示されています。Visualforce コードを調べると、escape=”false” で表示されたすべての差し込み項目が示されます。

    Title: <apex:outputText value="{!title}" escape="false" /><br/>
    Name: <apex:outputText value="{!name}" escape="false" /><br/>
    Favorite Color: <apex:outputText value="{!color}" escape="false" /><br/>
    Favorite Animal: <apex:outputText value="{!animal}" escape="false" /><br/>
    

    前に説明しましたが、escape 属性を false に設定すると、プラットフォームで提供される組み込みの HTML エンコーディングが無効になることを覚えているでしょうか。

  3. ページ下部にあるリンクをクリックして Apex コントローラを表示し、開発者がなぜこれを実行したかを確認してください。

    すべてが <b> タグでラップされています! 設計された効果を維持するには、特にユーザ制御値に焦点を当てて、Apex でエンコーディングを実行する必要があります。

  4. コントローラを編集し、この例のように、HTML コンテンツ内のページに送信されるすべてのアイテムを ESAPI.encoder().SFDC_HTMLENCODE() メソッドでラップします。
    title = '<b>' + ESAPI.encoder().SFDC_HTMLENCODE(person.Title__c) +'</b>';
    
  5. [保存] をクリックして [XSS Apex 軽減デモ] タブに戻ります。
  6. [HTML ベース XSS] ボタンを再度クリックして表示します。

エンコーディング関数が攻撃ペイロードは無力化したが、残りのデータは必要に応じて太字のままになっていることがわかります。セキュリティが強化されても、すべての機能は維持されています!