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

セキュアな Lightning コンポーネントの作成

学習の目的

この単元を完了すると、次のことができるようになります。

  • コンポーネント構造の HTML/マークアップタグと式言語について説明する。
  • 式言語のセキュリティの障害点を識別する。

コンポーネントの表示

メモ

メモ

前の単元の最後に、Kingdom Management 開発者組織を取得する方法を説明しました。この単元の Challenge を実行するにはこの組織が必要であるため、まだ取得していない場合は、戻ってサインアップしてください。

Lightning コードからスタイル設定した対話型 UI まで進むには多くの作業が必要です! ただし、Lightning フレームワークが開発者に代わってその多くを行います。Lightning は、アプリケーションが必要とするデータをその属性とサーバコールを使用して追跡します。コンポーネントのスコープにデータを入力し、式を解決してクリーンで使用に適したユーザインターフェースを生成します。

Lightning で扱うデータは外部から取り込まれるため、コンポーネントのマークアップのセキュリティを確保するための対策を講じる必要があります。そこで、これらの対策を確認しましょう。

HTML/Lightning マークアップ

Lightning が特に優れている点の 1 つは、一般的な Web テクノロジを使用できることです。Aura コンポーネントは、拡張 HTML、CSS、JavaScript を使用して作成されているため、これまでの知識を活かして新しいアプリケーションをすばやく開発し、Salesforce とセキュアに統合することができます。

HTML、CSS、JavaScript を Lightning プラットフォームと統合するには、Lightning マークアップでコンポーネントを定義します。これにはいくつかのステップが含まれます。 

  1. HTML を適切なコンテナ (<aura:application> または <aura:component>) でラップします。
  2. <script> または <style><ltng:require> に置き換えて、Lightning Experience がリソースを管理できるようにします。
  3. <aura:outputText> および <aura:outputRichText> タグで動的データを表示します。

次の例は、おなじみの Hello World プログラムの Lightning マークアップです。

  <aura:component implements="flexipage:availableForAllPageTypes" access="global"> <ltng:require scripts="{!$Resource.myJavaScriptLib}"/> 
    <div class="main-content"> 
      <aura:outputText value="Hello World!">
    </div> 
  </aura:component>

危険なタグ

Lightning プラットフォームは柔軟で強力です。ただし、強力なほど、責任も大きくなります。Lightning の機能によっては、不適切に使用すると問題になりかねません。 

特に強力なタグ <aura:unescapedHTML> を見てみましょう。このタグの値は、未加工でエスケープ解除された HTML として解釈されます。これによりページが大幅に変更されることがあります。<aura:unescapedHTML> タグは、信頼できるソースからの書式設定済みの HTML を出力するために提供されており、他に方法がない場合にのみ使用します。

どこで未加工の HTML を使用しますか? おそらく、動的データのために柔軟なスタイルを提供していて、HTML でスタイルを指定する場合でしょう。コントローラにデータ用のカスタムスタイル設定 HTML を作成し、スタイル設定文字列にデータを事前入力し、Aura で HTML として表示します。Lightning が表示されるデータを HTML ノード間で自動的にエンコードします(たとえば、<span>{!"Here"}</span> など)。Lightning がデータをエンコードしないようにするには、データを <aura:unescapedHTML> タグ内に配置します。それによって、コードとして実行できます。

かなり強力でしょう? このタグを使用すると、Lightning の動作が変わります。この場合、保護のレイヤを無効にします。そこで、開発者がその保護を提供する必要があります。いかなる形式の保護でも、無効にするときは慎重に行い、無効にする必要があるかどうか検討してください。Lightning でデータが保護されないのであれば、開発者が他の方法を使用してデータとコードを保護する必要があります。 

強力でなければいけないのか?

必要な機能を得るために保護を犠牲にしなくてもよい場合もあります。たとえば、<ui:outputRichText> を使用すると、未加工の HTML を処理せずにスタイル設定されたテキストを出力できます。このタグに関する詳細などは、『Lightning コンポーネント開発者ガイド』を参照してください。

実際のエスケープ解除された HTML

あきれているのでしょう? わかっています。未加工の HTML をコードとして実行するのは凄い方法です。凄いことを行う方法を説明したそばから、それをやってはいけないと言ったのですからあきれるのも当然です。私たちも何をすべきか指図されるのは好きではありません。説明するより実例を見たほうがわかりやすいので、エスケープ解除された HTML をコンポーネントに使用すると何が起こるかをこれからお見せしましょう。Kingdom Management 開発者組織にようこそ!

メモ

メモ

このモジュールは Lightning Experience 向けです。Kingdom Management 組織にログインしたら、Lightning Experience に切り替えてから次の手順や Challenge を実行してください。

  1. ...Kingdom Management 開発者組織のナビゲーションバーの左側で [アプリケーションランチャー] をクリックし、Secure Lightning アプリケーションを選択します。
    アプリケーションランチャーのスクリーンショット
  2. [Unescaped HTML Demo (エスケープ解除された HTML のデモ)] タブに移動します。
    • 架空上の動物 (Creature__c) がリストされたページが表示されます。「wyvern」を検索します。このアプリケーションは、wyvern に関する情報を表示し、その名前を <b> タグで強調表示します。
  3. 次は「Balrog」を検索します。
    • 何か変なことが起きています。Balrog の「ときの声」がページの表示方法を変えています。Balrog のコードは <aura:unescapedHTML> タグを使用しています。このタグは、渡されたすべての HTML をチェックせずに表示します。

Balrog は魔法のレコードではありません。唯一の特別な点は、その記述に文字列 marquee が含まれていることです。この文字列が HTML として表示されると、要素がページ内を回ります。幸い、LockerService が未加工の HTML によって事態が悪化するのを防止しています。攻撃者ができる最大のことは、データとその表示方法を変更することです。LockerService は、クロスサイトスクリプティングと類似の攻撃を防止します。

悪意のある Balrog に対してページを保護するにはどうすればよいのでしょうか? <ui:outputRichText> を使用してデータを表示するようにページを変更します。このタグは、ほとんどのアプリケーションに表示される標準のスタイル設定をサポートしていますが、悪意のあるスクリプトやタグがページに影響を与えるのを防止します。

<ui:outputRichText value="{!v.creatures}"/>

Lightning 式

Lightning 式を使用すると、コンポーネントのマークアップ内で計算を実行したり、オブジェクトデータにアクセスしたりできます。式は、コンポーネント間で値を渡す場合や、動的データを出力する場合に使用します。

式はリテラル値、変数、サブ式、演算子などで構成され、1 つの値に評価されます。メソッドの参照は許可されますが、それらのメソッドをコールすることは許可されません。有効な Lightning 式の例として、次のようなものがあります。

  • {!"Hello world"}
  • {!v.myAttribute}
  • {!c.myControllerFunction}
  • {#v.myUnboundAttribute}

コンポーネントが表示されるとき、またはコンポーネントが値を使用するときに、{! } または {# } 区切り文字内にあるすべてが評価され、置換されます。非バインド属性は {# } で指定され、基礎となるオブジェクトまたは属性が変更されても更新されません。

次の例では、division 要素のタイトルに component 属性を使用しています。

  <aura:component> 
    <aura:attribute name="divTitleValue" type="string" />
      <div title="{!v.divTitleValue}"> 
        Div Example 
      </div> 
    </aura:component>

式をセキュアにする

ほとんどの場合、Lightning 式言語は非常にセキュアです。まず、Lightning は式でアクセスされるデータを自動的にエスケープします。

Lightning 式は、ノード値または属性値にしかコンパイルできないという作成方法によってセキュリティが内在しています。他の場合はコンパイルされません。式の作成方法を制限することで、Lightning は式がセキュリティ脆弱性の原因にならないようにし、ユーザを問題から保護します。 

たとえば、次のコードスニペットは 2 つの行が原因でコンパイルに失敗します。NOT OK とマークされた行の 1 つ目では、式を使用して属性値の一部を作成しようとしています。式は属性全体を定義する必要があり、これは禁止されている操作です。2 つ目の「NOT OK」行では、式を HTML 属性名に割り当てようとしています。式はテキスト値または属性値にしかできないため、これは禁止されている操作です。

<aura:component> 
  <aura:attribute name='var1' type='String'/>
  <div title="{!'title:' + v.var1}">test</div> <!--OK-->
  <div title="title:{!v.var1}">test<div> <!--NOT OK-->  
  <div title="{!v.var1}">test</div> <!--OK-->
  <div {!v.var1}="hello">test</div> <!--NOT OK-->
  <div>test:{!v.var1}</div> <!--OK-->  
</aura:component>

ただし、Lightning 式は完全にセキュアというわけではありません。Lightning 式を一部の HTML タグと組み合わせると保護ができなくなります。href または src タグが式を参照すると、ユーザを不正な場所にリダイレクトすることができます。次のコンポーネントで何が起きるか、コードコメント (potentially unsafe: could go to any page (潜在的に安全ではない: 任意のページに移動可能)) を読んで確認しましょう。

<aura:component> 
  <aura:attribute name="val1" type="String">
  <a href="{!v.val1}">click</a>  //potentially unsafe: could go to any page 
  <iframe src="{!v.val1}"/> //potentially unsafe: could go to any page 
</aura:component>
メモ

メモ

Lightning 属性には「String」や「Account」のような型がありますが、属性の値が実際にその型と一致するかどうかは保証されません。JavaScript の動的型付けにより、属性にはどのデータ型も存在できます。油断は禁物です! アクセスしようとしているプロパティがオブジェクト (または Lightning 属性) にあること、またデータが期待しているものであることを常に確認します。

「href」属性を安全ではない方法で使用するとどうなるか確認してみましょう。 

  1. Kingdom Management 開発者組織のナビゲーションバーの左側で [アプリケーションランチャー] をクリックし、Secure Lightning アプリケーションを選択します。
    アプリケーションランチャーのスクリーンショット
  2. [Unsafe Href Demo (安全ではない href のデモ)] タブに移動します。
  3. 国の各地域 (Region__c) がリストされたページが表示されます。「underworld」(地下世界) を検索します。
    • underworld にようこそ。このページでは、そこに棲息する最もありふれたクリーチャーへのリンクが表示されます。この場合、underworld には安全ではないコンテンツはなく、どのリンクをクリックしても安全なはずです。「安全ではない」リンクはエラーになるかもしれませんが、あなたは Salesforce 内に留まっています。
  4. 次は別の地域「The rolling hills」を検索します。

rolling hills には、別のクリーチャーの集団が棲息しています。その名のとおり、最もありふれたクリーチャーである Common Monster のリンクは安全ではありません。このリンクをクリックすると、ミュージックビデオに移動します。オブジェクト項目構造やヘルプテキストは、Salesforce を離れるとは一言も言いません。Force.com から離れることについても一切警告はありません。ただ別の場所に転送されるのです。

ミュージックビデオについてどう感じるかに関係なく、この脆弱性は修正しなければなりません。さっそく取りかかりましょう。 

  1. 「Region__c.Common_Monster__c」からのデータが実際には ID であることを確認します。ID には英数字文字のみが含まれます。データに他の文字が含まれている場合、それは無効な ID であり、拒否されます。
  2. ここで、Lightning 式を使用して有効な ID をリンクにします。

安全ではない例:

<a href="{!v.region.Common_Monster__c}">Common monster</a>

式の v.region.Common_Monster__c の前にスラッシュ「/」を追加してみましょう。

安全な例:

<a href="{!'/'+v.region.Common_Monster__c}">Common monster</a>

「The blind forest」地域を検索します。「/」で始まる式は依然としてユーザを Salesforce 外にリダイレクトできます。この場合、最初の (安全ではない) [Common monster] リンクがユーザを再びミュージックビデオに送り込みます。2 つ目のリンクは安全であることがわかります。リンクを作成する前に v.safeCommonMonster で ID をサニタイズしたからです。v.safeCommonMonster を使用することで、すべてのリンクを安全にすることができます。

安全ではない例:

<a href="{!'/'+v.region.Common_Monster__c}">Common monster</a>

安全な例:

//JavaScript: we add a check for only alphanumeric and numeric values 
var alphaNum = /^[0-9a-zA-Z]+$/; 
if(results[0].Common_Monster__c.match(alphaNum)) { 
component.set('v.safeCommonMonster',results[0].Common_Monster__c); 
} 
Markup: 
<a href="{!'/' + v.safeCommonMonster}">Common monster</a>

これで、ID をサニタイズして場所を適切に制御するとリンクが安全になる理由を理解できました。

式のセキュリティは完全ではない

明らかに、Lightning 式を使用するとコンポーネントのセキュリティを強化できます。ただし、それは魔法のセキュリティソリューションではありません。href の出力が「ID」準拠であること、つまり英数字文字のみが含まれていることを確認してください。

Lightning コンポーネントのセキュアなコードがどのようなものか理解できました。セキュアな JavaScript コードについてはどうでしょうか? それが次のトピックです。 

リソース

Lightning コンポーネントフレームワークとは?

Introducing LockerService for Lightning Components (Lightning コンポーネント用の LockerService のご紹介)

Lightning コンポーネント開発者ガイド : Writing Secure Code with LockerService (LockerService を使用したセキュアなコードの記述)

Lightning コンポーネント開発者ガイド: ui:outputRichText

メモ

メモ

このモジュールは Lightning Experience 向けです。ハンズオン組織を起動するときには、Lightning Experience に切り替えてこの Challenge を実行してください。