
【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 の信用まで下がるんだけど!!!!!! 💢💢💢