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での直打ち対策
先ほどの例の場合、Reactではルーティングでの制御が簡単に行えます。
ホーム画面(ログイン)➔ユーザー画面と処理を踏む場合に、ログインで得た値が無いとリダイレクトさせるという方法です。
ディレクトリ構成です。
/
└─ src
├─ pages
│ ├─ Home.tsx
│ ├─ Redirect.tsx
│ └─ User.tsx
└─ App.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>
);
}
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>
);
}
export default function RedirectPage() {
return (
<div>
<h1>リダイレクト画面</h1>
<p>不正なアクセスです。</p>
</div>
);
}
useLocationとuseNavigate
useNavigateはページ遷移を制御するフックで、useLocationはuseNavigateで遷移する際に、遷移先に値を渡すことができるフックです。
送信元となるHome側の以下の部分で、User側にstate:{ fromHome: true }を送信しています。
avigate("/user", {
state: { fromHome: true },
});
User側では、Homeから送られてきたstate:{ fromHome: xxxx }がtrueかfalseか判定しています。
const location = useLocation();
if (!location.state || !(location.state as any).fromHome)
①stateがundefind,nullの場合、②fromHomeがfalse,undefind,nullの場合は/redirectページにnavigateされ、強制的に画面が遷移します。
論理否定演算子!について(余談)
if文中の!はJavaScriptとNOT演算子なので、!を削除すると、判定が反転します。
if (location.state || (location.state as any).fromHome) {
navigate("/redirect", {
replace: true,
});
上記を実行した場合、Homeからのアクセスは不正とみなされリダイレクトされます。
URL直打ちの場合はstateを受信できないのでどのページにもアクセスできません。
useEffect(() => {
if (!location.state || !(location.state as any).fromHome) {
navigate("/redirect", {
replace: true,
});
}
}, [location.state, navigate]);
ReactでURL直打ち対策のまとめ
- useNavigateとuseLocationフックを使用する
- 値を渡して判定する
- 値がtrueではない場合はリダイレクトする
あとがき
コードの検証はしましたが、動作しなかったらごめんなさい。
間違っていたらご指摘ください。
Discussion