先週~今週にかけてやったこと

「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」読了、AWSのチュートリアル実施、自作アプリケーションにセキュリティ強化の実装。

「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」読了

2018年の書籍ということで、今はもう使われていないヘッダが書いてある点があったが全体的に非常に勉強になった。

AWSのチュートリアル実施

AWS的な開発について学びたかったので基本的なウェブアプリケーションを構築するというチュートリアルを実施した。

自作アプリケーションにセキュリティ強化の実装

「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」を元に下記の実装を行った。

  • Origin のチェック
  • CSRF対策のカスタムヘッダのチェック
  • MIMEタイプのチェック
  • X-Content-Type-Optionsヘッダの付与
  • Content-Security-Policyの設定

Content-Security-Policyの設定

Next.js では next dev で起動した開発モードのときと next build && next start で起動したプロダクションモードのときで許可内容を変えたほうが良さそう。

style-src と script-src で、開発モードのときは unsafe な指定をしないと動かない。

を参考に下記のように分岐して実装した。

const { PHASE_PRODUCTION_BUILD } = require('next/constants')
const { PHASE_PRODUCTION_SERVER } = require('next/constants')
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')

module.exports = (phase, { defaultConfig }) => {
  let contentSecurityPolicy = ''
  if (phase === PHASE_DEVELOPMENT_SERVER) {
    contentSecurityPolicy = `
      default-src 'none';
      img-src 'self' data:;
      font-src 'self';
      frame-src https://${process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}/;
      style-src 'self' 'unsafe-inline';
      script-src 'self' 'unsafe-eval' https://apis.google.com/;
      connect-src 'self' https://securetoken.googleapis.com/ https://identitytoolkit.googleapis.com/ ${process.env.NEXT_PUBLIC_API_BASE_URL} ${process.env.NEXT_PUBLIC_FRONT_WS};
    `
  } else if (phase === PHASE_PRODUCTION_BUILD || phase === PHASE_PRODUCTION_SERVER) {
    contentSecurityPolicy = `
      default-src 'none';
      img-src 'self' data:;
      font-src 'self';
      frame-src https://${process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}/;
      style-src 'self';
      script-src 'self' https://apis.google.com/;
      connect-src 'self' https://securetoken.googleapis.com/ https://identitytoolkit.googleapis.com/ ${process.env.NEXT_PUBLIC_API_BASE_URL} ${process.env.NEXT_PUBLIC_FRONT_WS};
    `
  }
  const securityHeaders = [
    {
      key: 'X-Content-Type-Options',
      value: 'nosniff',
    },
    {
      key: 'Content-Security-Policy',
      value: contentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
    },
  ]

  /** @type {import('next').NextConfig} */
  const nextConfig = {
    reactStrictMode: true,
    swcMinify: true,
    async headers() {
      return [
        {
          // Apply these headers to all routes in your application.
          source: '/:path*',
          headers: securityHeaders,
        },
      ]
    },
  }

  return nextConfig
}

‘Content-Security-Policy-Report-Only’ ヘッダを指定して様子を見ながら設定したが、今後不足があれば追加していく。

以上