📑

Next.js Basic認証

2023/02/09に公開

Basic認証とは

概要

このタイトルを見て、べーしっく認証???となる方が多いかと思います。
正直筆者も??????😇という感じでした笑
しかし、この画像を見るだけで、「あ!見たことある!」となるかと思います。

image.png

これがBasic認証です。
Basic認証とは、Webサイトにアクセス制限をかける認証(httpの機能が持つ認証システム)の1つで
手軽に制限をかける認証方法のことです。
特徴は、特定の領域にだけ制限をかけられることです。

仕組み

Basic認証では、URLの入力またはリンクをクリックすることで
ブラウザがWebサーバーにリクエストを送ります。
ここでBasic認証を設定していると、Webサーバーはブラウザに認証が必要であることを知らせます。

Webサーバーから認証の知らせを受けたブラウザは、
画面に認証ダイアログを表示して認証を求め、正しい値を入力すれば認証が通り、
アクセスができるようになります。

上記のBasic認証では、「.htaccess」「.htpasswd」という2つのヘッダが働いていて
Basic認証はサーバーの認証をかけたいフォルダに
「.htaccess」「.htpasswd」という2つのファイルを作成し、
それぞれに決まったコードを記述するだけで設定は完了します。
(Nextでは少し特殊な方法でコードを記述すれば、同様のファイルを作成してくれます)
上記のヘッダ内に記載したIDやパスワードと照合することで、
認証の許可もしくは拒否という仕組みが成立します。

ログイン情報が一致していればアクセスが許可され、
不一致であればアクセスは拒否されるため、ユーザーはファイルにアクセスできません。
(ファイル:Nextではページという表現の方がわかりやすいかもですね!)

これがBasic認証の仕組みになります🙌

メリット・デメリット

時間がない方向け

  • メリット
    • 他の認証方法より比較的簡単
    • 一度認証すると、ログイン情報が記録される
  • デメリット
    • サーバーをまたいだ認証設定ができない
    • クローラーが巡回できないため、SEO対策はできない
    • セキュリティレベルが低め

メリット

  1. 他の認証方法より比較的簡単
  2. 一度認証すると、ログイン情報が記録される

デメリット

  1. サーバーをまたいだ認証設定ができない
  2. クローラーが巡回できないため、SEO対策はできない
  3. セキュリティレベルが低め

Basic認証の適用範囲は、ヘッダである「.htaccess」ファイルを設置したディレクトリのみなので
サーバーをまたぐなどディレクトリの範囲を超えた範囲で制限はかけられません。

また、Basic認証を適用した範囲は「クローラー」もアクセス不可となります。
クローラーが巡回できない範囲は情報が取得できず、検索結果に表示できません。
そのためSEO効果を狙いたい場合、Basic認証は使わない方が良いでしょう。

セキュリティレベルが低いことに関してですが、
Basic認証ではログイン情報を入力する際「Base64」という文字コードを使います。
Base64はアルファベット、数字、記号の64種類のみの文字コードでやり取りするため、
盗聴されやすい通信になります。

さらに、簡易的な通信である「Base64」を平文でやり取りする「http」通信でやり取りすれば、
盗聴されやすくすり抜けられるリスクが上がります。
そのため、Basic認証は脆弱性が高いといわれます。
(httpsであればSSL/TLSで暗号化されるため、比較的マシです)

このようなメリット、デメリットがありますが
今回はインターン先の社内でのみ使用するといったユースケースだったため
採用しました!

実装

実装するにあたって、必要なことは以下の2点です!

  1. API
  2. ミドルウェア
  3. 環境変数

ほぼコピペで実装できるようになってます〜🙌

API

まず、APIは以下のコードを記述します。

pages/api/auth.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(_: NextApiRequest, res: NextApiResponse) {
  res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"')
  res.statusCode = 401
  res.end(`Basic Auth Required.`)
}

解説

res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"')
レスポンスのヘッダ設定です。
ヘッダの内容(WWW-authenticate)が気になる方は以下の記事が参考になると思います!

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

res.statusCode = 401では、認証が不許可であることを示すUnauthorized
表すステータスコード401をセットします。

最後のres.end(Basic Auth Required.)では
認証失敗時に表示されるメッセージを入れます。

次に、ミドルウェアを実装しましょう〜!

ミドルウェア

以下のコードをコピペしましょう!

pages/_middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  const url = req.nextUrl
  if (!url.pathname.startsWith('/')) return

  const basicAuth = req.headers.get('authorization')

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1]
    const [user, pwd] = Buffer.from(authValue, 'base64').toString().split(':')

    if (
      user === process.env.NEXT_PUBLIC_BASIC_AUTH_NAME &&
      pwd === process.env.NEXT_PUBLIC_BASIC_AUTH_PASSWORD
    ) {
      return NextResponse.next()
    }
  }
  url.pathname = '/api/auth'

  return NextResponse.rewrite(url)
}

解説

if (!url.pathname.startsWith('/')) return/には使用したいページのパスを入れます。
ここでは、使うページ以外でBasic認証が動いてほしくないので
早期リターンにしています。

const [user, pwd] = Buffer.from(authValue, 'base64').toString().split(':')
ここはよく検索でヒットする内容と大きく異なる箇所になります。
検索で出てくるものはBuffer.from(authValue, 'base64').toString()の代わりに
atob()が使用されますが、今はBuffer.fromの書き方が推奨されているので
こちらを使用しました!

if ( user === process.env.NEXT_PUBLIC_BASIC_AUTH_NAME && pwd === process.env.NEXT_PUBLIC_BASIC_AUTH_PASSWORD )
ここも他の検索記事と少し違いますが、下手にパスワードが漏洩しても困るので
.envに記述し、環境変数として扱いました!

環境変数の設定

.env
NEXT_PUBLIC_BASIC_AUTH_NAME=username
NEXT_PUBLIC_BASIC_AUTH_PASSWORD=password

こちらは先ほど用意した環境変数ですね!
Nextでは、NEXT_PUBLIC_で始める必要があるので
その点だけご注意を!

終わりに

Basic認証、いかがでしたか?
サクッと簡単に認証したい場合に使えるので、ユースケースがあっていれば
すごく便利ですね!
ぜひみなさんもユースケースを考えて適切な認証を
作成しましょう✊
それでは👋

参考文献

https://www.cybersolutions.co.jp/blog/basic/#:~:text=Basic認証とはWeb,られる点が特徴です。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

https://nextjs.org/docs/advanced-features/middleware

Discussion