0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

フロントエンド・バックエンドの疎通までの設定

0
Posted at

この記事はプログラミング学習者が学習した内容を備忘録として記事におこしたものです。内容に不備などあればご指摘頂けると助かります。

前提条件

この記事はアプリケーションを制作中に開発環境でフロントエンドとバックエンドを通信できるように設定した時の実装内容を紹介しています。

使用したPC
MacBookAir M2のCPU

使用した主要なgemなど
バックエンド
Ruby: 3.4.1.0-slim-bookworm(Dockerイメージ)
Rails: 8.1.3.1

フロントエンド
Node.js: 24.21.0-bookworm-slim(Dockerイメージ)
TypeScript: 5.9.3
React: 19.2.8
Next.js: 16.3.6

データベース
PostgreSQL: 18.6-bookworm(Dockerイメージ)

実装概要

開発環境において、フロントエンドとバックエンドが疎通できるように設定を加えていてきます。

先ず、実際のdocker-compose.ymlを先に掲載しておきます。

docker-compose.yml
services:
  db:
    image: postgres:18.6-bookworm
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: treasure_box_development
    volumes:
      - db:/var/lib/postgresql
    ports:
      - '127.0.0.1:5432:5432'
    networks:
      - backend_net

  valkey:
    image: valkey/valkey:9.1.2
    # サーバー起動コマンド 60秒に1回以上変更があれば永続化保存(コンテナ停止時にメモリが消えるため) ログを推奨程度で表示
    command: valkey-server --save 60 1 --loglevel notice
    volumes:
      - valkey-data:/data
    healthcheck:
      test: ['CMD', 'valkey-cli', 'ping']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    ports:
      - '127.0.0.1:6379:6379'
    networks: 
      - backend_net # 内部DNS(名前解決)でIPアドレスが変わってもOK 他のDockerプロジェクトから分離

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      target: development
    command: npm run dev
    volumes:
      - ./frontend:/app # バインドマウント(ホストPCと接続)
      - /app/node_modules # 匿名ボリューム(ホストPCと同期せず、Docker内部に別途保存)
      - /app/.next # 匿名ボリューム(ホストPCと同期せず、Docker内部に別途保存)
    ports:
      - '127.0.0.1:8000:3000' # ホスト側ポート(左側)がbackendと重複しないように。コンテナ側は他コンテナと独立しているので重複問題無し
    tty: true
    stdin_open: true
    networks:
      - frontend_net

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: development
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      DATABASE_HOST: db
      DATABASE_USER: postgres
      DATABASE_PASSWORD: password
      VALKEY_URL: redis://valkey:6379/0
    networks:
      - frontend_net
      - backend_net
    volumes:
      - ./backend:/app
      - bundle:/usr/local/bundle
    ports:
      - '127.0.0.1:3000:3000' # ホスト側ポート(左側)がfrontendと重複しないように。コンテナ側は他コンテナと独立しているので重複問題無し
    depends_on:
      db:
        condition: service_started
      valkey:
        condition: service_healthy
    tty: true
    stdin_open: true
volumes:
  bundle:
  db:
  valkey-data:
networks:
  frontend_net:
    driver: bridge # 同一ネットワーク内でのみ通信可
  backend_net:
    driver: bridge # 同一ネットワーク内でのみ通信可

通常、ブラウザを使った通信には同一オリジンポリシー(SOP)が原則として当てはまり、同じオリジン同士でしか通信ができないように設定されています。

ここで、オリジンについて説明しておきます。
https://www.sample.com/443/questions/1 というURLを例として説明すると、
以下のように分類して説明することができます。
https: スキーム(プロトコル)
www: ホスト
sample.com: ドメイン
443: ポート
questions/1: パス

オリジンとは、スキーム、ホスト、ドメインとポートまでを範囲とします。
上記の例ではhttps://www.sample.com/443 となります。
URLでポートが表示されるずに省略されるので、実質はhttps://www.sample.com と同義です。

現状の開発環境(上述のdocker-compose.yml)ではフロントエンドは127.0.0.1(loalhost):8000、バックエンドは127.0.0.1(localhost):3000なので、同一オリジンポリシー(SOP)によりフロントエンドからバックエンドのアクセスはブロックされてしまいます。
そこでCORSという異なるオリジン間でリソースの共有ができるようになる仕組みを利用することになります。
以下はCORSによる接続までの設定を解説しています。

先ず、バックエンド側のRubyでgem "rack-cors"をインストールします。
通常はGemfileの中でコメントアウトされているので解除して使いましょう。

Gemfile
gem "rack-cors"
インストールコマンド
# ローカル
bundle install

# Docker内部(上述のymlファイルのサービス使用時)
docker compose run --rm --no-deps backend bundle install

bundle install: Gemfileに新しく加えられてGemをインストールする
docker compose run: 既存のイメージを使ってコンテナを作成・起動、イメージがなければbuildする
--rm: 作ったコンテナをコマンドの実行が終わったら削除する
--no-deps: docker-compose.ymlで設定されたdepends_onのサービスは起動しない
backend: docker-compose.ymlで指定されたサービス名

次にconfig/initializers/cors.rbでコメントアウトされているコードを解除後に追記して使います。

config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "http://localhost:8000" # 許可するフロントエンドのURL

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      # 許可するHTTPメソッドの一覧
      credentials: true # Cookie共有の許可を決める(true/false)
  end
end

originsは許可するフロントエンド側のオリジンを指しています。

最後にconfig/environments/development.rbに追記します。

config/environments/development.rb
Rails.application.configure do
    config.hosts << "backend" # Railsのサービス名を入力
end

backendの部分はdocker-compose.yml上のサービス名を記述しています。
Docker compose内の内部ネットワークではDNSがサービス名をドメイン名として解釈してくれます。
この設定はRails6以降から導入されたHost Authorizationという機能設定で、Host名を検証することでHTTPホストヘッダー攻撃を防ぎます。

HTTPホストヘッダーとはバーチャルホストの仕組みでは一つのIPアドレスに複数のドメインを設定されたWebサイトにアクセスがきた時にどのドメインへのアクセスなのかを判別するためにクライアント側が送信するために必要な要素です。
HTTPホストヘッダーの中身はホスト名(ドメイン名)とポート名で構成されています。

HTTPホストヘッダー攻撃とはHostヘッダーが信用されたり、不適切に設定されるとアプリケーションに脆弱性が生まれる可能性があり、攻撃者がHostヘッダーに様々なコンテンツを入れることで件の脆弱性につけ入り、様々な脆弱性を引き起こすことを指します。

以上で、CORSの設定が完了して、フロントエンドとバックエンドの疎通が成立するようになります。

最後まで読んでいただきありがとうございます。

参考にしたサイト

オリジン間リソース共有 (CORS)とは
なんとなく CORS がわかる...はもう終わりにする。
同一オリジンポリシー
HTTP Host ヘッダー攻撃とは?

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?