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

Visualforce と Apex での CRUD および FLS 違反の特定

学習の目的

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

  • CRUD と FLS を定義する。
  • Visualforce と Apex での CRUD および FLS 違反を特定する。

作成、参照、更新、削除 (CRUD) と項目レベルセキュリティ (FLS) とは何か?

前の単元で学習したように、CRUD と FLS は、システム管理者がユーザがアクセスおよび変更できるオブジェクトとオブジェクトの項目を指定できる認証設定です。

次の例は、Kingdom Management 開発者組織ではどのようになるのかを示しています。

Kingdom Management 開発者組織のカスタムオブジェクトに対して許可された CRUD のスクリーンショット  

ご覧のとおり、システム管理者は組織のこのユーザに、異なるアクセス制限を適用しました。ユーザは、さまざまなオブジェクトを参照できますが、変更できるのは小さなサブセットのみです。 

システム管理者は、さまざまなオブジェクトの FLS も設定しました。Treasures__c オブジェクトの項目は、次のようになっています。 

Kingdom Management 開発者組織の Treasure オブジェクトに設定された FLS のスクリーンショット

このユーザは (CRUD 設定により) オブジェクト自体を参照できますが、表示できるのは一部の項目のみ (名前や説明など) で、それ以外の項目は表示できません。 

Force.com アプリケーションで CRUD と FLS はどのように適用されるのか?

Kingdom Management 開発者組織で、システム管理者がさまざまなオブジェクトと項目に CRUD と FLS を設定しました。カスタムアプリケーションがこれらの設定に従うようにするのは、開発者の仕事です。すでに学習したように、プラットフォームにはコードを実行する、システムコンテキストとユーザコンテキストという 2 つのモードがあります。これらのモードはカスタムアプリケーションではどのように動作するのでしょうか?

Visualforce での CRUD と FLS

前の単元で、標準コントローラを使用する Visualforce ページは、ユーザコンテキストで実行されることを学習しました。したがって、CRUD と FLS がデフォルトで適用されます。 

たとえば、ユーザに、取引先オブジェクト (CRUD) と取引先責任者オブジェクトのメール項目 (FLS) の両方に対する参照アクセス権がある場合、次のデータが表示されます。

  <apex:page standardController=”Contacts”>
    <apex:outputField>{!contact.Email}</apex:outputField>
  </apex:page>

ただし、これはかなり単純化した例です。開発者がプラットフォーム用に作成するほとんどのアプリケーションは、カスタムコントローラまたは標準コントローラの拡張に依存します。では、そのような場合には、CRUD と FLS の適用はどのように動作するのでしょうか? 

残念ながら答えは、「場合によります」です。ではその理由を調べてみましょう。

カスタム Visualforce での CRUD と FLS の適用について

Kingdom Management 組織ではどのようになるのか見てみましょう。

  1. Kingdom Management 開発者組織にログインします。
  2. アプリケーションピッカーから [CRUD/FLS & Sharing (CRUD/FLS & 共有)] を選択します。
  3. [CRUD & FLS Visualforce Demo (CRUD & FLS Visualforce デモ)] タブをクリックします。

    CRUD & FLS Visualforce Demo アプリケーションのスクリーンショット

    このアプリケーションでは、王国内で発見された財宝をリストするカスタム Visualforce ページが表示されます。このデータは秘密にしておく必要があるため、CRUD と FLS が適切に適用されていることを確認してみましょう。

    アプリケーションの左上にあるドロップダウンを使用して、異なる CRUD および FLS アクセスレベルを持つユーザをシミュレーションし、各ユーザのアプリケーションにどのようなデータが表示されるのかを観察できます。実際にやってみましょう。

  4. ドロップダウンメニューから、[User with Limited Access to Treasures (財宝への制限付きアクセス権を持つユーザ)] を選択します。

    このユーザの権限セットを次に示します。この権限セットは Treasures オブジェクトの項目の小さなサブセットのみへのアクセス権を付与します。このユーザには、財宝に関する情報の一部が表示されますが、発見場所や保管場所は表示されません。
    ユーザに許可された Treasures オブジェクトの CRUD および FLS を示すスクリーンショット

    ドロップダウンからユーザを選択すると、ページが更新され、次の画像に表示されているような画面が表示されます。
    CRUD & FLS Visualforce Demo アプリケーションのデータ漏洩を示すスクリーンショット

    [Direct Reference (直接参照)] テーブルを見ると、予想どおりにデータが制限されています。[Found (検出)] 列はすべて false、[Castle (城)] 列は空白になっています。ただし、[Dereferenced (逆参照)] テーブルを見ると、ユーザのアクセスが許可されていない項目があります。なぜでしょうか? 適切に制限されていた最初のテーブルのコードを見てみましょう。

  5. [Return to user selection... (ユーザ選択に戻る...)] ボタンをクリックして、システム管理ユーザに戻ります。
  6. 下部にある [Visualforce page (Visualforce ページ)] リンクをクリックすると、ページのソースが表示されます。

    次のコードが表示されます。
        <h3>Discovered Treasure - Direct Reference</h3>
        <apex:pageBlockTable value="{!treasures}" var="p">
          <apex:column headervalue="Name">
            <apex:OutputText value="{!p.Name}" />
          </apex:column>
          <apex:column headervalue="Description">
            <apex:OutputText value="{!p.Description__c}" />
          </apex:column>
          <apex:column headervalue="Found">
            <apex:OutputText value="{!p.Found__c}" />
          </apex:column>
          <apex:column headervalue="Castle">
            <apex:OutputText value="{!p.Castle__r.Name}" />
          </apex:column>
        </apex:pageBlockTable>
          
    このテーブルには標準のオブジェクト表記 {!p.Name}、{!p.Description__c}、{!p.Found__c}、{!p.Castle__r.Name} を使用するオブジェクト項目が表示されているので、プラットフォームでは、デフォルトですべての CRUD および FLS 制限が適用されます。予想したとおり、機密項目が表示されないのはこのためです。

    では 2 つ目のテーブルについてはどうでしょうか? このテーブルで何が起こっているのかを見てみましょう。
          <h3>Discovered Treasure - Dereferenced</h3>
          <apex:pageBlockTable value="{!dereferencedTreasures}" var="p">
            <apex:column headervalue="Name">
              <apex:OutputText value="{!p.Name}" />
            </apex:column>
            <apex:column headervalue="Description">
              <apex:OutputText value="{!p.Description}" />
            </apex:column>
            <apex:column headervalue="Found">
              <apex:OutputText value="{!p.Found}" />
            </apex:column>
            <apex:column headervalue="Castle">
              <apex:OutputText value="{!p.Castle}" />
            </apex:column>
          </apex:pageBlockTable>
        
    このテーブルでは、dereferencedTreasures というリストをループ処理し、結果を返します。Apex に移動すれば、このリストに含まれている内容を表示できます。

  7. アプリケーションに戻り、下部にある [Apex Controller (Apex コントローラ)] リンクをクリックします。
          public with sharing class CRUD_Demo {
            public List<TreasureWrapper> dereferencedTreasures {get;set;}
            [...]
            public with sharing class TreasureWrapper {
              public Treasures__c treasure {get; private set;}
              public TreasureWrapper(Treasures__c treasure) {
                this.treasure = treasure;
                }
              public String getName(){
                return treasure.Name; // CRUD & FLS Violation
              }
              public Boolean getFound(){
                return treasure.Found__c; // CRUD & FLS Violation
              }
              public String getDescription(){
                return treasure.Description__c; // CRUD & FLS Violation
              }
              public String getCastle(){
                return treasure.Castle__r.Name; // CRUD & FLS Violation
              }
            }
          }
              
    Treasures__c オブジェクトを囲む、TreasureWrapper というラッパークラスが使用されています。これらのカスタムオブジェクトのリストが Visualforce に返されます。このクラスでは、Treasures__c オブジェクトの各項目の文字列値または Boolean 値を返すカスタム getter メソッド (getFound など) がいくつか定義されています。

    Visualforce が次を表示するように要求された場合
    <apex:OutputText value="{!p.Found}" />

    Apex では、Treasure__c.Found__c 項目の文字列表現を返す getFound() 関数が TreasureWrapper 内でコールされます。次にこの文字列が Visualforce に送信され、表示されます。この場合、Visualforce は (オブジェクトを表示した最初のテーブルとは異なり) オブジェクト項目ではなく、文字列を表示するように要求されています。したがって、オブジェクトと項目に関連付けらているすべての CRUD および FLS 設定は、ここで失われます。プリミティブ型を表示する場合、Visualforce には追加のコンテキストがないため、ユーザに適用されたアクセス制限に関係なく、文字列が表示されます。困りました。

Visualforce でのデータの表示方法によっては、ユーザがアクセス権を持たない情報が表示される可能性があります。 

Apex での CRUD と FLS

Visualforce でのデータ表示に関する問題について確認したので、次はデータの変更と作成について説明します。サーバ側で実行されるコードに CRUD と FLS はどのように適用されるのか?

Apex クラス、Apex トリガ、Apex Web サービスはすべてシステムコンテキストで実行されます。つまり、ユーザのアクセス制限に関係なく、Apex にはデフォルトですべてのデータへのアクセス権があります。

  • あらゆるオブジェクトまたはオブジェクト項目を照会できます。
  • Insert()、Update()、Delete() はすべてのオブジェクトまたは項目に対してコールできます。

Apex での CRUD と FLS の適用について

組織ではどのようになるのかを見てみましょう。

  1. [CRUD & FLS Apex Demo (CRUD & FLS Apex デモ)] タブをクリックします。

    CRUD & FLS Apex Demo アプリケーションのスクリーンショット

    このアプリケーションでは、王国中から寄せられた物資要求のリストが表示されます。許可されたユーザのみが、これらの要求を表示および削除できるようにする必要があります。そうしなければ、奇襲部隊がこのアプリケーションを利用して城を包囲し、物資を城内に運び込めないようにしたり、さらにひどい事態を引き起したりすることが考えられます。

    では、試してみましょう。前のアプリケーションと同様に、ドロップダウンメニューを使用して、さまざまなユーザをシミュレーションできます。

  2. [User with Read ONLY Access (参照のみアクセス権を持つユーザ)] を選択します。

    このユーザに適用された権限セットは次のとおりです。ご覧のとおり、ユーザは物資要求を参照できますが、変更はできません。物資要求には、Requisition__c オブジェクトを使用します。
    このユーザに参照アクセス権のみが付与されていることを示すスクリーンショット


  3. アプリケーションに戻り、右側にある [Del (削除)] リンクをクリックし、システムからレコードを削除します。

    困りました。ユーザがレコードを削除してしまいました。ユーザには参照のみアクセス権しかないはずなのに、これはどうやって起きたのでしょうか? 調べてみましょう。

  4. [Return to User Selection (ユーザ選択に戻る)] ボタンをクリックして、システム管理ユーザに戻ります。
  5. ページ下部にある [Apex Controller (Apex コントローラ)] リンクをクリックします。

    Visualforce で [削除] リンクをクリックしたときにコールされる Apex 関数を見てみましょう。
          public PageReference deleteRequest(){
            Requisition__c delr = [select id from Requisition__c where id =: id limit 1];
            if(delr != null) {
              try{
                delete delr;
              }
              catch(DmlException ex){
                  ApexPages.addMessages(ex);
              }
            }
            return null;
          }
    この関数では、データベースから削除する対象として選択されたレコードの ID が使用されています。Apex がシステムコンテキストで実行されることを覚えていますか? つまり、実行ユーザのアクセスレベルに関係なく、Apex はすべてのレコードを変更できます。ユーザには Requistion__c オブジェクトに対する参照アクセス権しかありませんが、deleteRequest 関数がコールされると、Apex はユーザの CRUD/FLS を検証せずに、オブジェクトインスタンスを削除します。

これらの簡単なデモで見たように、開発するカスタムアプリケーションでどのようにデータを表示および操作するかについては注意する必要があります。次の単元では、必要に応じてデータが制限されていることを確認するツールについて説明します。

リソース