RuffのBanditルールでPythonコードのセキュリティチェックをしてみた
はじめに
みなさんセキュアな Python コードを書けているでしょうか。
私も Lambda 開発で Python を使っていたのですが、コードのセキュリティチェックをどうするか気になっていました。 Bandit というセキュリティチェックツールを発見し導入を検討したところ、どうやら Ruff に同等のルールがあるようです。
Ruff を使っているプロジェクトではサクッと導入できるので、ぜひみなさんのプロジェクトでも導入を検討してみてください。
Ruffとは
Ruff は Rust で書かれた高速な Python 用のリンター・フォーマッターで、Flake8 や Black、isort などの多くのツールを統合したものです。既存のツールより 10-100 倍高速に動作し、最近の Python 開発では広く使われている認識です。
複数のツールを運用するのは大変なので、Ruff へ移行している方も多いのではないでしょうか。
そんな Ruff ですが、ドキュメントを確認すると flake8-bandit ルール(Sルール)を有効にすることで、Bandit とほぼ同等のセキュリティチェックをできることがわかりました。今回はこのルールを有効化してみます。
Banditとは
Bandit は Python コードのセキュリティ脆弱性を静的解析するツール(いわゆる SAST)です。開発段階でソースコードの脆弱性を検出できるため、セキュアな開発サイクルを回す上で重要な役割を担います。
主な検出項目は以下のとおりです。
- ハードコードされたパスワード
- SQL インジェクションのリスク
- 安全でない暗号化
- 危険なファイル操作
- シェルインジェクションのリスク など
Bandit 単体でインストールして使うこともできますが、Ruff の flake8-bandit ルール(Sルール)を使うことで、Ruff に統合された形でチェックできます。
何が嬉しいのか
Ruff で Bandit ルールを使うことで、以下のメリットがあります。
- 開発段階で脆弱性を検出できる
ハードコードされたパスワードや危険なファイル操作など、典型的なセキュリティリスクをコーディング時点で発見できます。pre-commit を活用すればコミット時に自動で検出が可能です。
- Ruff 既存ユーザーは追加インストール不要
すでに Ruff を使っているプロジェクトなら、設定ファイルに数行追加するだけで完了します。Bandit を別途インストールする必要がないので、依存関係を増やさずに済みます。
- CI/CD への組み込みも簡単
Ruff は既に CI/CD パイプラインで実行していることが多いです。もしすでにruff check .などでフォーマットチェックなどを実施していれば、セキュリティチェックも含められるので追加の設定が不要です。
やってみる
それでは実際に Ruff でセキュリティチェックを設定していきます。
Ruffでflake8-banditを有効化
それでは、Ruff の設定ファイルに flake8-bandit ルール(S)を追加してみましょう。
[tool.ruff] select = [ "E", # pycodestyle errors "F", # pyflakes + "S", # flake8-bandit (セキュリティチェック) ] または、ruff.toml を使用している場合は以下のようになります。
ファイル名: ruff.toml
select = ["E", "F", "S"] 設定ファイルを保存すれば、Ruff 実行時に Bandit ルールが適用されます。
設定が正しく反映されているか確認してみましょう。
$ ruff check --show-settings # "S" ルールが含まれていることを確認 脆弱性のあるコード例を用意
実際に脆弱性のあるコードを用意して、Ruff が検出できるか確認してみます。
import os # S105: ハードコードされたパスワード password = "my_secret_password" # S108: 安全でないtempファイル temp_file = "/tmp/test.txt" # S101: assertの使用 def validate_user(user_id): assert user_id > 0 このコードには意図的に 3 つの脆弱性を含めています。Ruff を実行してみましょう。
$ ruff check sample.py sample.py:4:12: S105 Possible hardcoded password assigned to: "password" | 3 | # S105: ハードコードされたパスワード 4 | password = "my_secret_password" | ^^^^^^^^^^^^^^^^^^^^ S105 5 | 6 | # S108: 安全でないtempファイル | sample.py:7:13: S108 Probable insecure usage of temporary file or directory: "/tmp/test.txt" | 6 | # S108: 安全でないtempファイル 7 | temp_file = "/tmp/test.txt" | ^^^^^^^^^^^^^^^ S108 8 | 9 | # S101: assertの使用(本番環境では危険) | sample.py:11:5: S101 Use of `assert` detected | 9 | # S101: assertの使用(本番環境では危険) 10 | def validate_user(user_id): 11 | assert user_id > 0 | ^^^^^^ S101 | Found 3 errors. それぞれの脆弱性が正しく検出されましたね。ハードコードされたパスワードや tmp フォルダの使用など、典型的なセキュリティ問題がすぐにわかります。
修正してみる
検出された脆弱性を修正してみましょう。先ほどの脆弱なコードを以下のように書き換えます。
import os import tempfile # 環境変数からパスワードを取得(ハードコードを避ける) password = os.environ.get("MY_PASSWORD") # 安全なtempファイルの作成 with tempfile.NamedTemporaryFile(delete=False) as temp_file: temp_path = temp_file.name # assertの代わりに適切なエラーハンドリング def validate_user(user_id): if user_id <= 0: raise ValueError("Invalid user_id") 修正後に Ruff を実行してみます。
$ ruff check sample.py All checks passed! **無事警告が表示されなくなりました。**これで安全なコードになっていますね。
一部のチェックを除外する
全て修正は行わず特定のルールを全て無効化する場合はpyproject.tomlで ignore を設定しましょう。
[tool.ruff] select = [ "E", # pycodestyle errors "F", # pyflakes "S", # flake8-bandit (セキュリティチェック) ] + ignore = ["S108"] 一部のファイルのみ除外したい場合はファイル名の指定でもできます。
[tool.ruff] select = [ "E", # pycodestyle errors "F", # pyflakes "S", # flake8-bandit (セキュリティチェック) ] + [tool.ruff.lint.per-file-ignores] + "sample.py" = ["S108"] 行単位で除外する場合は# noqa: S108のようなコメントを記載します。
# S108: 安全でないtempファイル temp_file = "/tmp/test.txt" # noqa: S108 注意点
Bandit は静的解析ツールのため、実行時にのみ発生する脆弱性や、複雑なロジックに潜む問題は検出できません。
より動的な SQL インジェクションや XSS など実行環境でのみ検出可能な脆弱性については、他のツールを検討してください。
まとめ
Ruff の flake8-bandit ルールを使うことで、Python コードのセキュリティ脆弱性を簡単にチェックできることを確認しました。
既に Ruff を使っているプロジェクトであれば、S 系のルール設定追加だけでセキュリティレベルを向上できます。もし Python を使ったプロジェクトで SAST を導入していない場合は、一度ルールを追加して検出結果を確認してみてはいかがでしょうか。
AI によるコーディングの比率が上がっている今こそ、こうしたツールによるセキュリティチェックも大事になってきます。 これを機に Python コードをよりセキュアな状態にしていきましょう。
以上、鈴木純がお送りしました。









