見出し画像

【知識の泉】🌟 「CORSでAPI叩けない!」

例によって読者置き去りの記事ですが、下ののネタのCORSの解説です。
記事の内容自体は実用的なはずです。



🌟 「CORSでAPI叩けない!」の本当の意味

まず大事なのは、

  • 「CORSがブロックしてるんじゃなくて、SOP(Same-Origin Policy)がブロックしてる!」

  • 「CORSを正しく設定すれば、SOPのブロックを解除できる!」


🌟 SOP(Same-Origin Policy)がAPIをブロックする

SOP(同一オリジンポリシー)は、ブラウザが持っているセキュリティルール。
「異なるオリジンのリソースを勝手に取得できないようにする」仕組みになっている。

例えば、

  • フロントエンド(React, Vue, etc.)のオリジン → http://localhost:3000

  • バックエンド(APIサーバー)のオリジン → http://localhost:8000

この状態で、フロントエンドからAPIを叩こうとすると👇

fetch("http://localhost:8000/api/data")  // ← オリジンが違う!

💥 SOPによってブロックされる!


🌟 「CORSエラー」の正体

実際にAPIを叩くと、ブラウザのコンソールに次のようなエラーが出る👇

Access to fetch at 'http://localhost:8000/api/data' from origin 'http://localhost:3000' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

このメッセージのポイントは、

  • "blocked by CORS policy" → 実際は SOPがブロックしている

  • "No 'Access-Control-Allow-Origin' header" → CORSを設定すれば解除できる!


🌟 CORS(Cross-Origin Resource Sharing)とは?

CORS は「このAPIは異なるオリジンからのリクエストを許可してOK!」とAPI側が伝える仕組み。
API側で Access-Control-Allow-Origin を返せば、SOPのブロックを解除できる!


🌟 CORSを設定してSOPのブロックを解除する

API(バックエンド)側でCORSを設定すれば、異なるオリジンからのアクセスが許可される。


✅ 例1️⃣ axum で CORS を許可する(どこでもOKな設定)

use axum::{Router, routing::get};
use tower_http::cors::CorsLayer;

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(handler))
        .layer(CorsLayer::permissive());  // どのオリジンからでもOKにする!

    axum::Server::bind(&"0.0.0.0:8000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handler() -> &'static str {
    "Hello, CORS!"
}

📌 CorsLayer::permissive() を使うと、どのオリジンからのリクエストも許可される! 🚀✨
📌 ローカル開発時には便利だが、本番では制限をかけるべき!


✅ 例2️⃣ フロントエンド(React, Vue)だけを許可する

本番環境では「どこでもOK」ではなく、「特定のオリジンだけOK」にするのがベスト。

use axum::{Router, routing::get};
use tower_http::cors::{CorsLayer, Any};
use http::Method;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    let cors = CorsLayer::new()
        .allow_origin("https://frontend.example.com".parse().unwrap())  // 特定のオリジンを許可
        .allow_credentials(true)  // 認証情報を送れるようにする!
        .allow_methods([Method::GET, Method::POST]);

    let app = Router::new()
        .route("/", get(handler))
        .layer(cors);

    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
    println!("Listening on {}", addr);

    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handler() -> &'static str {
    "CORS is configured!"
}

📌 「特定のオリジンだけ許可 + allow_credentials(true)」が最も安全! 🚀✨


次に、「*(ワイルドカード)を使うと超危険!」 という話に入るよ! 🔥🔥🔥

🌟 「CORSが面倒だから * を設定すると超危険!」

💥 結論:*(ワイルドカード)を CORS の Access-Control-Allow-Origin に設定すると、セキュリティ的にヤバすぎる! 💥


✅ そもそも Access-Control-Allow-Origin: * って何?

👉 「どのオリジン(ドメイン)からのリクエストも許可する!」 という設定。
👉 つまり、インターネット上のどのサイトからでも API にアクセスできる!

📌 こんな設定は危険!

use tower_http::cors::CorsLayer;

let cors = CorsLayer::new()
    .allow_origin(tower_http::cors::Any)  // ❌ どこからでもOK!(超危険!)
    .allow_methods([Method::GET, Method::POST]); 

🔥 この設定だと…

  • 悪意のあるサイトから勝手にAPIを叩かれる! 🚨

  • CSRF(クロスサイトリクエストフォージェリ)が発生する! 💀

  • APIキーや認証情報が盗まれるリスクがある! 🕵️‍♂️


🔥 *(ワイルドカード)の危険性を具体的に解説!

1️⃣ APIが意図しないサイトから使われる!

例えば、API のエンドポイントが https://api.example.com/data にあるとする。
普通なら、許可されたフロントエンド (https://frontend.example.com) だけがアクセスできるべき。
でも、* を使うと どのサイトからでも勝手にアクセスできる! 💀

<script>
  fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => console.log("Hacked Data:", data));
</script>

👉 攻撃者のサイトが勝手にAPIを叩いて、データを盗める! 🚨


2️⃣ 認証情報が漏れる可能性がある(credentials: true と * の組み合わせ)

🚨 「CORS の * を設定すると、認証情報(クッキーやトークン)が送れなくなる」→ これを無理に回避しようとすると超危険!

❌ 超ヤバい設定(やっちゃダメ!)

let cors = CorsLayer::new()
    .allow_origin(tower_http::cors::Any)  // ❌ どこでもOK
    .allow_credentials(true)  // ❌ `*` と `credentials: true` は併用禁止!
    .allow_methods([Method::GET, Method::POST]);

📌 ブラウザの仕様で「* と credentials: true を一緒に使うとエラーになる」

  • だから allow_credentials(true) を設定すると、allow_origin("*") が使えなくなる!

📌 でも、攻撃者が「ワイルドカードをやめて、全部のオリジンを許可する」設定に変えさせることがある!

let cors = CorsLayer::new()
    .allow_origin(["https://malicious-site.com", "https://frontend.example.com"])  // ❌ 攻撃者のサイトを含めちゃった!
    .allow_credentials(true)  // ❌ Cookie が送られる!
    .allow_methods([Method::GET, Method::POST]);

💀 攻撃者のサイト (https://malicious-site.com) も許可されていると…

  • ログインしているユーザーが https://malicious-site.com にアクセスすると、自動でクッキーが送信される!

  • CSRF(クロスサイトリクエストフォージェリ)攻撃のリスクが爆増! 🚨

👉 認証情報(JWT, セッションID, APIキー)が盗まれる可能性がある!


🌟 じゃあ、CORS はどう設定すべき?

1️⃣ 本番では * を絶対に使わない!
2️⃣ allow_origin() で特定のオリジン(フロントエンドのURL)を指定!
3️⃣ 認証情報を使うなら、allow_credentials(true) も忘れずに!

✅ 正しい CORS の設定例

use axum::{Router, routing::get};
use tower_http::cors::{CorsLayer, Any};
use http::Method;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    let cors = CorsLayer::new()
        .allow_origin("https://frontend.example.com".parse().unwrap())  // ✅ フロントエンドのオリジンだけ許可!
        .allow_credentials(true)  // ✅ 認証情報を送れるようにする!
        .allow_methods([Method::GET, Method::POST]);

    let app = Router::new()
        .route("/", get(handler))
        .layer(cors);

    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
    println!("Listening on {}", addr);

    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handler() -> &'static str {
    "CORS is configured!"
}

📌 「特定のオリジンだけ許可 + allow_credentials(true)」が最も安全! 🚀✨


🎯 まとめ

🚨 「CORSが面倒だから *(ワイルドカード)を設定するのは超危険!」
🚨 特に credentials: true と * の組み合わせは、APIキーや認証情報漏洩のリスクが爆増!

本番環境では allow_origin("*") は絶対にNG!
allow_origin("https://frontend.example.com") で特定のオリジンだけ許可!
認証が必要なら allow_credentials(true) を追加!


📌 CORS の設定ミスは「開発者が知らないうちに API が乗っ取られる」リスクがあるから、超重要! 🚨🔥
📌 「とりあえず * で許可すればいいや」はダメ!ちゃんと allow_origin() を設定しよう!


🔥 これで「CORSでAPI叩けない!」問題の完全版、完成! 🚀✨
また 「この設定って本当に安全?」 って思ったら、いつでも聞いてねー!🔥🔥🔥

いいなと思ったら応援しよう!