ブログ

【SAP】User-Agentの偽装を防ぐ!特定のブラウザのみにアクセスを制限する確実な識別法

この記事をSNSでシェア!

はじめに

皆様の開発現場やプロジェクトにおいて、「サポートするブラウザを固定することで、動作検証やテストの量を限定させたい」といったケースに直面したことはありませんか?
実際に今回の記事を執筆するにあたり、「特定のブラウザ以外からのアクセスをブロックしたい」という要件がありました。

こうしたSAP Fioriアプリにおけるアクセス制御を実現するため、今回は SAP Build Work Zone で利用可能な「Shell プラグイン」機能を活用した手法をご紹介します。

本記事は、SAP Fiori(SAPUI5)開発を担当されているエンジニアの方や、SAP BTP環境下で厳密なブラウザ制御を実装したいエンジニアの方に向けて執筆しています 。

Shell プラグインとは?

Shell プラグインとは、Fiori Launchpad(FLP)のシェルをカスタマイズするためのHTML5アプリケーションの一種です。
通常のFioriアプリとは異なり、ログイン時にバックグラウンドで実行されるため、サイト全体に共通のロジック(アクセス制御など)を組み込むのに最適です。
今回はこの機能を活かし、ブラウザ判定によるアクセス制御を実装します。

Shell プラグインは、通常の Fiori アプリとは構成が異なります。
View(画面)を持たず、FLP起動時に裏で動くJavaScriptとして機能するのが特徴です。

なぜ User-Agent(文字列判定)だけでは不十分なのか?

特定のブラウザのみに対応させたい場合、従来のUser-Agent 文字列 を利用した判定・制御が思い浮かぶかもしれません。
しかし、これには実務上、解決すべき2つの大きな課題が存在します。

1.検証ツールによる容易な偽装

User-Agent は「検証ツール(デベロッパーツール)」の「Network conditions」などから簡単に書き換えが可能です。
これにより、User-Agent を書き換えることで、アクセス制限を簡単に回避できてしまいます。

2.Chromium系ブラウザの識別

現在の Microsoft Edge などの Chromium系ブラウザは、互換性を保つためにデフォルトの User-Agent 文字列内にChromeという単語を含めています。
これにより、単純な文字列判定(indexOf)だけでは、これらを区別しきれません。


本記事では、これらを阻止するために User-Agent Client Hints(UA-CH) を用いた判定の実装と、ブラウザ制御をFiori Launchpad全体に適用するための具体的な手順を解説します。
※本機能は、UA-CHに対応しているChromium系ブラウザ(Chrome, Edge, Operaなど)でのみ機能します。

従来の User-Agent今回の UA-CH
正確性低(Edge等もChromeを名乗る)高(識別可能)
UA偽装耐性低(偽装で誤判定)高(APIレベルでの情報取得)

例:Edgeからアクセス時にUAを書き換えるとchromeと判定されてしまう

前提条件

実行環境
SAP BTP Cloud Foundry環境(アプリケーションのデプロイ先として使用します。)
開発ツール
VS Code または SAP Business Application Studio(今回は VS Code を使用しています。)
前提知識
SAP UI5 および Fiori Launchpad Shell プラグインの基礎知識
検証ブラウザ
User-Agent Client Hints (UA-CH) に対応した Chromium 系ブラウザ(Google Chrome, Microsoft Edge 等)

実装

要件

・Chromeのみアクセス許可を想定
・Chrome以外からアクセスした場合は拒否
・アクセス拒否時はダイアログを表示し、ログアウト

BWZにアクセスしてからアクセス制御が機能するまでのフロー図は以下の通りです。

Component.js

アクセス制御ロジックをwebapp/Component.jsに記述します。
ここでは前述の通り、UA-CH(User-Agent Client Hints) を利用します。
従来の User-Agent 文字列のみの判定とは異なり、ブラウザが提供するブランドリストを取得し、検証ツールで偽装された「Chromeのふりをした他ブラウザ」を排除 できるのがポイントです。

sap.ui.define([
    "sap/ui/core/UIComponent",
    "your/namespace/model/models",
    "sap/m/MessageBox"
], (UIComponent, models, MessageBox) => {
    "use strict";

    return UIComponent.extend("your.namespace.Component", {
        metadata: {
            manifest: "json",
            interfaces: [
                "sap.ui.core.IAsyncContentCreation"
            ]
        },

        init() {
            // call the base component's init function
            UIComponent.prototype.init.apply(this, arguments);

            // set the device model
            this.setModel(models.createDeviceModel(), "device");

            // ブラウザ判定関数
            this._checkBrowser();
        },

        _checkBrowser: async function () {
            // Chrome判定
            var isChrome = false;

            if (navigator.userAgentData) {
                try {
                    // UA-CH対応時:chromeチェック
                    const ua = await navigator.userAgentData.getHighEntropyValues(["fullVersionList"]);
                    const hasChrome = ua.fullVersionList.some(item => item.brand === "Google Chrome");
                    const hasEdge = ua.fullVersionList.some(item => item.brand === "Microsoft Edge");
                    const hasOpera = ua.fullVersionList.some(item => item.brand === "Opera" || item.brand === "OPR");

                    isChrome = hasChrome && !hasEdge && !hasOpera;
                } catch (e) {
                    // UA-CHエラー時:アクセス拒否
                    isChrome = false;
                }
            }else{
                // UA-CH未対応時:アクセス拒否
                isChrome = false;
            }

            // 判定
            if (isChrome) {
                console.log("Access OK");
            } else {
                MessageBox.error(
                    "当システムは Google Chrome 専用です。ブラウザを切り替えて再度アクセスしてください。",{
                        onClose: function () {
                        //ログアウト処理
                        sap.ushell.Container.logout()
                    }
                });
            }
        }
    });
});
manifest.json

「Shell プラグイン」として認識させるために、webapp/manifest.json を修正します。

参考:https://help.sap.com/docs/cloud-portal-service/sap-cloud-portal-service-on-cloud-foundry/add-shell-plugin

アプリのタイプを「component」に変更

"sap.app": {
    "id": "xxxxxx",
    "type": "component",
    ...
}

crossNavigation 設定を追加

"sap.app": {
    ...
    "crossNavigation": {
        "inbounds": {
	    "Shell-plugin": {
	        "signature": {
		    "parameters": {},
		    "additionalParameters": "allowed"
	        },
	        "hideLauncher": true,
		"semanticObject": "Shell",
		"action": "plugin"
            }
        }
    }
}

“type”: “plugin” の追加

"sap.app": {
    ...
},
"sap.flp": {
    "type": "plugin"
},

動作検証

通常通りビルド・デプロイを行うことで、実装できます。

ブランドリストの比較
chromeからアクセス時

①通常通りアクセス可能
 ※ダイアログ表示なし

Edgeからアクセス時

①ダイアログ表示


②「Close」押下後、ログアウト

Edgeからアクセス時(UA偽装)

①ダイアログ表示


②「Close」押下後、ログアウト

最後に

今回は、SAP Fiori Launchpad Shell プラグインを用いて、UA-CHによるブラウザ判定とアクセス制限を実装する方法を解説しました。

なお、今回はGoogle Chrome専用のアクセス制限を例に解説しましたが、UA-CHに対応しているブラウザ(Microsoft EdgeやOperaなど)であれば、同様の手法で判定・制御をカスタマイズすることが可能です。
要件に合わせて処理を調整し、ご活用ください。

従来の User-Agent 判定の懸念点解消や、検証ツールによる偽装対策など、本記事が同じ課題を持つエンジニアや学習者の方々の一助となれば幸いです。

今後も、SAPに関する技術記事や備忘録を発信していきます。
最後までお読みいただき、ありがとうございました。

投稿者プロフィール

山田 翔太
山田 翔太
2024年新卒入社。
現在はJavaやvue.js、AWSを中心にWebアプリの開発に取り組んでいます。
趣味はスポーツ観戦で、野球やサッカー、バスケットボールなど名古屋のチームを応援しています。
業務で学んだことや疑問に感じた点を記事として発信し、共有していきたいと思います。
この記事をSNSでシェア!