Apex クラスの保護

デフォルトでは、Apex はシステムモードで実行されます。つまり、非常に上位の権限で実行され、あたかもユーザがほとんどの権限を持っていてすべての項目レベルおよびオブジェクトレベルのアクセス権が付与されているように動作します。これらのセキュリティレイヤが Salesforce UI のように適用されないため、適用するためのコードを記述する必要があります。そうしないと、通常は Salesforce UI ではユーザに表示されない機密データがコンポーネントによって公開されてしまう可能性があります。

Salesforce のレコードを処理する場合は、共有ルール、CRUD、および項目レベルセキュリティをユーザに代わって扱ってくれる Lightning Data Service の使用をお勧めします。「データガイドライン」を参照してください。

Note

Apex クラスのアクセス権のユーザへの付与 

認証済みユーザまたはゲストユーザは、ユーザのプロファイルまたは割り当てられた権限セットで Apex クラスへのアクセスが許可されている場合にのみ、@AuraEnabled Apex メソッドにアクセスできます。

Apex クラスへのユーザプロファイルまたは権限セットのアクセス権の設定についての詳細は、『Apex 開発者ガイド』の「クラスのセキュリティ」を参照してください。

共有ルールを適用 

クラスを宣言するときには、with sharing を指定して、コンポーネントが Apex コントローラを使用するときに共有ルールを適用することをお勧めします。

1public with sharing class SharingClass {
2    // Code here
3}

with sharing または without sharing を明示的に設定しない、あるいは inherited sharing を使用して定義された @AuraEnabled Apex クラスは、デフォルトまたは暗黙的な値として with sharing を使用します。ただし、with sharing または without sharing のどちらも明示的に指定されていない Apex クラスでは、Apex クラスが実行されているコンテキストから値が継承されます。したがって、このいずれかのキーワードを設定したクラスによって、明示的な共有動作を指定していないクラスが呼び出された場合、そのクラスは呼び出し元のクラスの共有動作を使用して動作します。クラスで共有ルールが適用されるようにするには、with sharing を設定します。

with sharing キーワードを使用すると、レコードレベルのセキュリティが適用されます。オブジェクトレベルおよび項目レベルのセキュリティは適用されません。オブジェクトレベルおよび項目レベルのセキュリティは、Apex クラスで手動で別々に適用する必要があります。

オブジェクト権限と項目権限の適用 (CRUD および FLS) 

Apex コード内でオブジェクトレベルおよび項目レベルの権限を適用するには、いくつかの代替方法があります。

WITH SECURITY_ENFORCED を使用した最も簡単な適用

オブジェクトレベルおよび項目レベルの権限を適用するには、Apex コード内の SOQL SELECT クエリ (サブクエリとクロスオブジェクトリレーションを含む) で WITH SECURITY_ENFORCED 句を使用します。

WITH SECURITY_ENFORCED 句は、セキュアなコードの開発経験が少ししかない場合や、権限エラーのグレースフルデグラデーションがアプリケーションで不要な場合に最適です。

次の例では、セキュアでないメソッド get_UNSAFE_Expenses() を含むカスタム Expense オブジェクトの項目が照会されます。このクラスは使用しないでください。

1// This class is an anti-pattern.
2public with sharing class UnsafeExpenseController {
3    // ns refers to namespace; leave out ns__ if not needed
4    // This method is vulnerable because it doesn't enforce FLS.
5    @AuraEnabled
6    public static List<ns__Expense__c> get_UNSAFE_Expenses() {
7        return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
8            ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
9    }
10}

次の例では、セキュアなメソッド getExpenses() が使用されます。このメソッドは、WITH SECURITY_ENFORCED 句を使用して、オブジェクトレベルの権限と項目レベルの権限を適用します。このクラスは UnsafeExpenseController の代わりに使用します。

1public with sharing class ExpenseController {
2    // This method is recommended because it enforces FLS.
3    @AuraEnabled
4    public static List<ns__Expense__c> getExpenses() {
5    // Query the object safely
6    return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
7            ns__Reimbursed__c, CreatedDate
8             FROM ns__Expense__c WITH SECURITY_ENFORCED];
9    }
10}

詳細は、『Apex 開発者ガイド』を参照してください。

stripInaccessible() を使用したグレースフルデグラデーション

権限エラーのグレースフルデグラデーションを可能にするには、stripInaccessible() メソッドを使用して項目レベルおよびオブジェクトレベルのデータ保護を適用します。このメソッドではクエリおよびサブクエリの結果からユーザがアクセスできない項目およびリレーション項目を除外します。必要に応じて、項目が除外されたかどうかを検出し、カスタムエラーメッセージを含む AuraHandledException を発生させることができます。

このメソッドを使用すると、DML 操作の前にアクセス不可の sObject 項目を削除して例外を回避したり、信頼できないソースからデシリアライズされた sObject をサニタイズしたりすることもできます。

次の例では、WITH SECURITY_ENFORCED SOQL 句の代わりに stripInaccessible() を使用するように ExpenseController が更新されます。結果は同じになりますが、stripInaccessible() では、WITH SECURITY_ENFORCED の使用時にアクセス違反が発生した場合、失敗する代わりに、グレースフルデグラデーションを行うことができます。

1public with sharing class ExpenseControllerStripped {
2
3    @AuraEnabled
4    public static List<ns__Expense__c> getExpenses() {
5        // Query the object but don't use WITH SECURITY_ENFORCED
6        List<ns__Expense__c> expenses =
7            [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
8                ns__Reimbursed__c, CreatedDate
9                 FROM ns__Expense__c];
10
11         // Strip fields that are not readable
12         SObjectAccessDecision decision = Security.stripInaccessible(
13               AccessType.READABLE,
14               expenses);
15
16         // Throw an exception if any data was stripped
17         if (!decision.getModifiedIndexes().isEmpty()) {
18            throw new AuraHandledException('Data was stripped');
19         }
20
21         return expenses;
22    }
23}

詳細や例については、『Apex 開発者ガイド』を参照してください。

DescribeSObjectResult メソッドと DescribeFieldResult メソッドを使用した従来のコード

WITH SECURITY_ENFORCED 句と stripInaccessible() メソッドが使用できるようになるまでは、オブジェクトと項目の権限を適用する唯一の方法は、Schema.DescribeSObjectResult メソッドと Schema.DescribeFieldResult メソッドをコールしてユーザのアクセス権限を確認することでした。そして、ユーザに必要な権限がある場合には特定の DML 操作またはクエリを実行します。

たとえば、Schema.DescribeSObjectResultisAccessibleisCreateable、または isUpdateable メソッドをコールすると、現在のユーザーが sObject に対して、それぞれ参照アクセス権、作成アクセス権、または更新アクセス権を持っているかどうかを確認できます。同様に、Schema.DescribeFieldResult が公開しているアクセス制御メソッドをコールすると、現在のユーザーが項目に対して参照アクセス権、作成アクセス権、または更新アクセス権を持っているかどうかを確認できます。

次の例では、Describe Result メソッドが使用されています。この方法では、定型のコード行がさらに多く必要になるため、代わりに WITH SECURITY_ENFORCED 句または stripInaccessible() メソッドを使用することをお勧めします。

1public with sharing class ExpenseControllerLegacy {
2    @AuraEnabled
3    public static List<ns__Expense__c> getExpenses() {
4        String [] expenseAccessFields = new String [] {'Id',
5                                                       'Name',
6                                                       'ns__Amount__c',
7                                                       'ns__Client__c',
8                                                       'ns__Date__c',
9                                                       'ns__Reimbursed__c',
10                                                       'CreatedDate'
11                                                       };
12
13
14    // Obtain the field name/token map for the Expense object
15    Map<String,Schema.SObjectField> m = Schema.SObjectType.ns__Expense__c.fields.getMap();
16
17    for (String fieldToCheck : expenseAccessFields) {
18
19        // Call getDescribe to check if the user has access to view field
20        if (!m.get(fieldToCheck).getDescribe().isAccessible()) {
21
22            // Pass error to client
23            throw new System.NoAccessException();
24        }
25    }
26
27    // Query the object safely
28    return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
29            ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
30    }
31}

関連トピック

The Japanese Summer '24 guide is now live

日本語の Summer '24 ガイドが公開されました! 「Component Reference (コンポーネントリファレンス)」は、以前と同様にコンポーネントライブラリにあります。