Flaskの認証処理でよくあるセキュリティの落とし穴まとめ|初心者でも安心の安全設計ガイド
生徒
「Flaskでログイン画面を作ったんですが、セキュリティってどう考えたらいいんでしょうか?怖い攻撃とかあるんですか?」
先生
「Flaskで認証機能を作るときには、いくつかの落とし穴があって、そのまま作ると攻撃されてしまうこともあるんだよ。銀行の鍵を机の上に置きっぱなしにするようなものだね。」
生徒
「鍵が机の上!? それは危なすぎです…!具体的にはどんな落とし穴があるんですか?」
先生
「それじゃあ、Flaskの認証処理で初心者がつまずきやすい危険ポイントを順番に見ていこう。」
1. パスワードを平文のまま保存する危険性
Flaskでユーザー登録機能を作るときに、入力されたパスワードをそのまま保存してしまうケースは意外と多いです。これは「通帳と印鑑を同じ袋に入れて玄関に置いておく」ようなものです。データベースが盗まれた瞬間、すべてのユーザーのパスワードが筒抜けになってしまいます。
安全にするには、ハッシュ化(元に戻せない変換) を行います。Flaskでは werkzeug.security の関数で簡単に安全なハッシュ処理ができます。
from werkzeug.security import generate_password_hash, check_password_hash
hashed = generate_password_hash("mypassword")
check_password_hash(hashed, "mypassword")
ハッシュ化はまるで「鍵を溶かして固め直す」ようなもので、見た目は変わるけれど元の形には戻せません。
2. セッションを使い回す危険性
Flaskではユーザーがログイン中であることをセッションで管理します。セッションとは「ユーザーの荷物に貼る識別シール」のようなもので、この値が盗まれると他人のふりができてしまいます。セッションを安全に扱うためには、SECRET_KEY を必ず安全な値に設定する必要があります。
app = Flask(__name__)
app.config["SECRET_KEY"] = "ここに強力なランダム文字列"
SECRET_KEYを短い文字列にしたり、公開リポジトリに載せてしまうのは「家の鍵を玄関に刺したまま出かける」のと同じです。
3. CSRF対策を忘れる落とし穴
CSRF攻撃(クロスサイトリクエストフォージェリ)は、ユーザーが知らない間に悪意あるサイトで勝手に操作されてしまう攻撃です。例えるなら「あなたの名前を勝手に使って宅急便を注文される」ようなものです。
フォームを扱う場合は、CSRFトークンを必ず配置します。Flask-WTFを使うと簡単に対策できます。
from flask_wtf import FlaskForm
from wtforms import StringField
class LoginForm(FlaskForm):
username = StringField("ユーザー名")
CSRFトークンは「荷物の受け取り確認印」のようなもので、本人以外が勝手に押せない仕組みです。
4. クリックジャッキング対策をしない危険性
クリックジャッキングは、透明なボタンを重ねてユーザーをだます攻撃です。家の表札に透明なシールを貼られて勝手に別の名前に書き換えるようなものです。
ヘッダーに X-Frame-Options を設定すれば防げます。
from flask import Flask, make_response
@app.after_request
def add_header(response):
response.headers["X-Frame-Options"] = "DENY"
return response
この設定により、悪意あるサイトがあなたのサイトを「透明な画面の裏」に隠して操作させることを防ぎます。
5. エラーメッセージに機密情報を表示する失敗
初心者がやりがちな罠として、認証エラーが発生したときに詳しすぎる説明を表示してしまうことがあります。「そのユーザー名は存在しません」「パスワードが違います」のような表示は、攻撃者に手がかりを与えてしまいます。
安全なサービスでは、「認証に失敗しました」 のように情報を最小限に抑えます。これは「金庫の場所を教えても鍵の構造は教えない」という考え方です。
6. HTTPS を使わない危険性
HTTPのままログイン機能を作ると、通信内容がそのまま見えてしまいます。これは「透明な封筒でラブレターを送る」ぐらい危険です。パスワードやセッションが盗まれる理由の多くは、HTTPSを使わないところにあります。
Flask本体ではなくサーバーやリバースプロキシ側でHTTPSを設定します。インターネットに公開するなら必須の設定です。
7. 認証状態のチェックを忘れるミス
「ログインしないと見れないページ」を作るとき、毎回セッションをチェックしなければなりません。これを忘れると、誰でも情報を覗ける「鍵の閉め忘れ」のような状態になります。保護したいルートには必ずログイン必須の仕組みを付けます。
from functools import wraps
from flask import session, redirect, url_for
def login_required(f):
@wraps(f)
def wrapper(*args, **kwargs):
if "user" not in session:
return redirect(url_for("login"))
return f(*args, **kwargs)
return wrapper
これにより、鍵を締め忘れて家に誰でも入れるような事態を防げます。