🔗

ReactでURL直打ち対策

に公開

はじめに

学園祭用の作品をReact(Vite)で開発中に、URLの直打ちでページをスキップできてしまうことに気づいたので、実施したフロントエンド側の対策方法を記事にまとめる。

URLの直打ちとは

URLの直打ちとは、URLを直接アドレスバーに入力してページにアクセスすることです。

例えば、以下のようなページで構成されたWEBアプリがあったとします。

root
├─ home
└─ user

このWEBアプリでは、homeとそれぞれのuser用の画面が存在します。URLは以下のようになると思います。

https://xxxxx.jp/
├─ https://xxxxx.jp/home
└─ https://xxxxx.jp/user
   └─ https://xxxxx.jp/user/id

今回はidごとにuserのページが存在しているものとします。
データベースには、以下のようにユーザーデータが保存されているとします。

id user_name
1 zenkun
999 zenjii

この場合、" https://xxxxx.jp/home "にアクセスするとサイトのメインコンテンツが表示されます。
" https://xxxxx.jp/user/1 "にアクセスするとzenkunのページが表示されます。
" https://xxxxx.jp/user/999 "にアクセスするとzenjiiのページが表示されます。

つまり、上記の構成だけではURLを入力することで誰でもユーザーの情報にアクセスできます。

実際のWEBアプリでは、ログインなどの認証処理を通してから各ページにアクセスする仕組みになっています。しかし、フロントエンド側やバックエンド側で対策をしていないと、ログインしていない状態でもURLを直接打つことで意図しないアクセスが可能になります。

Reactでの直打ち対策

https://github.com/CA01971020/url-protection_democode

先ほどの例の場合、Reactではルーティングでの制御が簡単に行えます。
ホーム画面(ログイン)➔ユーザー画面と処理を踏む場合に、ログインで得た値が無いとリダイレクトさせるという方法です。

ディレクトリ構成です。

/
└─ src
   ├─ pages
   │  ├─ Home.tsx
   │  ├─ Redirect.tsx
   │  └─ User.tsx
   └─ App.tsx

コードは以下のようになります。

Home.tsx
import { useNavigate } from "react-router-dom";

export default function HomePage() {
  const navigate = useNavigate();

  const handleNavigate = () => {
    navigate("/user", {
      state: { fromHome: true },
    });
  };

  return (
    <div>
      <h1>ホーム画面</h1>
      <button onClick={handleNavigate}>ログインする</button>
    </div>
  );
}
User.tsx
import { useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";

export default function UserPage() {
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    if (!location.state || !(location.state as any).fromHome) {
      navigate("/redirect", {
        replace: true,
      });
    }
  }, [location.state, navigate]);

  return (
    <div>
      <h1>ユーザー画面</h1>
      <p>ようこそ。ユーザーのページです。</p>
    </div>
  );
}

Redirect.tsx
export default function RedirectPage() {
  return (
    <div>
      <h1>リダイレクト画面</h1>
      <p>不正なアクセスです。</p>
    </div>
  );
}

useLocationとuseNavigate

useNavigateはページ遷移を制御するフックで、useLocationはuseNavigateで遷移する際に、遷移先に値を渡すことができるフックです。

送信元となるHome側の以下の部分で、User側にstate:{ fromHome: true }を送信しています。

Home.tsx
avigate("/user", {
  state: { fromHome: true },
});

User側では、Homeから送られてきたstate:{ fromHome: xxxx }がtrueかfalseか判定しています。

User.tsx
const location = useLocation();
if (!location.state || !(location.state as any).fromHome)

①stateがundefind,nullの場合、②fromHomeがfalse,undefind,nullの場合は/redirectページにnavigateされ、強制的に画面が遷移します。

論理否定演算子!について(余談)

if文中の!はJavaScriptとNOT演算子なので、!を削除すると、判定が反転します。

sample
    if (location.state || (location.state as any).fromHome) {
      navigate("/redirect", {
        replace: true,
      });

上記を実行した場合、Homeからのアクセスは不正とみなされリダイレクトされます。
URL直打ちの場合はstateを受信できないのでどのページにもアクセスできません。

User.tsx
 useEffect(() => {
    if (!location.state || !(location.state as any).fromHome) {
      navigate("/redirect", {
        replace: true,
      });
    }
  }, [location.state, navigate]);

ReactでURL直打ち対策のまとめ

  • useNavigateとuseLocationフックを使用する
  • 値を渡して判定する
  • 値がtrueではない場合はリダイレクトする

あとがき

コードの検証はしましたが、動作しなかったらごめんなさい。
間違っていたらご指摘ください。

Discussion