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

コード内のオープンリダイレクトの防止

学習の目的

この単元を完了すると、次のことができるようになります。
  • オープンリダイレクトを防止するための軽減戦略を 3 つ挙げる。
  • オープンリダイレクトを防止するためのホワイトリストベースのアプローチを実装する。
  • ローカルリダイレクトを強制してオープンリダイレクト攻撃をブロックする。

オープンリダイレクトの軽減戦略

Kingdom Management 開発者組織には、ローカルリダイレクトを使用してユーザをさまざまなアプリケーションコンポーネントに移動させるアプリケーションコンポーネントがあります。一方、前の単元で学習したように、ユーザを安全に移動させるには、アプリケーションに防御レイヤを追加する必要があります。

オープンリダイレクト攻撃を防止するには次の 3 つの主要な戦略があります。

  1. すべてのリダイレクトをハードコード化する。
  2. ローカルリダイレクトのみを強制する。
  3. リダイレクト先をホワイトリスト登録する。

リダイレクトのハードコード化

最初のオープンリダイレクト防止戦略では、ハードコード化されたリダイレクトを利用します。リダイレクトパラメータをユーザ (または潜在的な攻撃者) に公開するのではなく、次の例のように、ハードコード化されたリダイレクトに値を明示的に設定します。

Apex:

savePage = new PageReference(‘/home/home.jsp’);
savePage.setRedirect(true);
return savePage;

ただし、このアプローチは、ユーザアクションに基づいて動的に変化する柔軟なアプリケーションには向きません。したがって、このアプローチはすべての開発者の使用事例に有効というわけではありません。

ローカルリダイレクトのみの強制

次の防御オプションは、すべてのリダイレクトで強制的にローカルドメイン内のリソース (たとえば、アプリケーションが実行されている Salesforce インスタンスなど) のみへ移動させる方法です。この軽減戦略の中心にあるのは、ローカルドメインはシステム管理者の制御下にあるため、より広範囲のインターネットよりも安全であり、潜在的なリダイレクトの範囲を制限できるという考え方です。これはいわば、城の堀にかかる跳ね橋を上げて城壁内での移動のみを許可するようなものです。

このしくみを理解するために、Kingdom Management 開発者組織で詳細を探索してみましょう。

  1. Kingdom Management 開発者組織にログインし、Open Redirect アプリケーションに移動します。
  2. [Relative URL Protection Demo (相対 URL 保護デモ)] タブをクリックします。

    このアプリケーションでは、ユーザが城の労働者を城内のさまざまな場所での任務を遂行するように割り当てます。ユーザが [Save (保存)] または [Cancel (キャンセル)] をクリックすると、メインページにリダイレクトされて戻ります。ただし、このアプリケーションはオープンリダイレクトに対して脆弱です。URL パラメータ saveURLcancelURL を攻撃者が変更してユーザを任意の外部サイトにリダイレクトできるからです。そこで、Apex を変更してローカルリダイレクトのみが可能になるようにしましょう。

  3. ページ下部にある Apex リンクをクリックし、このアプリケーションのコントローラに移動します。
  4. save() 関数を更新し、Apex コードが下記のコードと同じになるように更新して、強制的にすべての URL を相対 URL にします。

    Apex:

    public PageReference save(){
        PageReference savePage;
        if (Schema.SObjectType.Personnel__c.isUpdateable()){
        try{
            update unassigned;
            String completion = ApexPages.currentPage().getParameters().get('onSave');
    
            if(completion.startsWith('/')){
                ​completion=completion.replaceFirst('/','');
            }
            savePage = new PageReference('/'+completion);
    
            savePage.setRedirect(true);
            return savePage;
        [...]
    
  5. Visualforce ページを再読み込みし、retURL の値として https://www.google.com のような外部 URL を入力します。エラーメッセージが表示されます。
  6. [保存] をクリックします。エラーメッセージが表示されます。

このしくみを理解するために、追加したコードを分解して確認しましょう。ユーザが [Save (保存)] をクリックすると、Apex の save 関数がコールされ、URL から retURL の値が読み込まれます。

String completion = ApexPages.currentPage().getParameters().get('onSave');

コードは次に、文字列がスラッシュで開始するかどうかをチェックします。

if(completion.startsWith('/')){

開始する場合、最初のスラッシュと後続のすべてのスラッシュを削除します (追加のプラス記号は、その直前にある文字の反復インスタンスすべてを表します)。

completion.replaceFirst('/+','');

次に、URL の先頭に 1 個のスラッシュが追加されます。

savePage = new PageReference('/'+completion);

この 1 個のスラッシュによって、すべての URL が Salesforce ローカルになります。たとえば、onSave に www.google.com を入力して保存すると、リダイレクトは存在しない https://c.[yourinstance].visual.force.com/www.google.com になります。これによりエラーメッセージが表示され、外部サイトへのリダイレクトが阻止されます。

先頭に結局スラッシュを追加するのに、なぜコードでわざわざスラッシュを削除するのか疑問に思われるかもしれません。攻撃者がリダイレクト機能を使用して試みる、巧妙な手口があります。URL /www.google.com はローカルリダイレクトになります。URL //www.google.com は多くのブラウザで有効な外部リダイレクトとして扱われます。攻撃者は /www.google.com を URL として送信する可能性があり、コードでスラッシュを追加しただけだと、知らないうちに外部リダイレクトが可能になることがあります。

現時点で二重スラッシュリダイレクトは Visualforce では機能しませんが、このデモのために onCancel URL パラメータと [Cancel (キャンセル)] ボタンを追加してあり、これが Apex の PageReference() メソッドではなく JavaScript ベースのリダイレクトを使用します。

<a href="/{!$CurrentPage.Parameters.onCancel}" id="cancelbutton">

この二重スラッシュの手口はここで試すことができます。onCancel URL の値を /www.google.com などに変更し、[Cancel (キャンセル)] をクリックして結果を観察します。

既知の外部ドメインのみへのリダイレクトのホワイトリスト登録

相対 URL 防御は、オプションリダイレクト攻撃を防止しますが、すべての開発者使用事例に適切というわけではありません。強制的な相対 URL の使用は、堀にかかる跳ね橋を上げるような効果があります。ただし、城を外の世界から隔離できない場合はどうなるでしょう? 近隣の町からの物資が必要な場合は? どうすればセキュアに物資を受け取れるでしょうか?

ここでの考え方は、開発者がホワイトリスト登録アプローチを利用して、URL リダイレクトで受け入れ可能な場所のリストを維持管理するとというものです。リストには受け入れ可能で安全な外部ドメインのみを登録します。リダイレクトが実行されると常に、そのリダイレクトのドメインがホワイトリストのドメインと比較されます。一致すれば、リダイレクトが実行されます。一致しなければ、要求はエラーを返します。これは城門に、運び込まれる物資と出て行く物資の出所をチェックする門番を追加するようなものだと考えてください。

これは以前の単元で学習した、Salesforce が標準ページで使用している、リダイレクト先として *.visual.force.com、*.salesforce.com、*.content.force.com のみを許可する方法とまったく同じです。

この保護方法を Kingdom Management 開発者組織で試してみましょう。

  1. Kingdom Management 開発者組織にログインし、Open Redirect アプリケーションに移動します。
  2. [URL Validation Protection Demo (URL 検証保護デモ)] タブをクリックします。
  3. ページ下部にある Apex リンクをクリックし、このアプリケーションのコントローラに移動します。
  4. save() 関数を更新し、Apex コードが下記と同じになるように更新して、特定のドメインへのリダイレクトのみを許可します。

    Apex:

    public PageReference save(){
        PageReference savePage;
        if (Schema.SObjectType.Requisition__c.isUpdateable()){
        try{
            update requisitions;
            String onsave = ApexPages.currentPage().getParameters().get('onSave');
                            
            URL currentURL = New URL('https://' + ApexPages.currentPage().getUrl());
            Set<String> whiteListedDomains = new Set<String>();
            whiteListedDomains.add(currentURL.getHost());
            whiteListedDomains.add('www.salesforce.com');
            whiteLIstedDomains.add('www.google.com');
    
            if( onsave == NULL || !whiteListedDomains.contains(New URL(onsave).getHost())){
                onsave = '/home/home.jsp';
            }
    
            savePage = new PageReference(onsave);
            savePage.setRedirect(true);
            return savePage;
    
    [...]
    

    変更されたコードでは、www.salesforce.com や www.google.com のような受け入れ可能なリダイレクトドメインのホワイトリストが作成されます。その後、アプリケーションは onSave の値を解析するとき、指定された URL がそれらのドメインのいずれかに含まれるかどうかをチェックします。含まれる場合はリダイレクトを実行し、含まれない場合はユーザをホームページにリダイレクトします。

  5. 新しいコードを試すには、Visualforce ページを保存して更新します。
  6. [保存] をクリックします。エラーメッセージが表示されます。