見出し画像

【AI文芸】『RustによるWebアプリケーション開発 設計からリリース・運用まで (KS情報科学専門書)』 ~ CORS Any 編(src/bin/app.rs の疑問点と改善策 付き)

はっ?!

ちょっと待って、Rust を使う意味って 「安全性」じゃないの???
それなのに バックエンドの CORS が Any になってるとか、もう完全に終わってるんだけど???


① バックエンドの選択肢が多いのに、Rust を選ぶ意味あるの?

バックエンドの技術スタックなんて、
Node.js, Python, Go, Java, Ruby, PHP, Elixir, Kotlin, C# ……選び放題じゃん?

特に Node.js や Go なら開発速度も速いし、エコシステムも充実してる
それなのに Rust をバックエンドに使う理由って何?

Rust って 学習コスト高いし、ライブラリもまだ発展途上で、開発速度は遅め
じゃあ Rust を選ぶ価値って何なの?

メモリ安全 → バッファオーバーフローなし
スレッド安全 → データ競合しにくい
型安全 → 変なバグが起こりにくい

この 「Rust だから安全」っていう売り があるからこそ、Rust のバックエンドを選ぶ価値があるのに……


② CORS Any にしたら、Rust の強みが全て無意味なんだけど???

fn cors() -> CorsLayer {
    CorsLayer::new()
        .allow_headers(cors::Any)
        .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
        .allow_origin(cors::Any)  // 💢💢💢 ちょっと、何やってんのよ!!!!!
}

💢 Rust のバックエンドを「最強の金庫」にしようとしたのに、扉が全開なんだけど????? 💢

これって 「最高の防犯システムを作りました!……ただし、鍵はかけません!」 って言ってるのと同じ。
Rust で セキュリティの高いサーバーを作る意味が、CORS Any ひとつで全部ぶっ壊れる!!

こんなの、もう攻撃してくださいって言ってるようなもんじゃん???


③ CORS Any の何がダメなの???

💢 「どこでもOK!」の危険性、わかってる??? 💢

攻撃者が自由にAPIを叩ける → クレデンシャル付きリクエストを乗っ取られる可能性
CSRF(クロスサイトリクエストフォージェリ)に完全に無防備
どこからでも不正リクエストが飛んできても全部許可!!
認証情報付きで他のサイトから API を叩ける設定になってるかも???

つまり、「APIのセキュリティ崩壊しました!」って宣言してるようなもん なんだけど?
「開発環境だから」? だからって Any はやめなさいよ!!!!!


④ ちゃんと制限しろ!!!

CORS ちゃんと設定しなさいよ!!!!!

特定のオリジンだけ許可

fn cors() -> CorsLayer {
    CorsLayer::new()
        .allow_origin("https://example.com".parse().unwrap()) // ここをちゃんと設定!!
        .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
        .allow_headers(["content-type"].iter().cloned())
}

「何でもOK!」じゃなくて、必要なところだけ許可しなさいよ!!!!!


⑤ もし開発環境なら…

開発環境で Any を使いたいなら、本番環境では絶対に有効にならないようにしとけ!!!

環境変数で管理

use std::env;

fn cors() -> CorsLayer {
    let allowed_origin = env::var("CORS_ALLOWED_ORIGIN").unwrap_or("https://example.com".to_string());

    CorsLayer::new()
        .allow_origin(allowed_origin.parse().unwrap())
        .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
        .allow_headers(["content-type"].iter().cloned())
}

.env で:

CORS_ALLOWED_ORIGIN=https://example.com

本番環境なら * を禁止するバリデーション を入れてもいい。
とにかく、 「全部許可!」はマジで論外!!!!!!」


⑥ CORS 以外のヤバいポイントをぶった切る!


① env のエラーハンドリングが甘い (which() の扱い)

let log_level = match which() {
    Environment::Development => "debug",
    Environment::Production => "info",
};

💢 ちょっと待って 💢
which() の戻り値、エラーになる可能性ない???

もし which() が環境変数をうまく読み取れなかったら、
このコード None を返すパターンを想定してない!!!

修正案

let log_level = match which().unwrap_or(Environment::Development) {  // ← デフォルト設定
    Environment::Development => "debug",
    Environment::Production => "info",
};

エラーになったら Development をデフォルトにする のがベター。
エラーが起きたことも ちゃんとログに出すべき!!!


② connect_database_with() で DB コネクションが足りなくなったらどうするの??

let pool = connect_database_with(&app_config.database);

💢 ちょっと、DB への接続って Result じゃないの???
接続に失敗したら、サーバーはどうなるの?????

修正案

let pool = connect_database_with(&app_config.database)
    .expect("Failed to connect to the database! Check your DB configuration.");

エラーを unwrap() で握りつぶさない!!!
ちゃんと 「接続に失敗した」ことをログに出して、落とすべき!!!


③ RedisClient::new() のエラーハンドリングが雑

let kv = Arc::new(RedisClient::new(&app_config.redis)?);

💢 ちょっと待って 💢
Redis に接続できなかったらどうするの???????

修正案

let kv = Arc::new(RedisClient::new(&app_config.redis)
    .expect("Failed to connect to Redis! Check your Redis configuration."));

または、Redis が使えなくてもサーバーを動かすなら Option<RedisClient> にする!

let kv = Arc::new(RedisClient::new(&app_config.redis).ok()); // 失敗したら None

「Redis が使えない」ことをちゃんとログに出せ!!!


④ bootstrap() の inspect_err() の tracing::error!() が変

.axum::serve(listener, app)
    .await
    .context("Unexpected error happened in server")
    .inspect_err(|e| {
        tracing::error!(
            error.cause_chain = ?e,error.message = %e, "Unexpected error"
        )
    })

💢 ちょっと 💢
この .inspect_err() って 本当に tracing::error!() を適切に使えてるの???

  • error.cause_chain = ?e は error = ?e でよくない???

  • .context() のメッセージを tracing::error!() に入れてるのに、 error.message = %e で2回出す意味ある???

修正案

.axum::serve(listener, app)
    .await
    .context("Unexpected error happened in server")
    .inspect_err(|e| tracing::error!(error = ?e, "Unexpected server error"));

これで 無駄なく、エラーの情報が適切に出る!!!


⑤ TcpListener::bind(addr) のポート固定問題

let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080);

💢 ポート 8080 固定はヤバくない??? 💢

問題点

  • すでに 8080 が使われてたらどうするの???

  • 環境変数で設定できるようにするべきじゃない???

  • 開発環境と本番環境でポートを分けられるようにするべき!!!

修正案

let port: u16 = std::env::var("PORT")
    .unwrap_or("8080".to_string())  // デフォルト 8080
    .parse()
    .expect("Invalid port number! Set the PORT environment variable.");

let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port);

環境変数 PORT を使えば、動的に設定できるようになる!!!


💢 まとめ 💢

which() のエラーハンドリングが甘い → デフォルトを設定すべき!
DB 接続 (connect_database_with()) に失敗したら、ちゃんとログを出して落とせ!
Redis への接続も expect() でエラーを明示的に出せ!
.inspect_err() の tracing::error!() の使い方が変!
TcpListener::bind(addr) のポート 8080 固定はやめて、環境変数にしろ!


ちょっと、このコード 本に載せる前にちゃんと見直しなさいよ!!!!!!
Rust って 「安全なバックエンドが作れる言語」 なんだから、
適当に書いたら Rust の信用まで下がるんだけど!!!!!! 💢💢💢

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