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

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

学習の目的

この単元を完了すると、次のことができるようになります。
  • クロスサイトリクエストフォージェリの脆弱性の概要を説明する。
  • Lightning Platform アプリケーション内の CSRF の脆弱性を識別する。
  • コード レベルまたは組織レベルの保護を使用して CSRF の脆弱性を防止する。

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

クロスサイトリクエストフォージェリ (CSRF) は、以前からよく知られている Web アプリケーションの脆弱性です。Web アプリケーションで広く発生しているため、2007 年以降常に Web 脆弱性の上位にあります。

CSRF は、悪意のあるアプリケーションが、ユーザが現在認証されている信頼済みサイト上で、ユーザのクライアントに不正なアクションを実行させる脆弱性です。実際にはどういうことなのでしょうか?

説明のために、ここでは簡単な例を使用してこの攻撃が実際のアプリケーションでどのように動作するか順を追って確認していきます。

  1. Kingdom Management 開発者組織にログインし、Cross-Site Request Forgery アプリケーションを開きます。
  2. [CSRF Demo (CSRF デモ)] タブに移動します。

    このアプリケーションには、現在城で働いている従者のリストが表示されます。ユーザは、各従者を騎士に昇格させることができます。システム管理者または王だけにこのページへのアクセス権があります。

  3. [Knight This Squire (この従者を騎士にする)] リンクのいずれかをクリックして機能をテストします。

ページが更新され、従者が騎士になったことを知らせるアラートが表示されます。[Knight This Squire (この従者を騎士にする)] ボタンは、実際には /CSRF_Demo?UserId=<userid> に対して GET 要求を実行します。ページの読み込み時に URL パラメータ値を読み取り、自動的に従者のロールを騎士に変更します。

とても簡単そうです。脆弱性はどこにあるのでしょう? このシナリオをもう一度見て、少し変更しましょう。

今回は、Kingdom Management 開発者組織にログインした後、www.towncrier.com (1) のような別の Web サイトを閲覧することにしました。この Web サイトの読み込み時、悪意のある Web サイトが Kingdom Management 開発者組織に要求を実行し (2)、知らないうちに従者を騎士に昇格させます (3)。

CSRF の基本的な攻撃フローを示すスクリーンショット

この例は、クロスサイトリクエストフォージェリ攻撃のしくみを正確に表しています。攻撃者は、ユーザが現在認証されている信頼済みサイト (Kingdom Management アプリケーション) で、ユーザのクライアント (ブラウザ) に不正なアクション (従者の騎士昇格) を実行させます。

クロスサイトリクエストフォージェリの防止

CSRF 攻撃を成功させるのは容易ではありません。まず標的にしたユーザが攻撃ページを訪問するとともに、標的のサービスに認証されていなければなりません。多くの場合、これには攻撃者側で連携した手口が必要になります (これはフィッシング攻撃で最もよく見られます)。攻撃が成功すると深刻な影響を及ぼすことがあります。影響はユーザの作成や改変から、データの削除、アカウントの侵害にいたるまで多岐に渡ります。では、どうやって攻撃者がエンドユーザに攻撃を開始するのを防止すればよいでしょうか?

騎士昇格の例では、悪意のある Web サイトが、成功に必要な URL パラメータ (ユーザ ID) が含まれる要求を自動的に Kingdom Management アプリケーションに対して送信していました。この攻撃が成功するためには、攻撃者がパラメータの正確な名前を知っていて (つまり、攻撃者はパラメータ名が「id」や「user」ではなく「userID」であることを知っていなければならない)、関連する値も指定する必要があります。

2 つの URL パラメータ (userId と token) を必要とする、少し異なるバージョンのページを検討してみましょう。

userid と token を使用する要求と応答

これで同じ攻撃を実行するには、こちらでも攻撃者が両方のパラメータの値を正確に推測する必要があります (1)。

ただし、token パラメータ値をランダムにしたら、つまり要求ごとに変化する一意の値にしたらどうでしょうか? こうすると、攻撃者が現在の値を推測することはほぼ不可能になり、攻撃が防止されます (2)。

この例は、クロスサイトリクエストフォージェリの最も一般的な防止方法です。

この方法が成功するためには、次の 4 つの条件が満たされる必要があります。

  • 機密性の高い状態変更要求 (データベース操作を実行する要求) すべてにトークンが含まれていなければならない。
  • トークンは要求ごと、またはユーザのセッションごとに一意でなければならない。
  • トークンは予測困難 (長くランダム) でなければならない。
  • 要求が意図したユーザから発信されたことを確認するために、トークンはサーバによって検証されなければならない。

サーバで 4 つのステップがすべて適切に実装されると、攻撃者はトークンパラメータの現在の値を推測できず、ユーザのブラウザを操作して Kingdom Management アプリケーションに対して正しい従者の騎士昇格要求を実行させることができません。攻撃者にはエラーが表示されます。

Salesforce プラットフォームを使用した CSRF に対する保護

幸い、Salesforce には標準で開発者向けに CSRF に対する保護が含まれています。

Lightning Platform プラットフォームで実行されるすべてのフォーム要求は保護されます。下記はデフォルトで有効化されている Salesforce 組織の設定です。この保護を無効化する場合は、Salesforce に連絡する必要があります。この CSRF 設定を表示するには、[設定] -> [セキュリティのコントロール] -> [セッションの設定] を選択します。

Salesforce の CSRF 用設定ページが表示されたスクリーンショット

この設定が有効化されている場合、Apex フォームが読み込まれると常に、プラットフォームがフォームに com.salesforce.visualforce.ViewStateCSRF パラメータを追加し、トークンが送信時に検証されます。このトークンは、前述した CSRF 対策トークン要件に従っています。つまり、要求およびユーザごとに一意です。

Lightning Platform アプリケーションのクロスサイトリクエストフォージェリ

Salesforce が提供するデフォルトの CSRF 保護によって開発者の対応は容易になりますが、制限があります。すでに学習したとおり、プラットフォームでは CSRF 保護がデフォルトで有効化されています。ではなぜ、前の CSRF デモは機能したのでしょうか? この問題を調べるために、Visualforce を見てみましょう。

Visualforce:

 
<apex:page controller="CSRF_Demo" sidebar="false" action="{!performInitAction}" tabStyle="CSRF_Demo__tab"> 

Apex:

 
public void performInitAction() {
    try {
        String id = ApexPages.currentPage().getParameters().get('UserId');

        [...]

        Personnel__c obj = [select id, Name FROM Personnel__c WHERE id = :id];
        Resource_Type__c rt= [select id from Resource_Type__c where Name='Knight'][0];

        if(Personnel__c.sObjectType.getDescribe().isUpdateable()) {
            obj.Type__c=rt.id; update obj;
        }
    
    [...]
    } 

Visualforce ページがユーザのブラウザに表示される前に、CSRF_Demo.cls から performInitAction メソッドがコールされます。この関数は URL に指定された UserId パラメータを解析し、それを使用してユーザをシステム内の騎士として更新します。

このアクションは、ページの残りが読み込まれる前に実行されるため、プラットフォームで提供されるデフォルトの CSRF 保護を迂回します。要求には CSRF 対策トークンは含まれず、攻撃者は、ユーザのブラウザに https://[Your Instance]/apex/CSRF_Demo?Userid=<User_Id> を送信させるだけで、データベースでアクションを実行できます。

Lightning Platform アプリケーションでの CSRF の防止

この問題を防止するには、アプリケーションロジックを変更して、apex:page アクションハンドラからの状態変更操作をすべて削除します。これにより、すべての要求がデフォルトの Salesforce CSRF 保護によって保護されます。この軽減方法を Kingdom Management 開発者組織で試してみましょう。
  1. Kingdom Management 開発者組織にログインし、Cross-Site Request Forgery アプリケーションを選択します。
  2. [CSRF Mitigation Demo (CSRF 軽減デモ)] タブをクリックします。
    お気付きのように、これは [CSRF Demo (CSRF デモ)] タブで実行したのと同じアプリケーションです。ここで CSRF の脆弱性にけりを付けましょう。
  3. ページ下部にある Visualforce リンクをクリックします。
    この脆弱性を修正するには、まずアプリケーションがページ読み込み時にデータベース操作を実行しないようにします。
  4. Visualforce ページの最初の行を編集してアクション属性を削除します。コードは次のようになります。
    <apex:page controller="CSRF_Mitigation_Demo"sidebar="false" tabStyle="CSRF_Mitigation_Demo__tab">
    

    次に、outputLink を commandButton に変更する必要があります。これにより、ユーザがリンクをクリックすると、ページが更新されずに Apex コードが実行されます。

  5. Visualforce を編集して、アプリケーションの次の部分を
    <apex:outputLink value="/apex/CSRF_Demo?UserID={!person.Id}"> Knight This Squire </apex:outputLink>

    次のコードで置き換えます。

    <apex:commandLink value=”Knight This Squire” action=”{!knightSquire}”>
        <apex:param name="accId" value="{!person.id}" assignTo="{!currPerId}"/>
    </apex:commandLink>
  6. [Save (保存)] をクリックし、CSRF_Mitigation_Demo に戻ってアプリケーションを試します。

    アプリケーションは、前とまったく同じ機能を実行しますが、もう CSRF に対して脆弱ではありません。

  7. [Knight This Squire (この従者を騎士にする)] リンクを再度クリックします。

現在の騎士のリストの人数が 1 増えました。ただし、ブラウザの開発者ツールで要求を調べると、違いがあることに気付きます。

ブラウザの要求ヘッダーのスクリーンショット

要求では、ViewStateCSRF というパラメータが含まれていたことがわかります。このトークンは、アプリケーションで以前は行われていなかった CSRF に対するデフォルトの Salesforce 保護です。このトークンを変更するか、完全に省略してみると、フォーム送信が失敗します。つまり、ユーザは CSRF 攻撃から保護されます。Visualforce ページアクション値で状態変更操作を回避すると、アプリケーションは追加の作業なしでプラットフォームの CSRF 保護を利用できるようになります。

リソース

アプリケーションロジックの脆弱性防止

OWASP Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet (OWASP クロスサイトリクエストフォージェリ (CSRF) 防止早見表)

Salesforce Classic のコンテンツであることを示すには花のアイコンが使用されます

このモジュールは Salesforce Classic 向けです。ハンズオン組織を起動するときには、Salesforce Classic に切り替えてから、この Challenge を実行してください。