Bandit とは
Python コードのセキュリティ問題をチェックしてくれるツールです。Amazon CodeGuru Reviewer の内部でも使用されています。
試してみる
実際のプロジェクトへの導入方法を確認します。
インストール
pip でインストールが可能です。
$ pip install bandit
正しくインストールされていれば以下コマンドでバージョンを確認可能です。
$ bandit --version bandit 1.7.4 python version = 3.9.13 (main, Aug 21 2022, 11:28:09) [Clang 13.1.6 (clang-1316.0.21.2.5)]
実行方法
以下コマンドで、ファイルを指定して実行できます。
$ bandit bandit_sample.py [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.9.13 [node_visitor] WARNING Unable to find qualified name for module: bandit_sample.py Run started:2022-09-26 10:15:47.166530 Test results: >> Issue: [B307:blacklist] Use of possibly insecure function - consider using safer ast.literal_eval. Severity: Medium Confidence: High CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) Location: bandit_sample.py:3:0 More Info: https://bandit.readthedocs.io/en/1.7.4/blacklists/blacklist_calls.html#b307-eval 2 3 eval(sys.args[1]) 4 -------------------------------------------------- Code scanned: Total lines of code: 2 Total lines skipped (#nosec): 0 Run metrics: Total issues (by severity): Undefined: 0 Low: 0 Medium: 1 High: 0 Total issues (by confidence): Undefined: 0 Low: 0 Medium: 0 High: 1 Files skipped (0):
チェック対象にしたサンプルコードは以下の通りです。コマンドライン引数をそのまま eval するという暴挙に出ています。
# bandit_sample.py import sys eval(sys.args[1])
-r
オプションでディレクトリ指定が可能です。
$ bandit -r path/to/dir
-h
オプションでその他オプションの解説、およびチェック対象の一覧が表示されます。
$ bandit -h usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] [-p PROFILE] [-t TESTS] [-s SKIPS] [-l | --severity-level {all,low,medium,high}] [-i | --confidence-level {all,low,medium,high}] [-f {csv,custom,html,json,screen,txt,xml,yaml}] [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q] [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] [--ini INI_PATH] [--exit-zero] [--version] [targets ...] Bandit - a Python source code security analyzer positional arguments: targets source file(s) or directory(s) to be tested optional arguments: -h, --help show this help message and exit -r, --recursive find and process files in subdirectories -a {file,vuln}, --aggregate {file,vuln} aggregate output by vulnerability (default) or by filename -n CONTEXT_LINES, --number CONTEXT_LINES maximum number of code lines to output for each issue -c CONFIG_FILE, --configfile CONFIG_FILE optional config file to use for selecting plugins and overriding defaults -p PROFILE, --profile PROFILE profile to use (defaults to executing all tests) -t TESTS, --tests TESTS comma-separated list of test IDs to run -s SKIPS, --skip SKIPS comma-separated list of test IDs to skip -l, --level report only issues of a given severity level or higher (-l for LOW, -ll for MEDIUM, -lll for HIGH) --severity-level {all,low,medium,high} report only issues of a given severity level or higher. "all" and "low" are likely to produce the same results, but it is possible for rules to be undefined which will not be listed in "low". -i, --confidence report only issues of a given confidence level or higher (-i for LOW, -ii for MEDIUM, -iii for HIGH) --confidence-level {all,low,medium,high} report only issues of a given confidence level or higher. "all" and "low" are likely to produce the same results, but it is possible for rules to be undefined which will not be listed in "low". -f {csv,custom,html,json,screen,txt,xml,yaml}, --format {csv,custom,html,json,screen,txt,xml,yaml} specify output format --msg-template MSG_TEMPLATE specify output message template (only usable with --format custom), see CUSTOM FORMAT section for list of available values -o [OUTPUT_FILE], --output [OUTPUT_FILE] write report to filename -v, --verbose output extra information like excluded and included files -d, --debug turn on debug mode -q, --quiet, --silent only show output in the case of an error --ignore-nosec do not skip lines with # nosec comments -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) -b BASELINE, --baseline BASELINE path of a baseline report to compare against (only JSON-formatted files are accepted) --ini INI_PATH path to a .bandit file that supplies command line arguments --exit-zero exit with 0, even with results found --version show program's version number and exit CUSTOM FORMATTING ----------------- Available tags: {abspath}, {relpath}, {line}, {col}, {test_id}, {severity}, {msg}, {confidence}, {range} Example usage: Default template: bandit -r examples/ --format custom --msg-template \ "{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}" Provides same output as: bandit -r examples/ --format custom Tags can also be formatted in python string.format() style: bandit -r examples/ --format custom --msg-template \ "{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}" See python documentation for more information about formatting style: https://docs.python.org/3/library/string.html The following tests were discovered and loaded: ----------------------------------------------- B101 assert_used B102 exec_used B103 set_bad_file_permissions B104 hardcoded_bind_all_interfaces B105 hardcoded_password_string B106 hardcoded_password_funcarg B107 hardcoded_password_default B108 hardcoded_tmp_directory B110 try_except_pass B112 try_except_continue B201 flask_debug_true B301 pickle B302 marshal B303 md5 B304 ciphers B305 cipher_modes B306 mktemp_q # 以下略
設定
プロジェクト導入時、毎回コマンドラインオプションを指定するのも面倒です。そんな時は設定ファイルを用意します。.bandit
ファイルに INI ファイル形式で設定を記述できます。
# .bandit [bandit] exclude = tests skips = B101,B601
上記例の場合、tests
ディレクトリをチェック対象から除外、また B101
、B601
のテストは実施しなくなります。その他の設定は以下ページを参照ください。
PEP 518 に準拠して pyproject.toml
にも記載可能です。以下のような記載になります。
# pyproject.toml [tool.bandit] exclude_dirs = ["tests"] skips = ["B101", "B601"]
注意点は、pyproject.toml
の場合 exclude
-> exclude_dirs
とすること、実行時に -c
オプションで pyproject.toml
を指定することです。
$ bandit -c pyproject.toml -r .
特定箇所のみチェックを無効にしたい
# nosec
コメントで当該行のチェックを無効にできます。
# bandit_sample.py import sys eval(sys.args[1]) # nosec B307
flake8 のプラグインとして
有志が開発した flake8-bandit
を使えば flake8 のプラグインとして、つまり flake8 実行時に Bandit のチェックも実行してくれます。
こちらも pip でインストール可能です。事前に flake8 はインストールしておく必要がありますが、Bandit 自体は依存に含まれているので bandit
、flake8-bandit
を両方インストールする必要はありません。
$ pip install flake8-bandit
flake8 を実行すると Bandit のチェックが行われていることが確認できました。
$ flake8 ./bandit_sample.py:3:1: S307 Use of possibly insecure function - consider using safer ast.literal_eval.
注意点としてはコード体系が BXXX
ではなく SXXX
となっているので読替えが必要な点です。これは flake8-bugbear
という別プラグインが BXXX
のコード体系を使用している経緯からです。
前述の特定箇所のみチェックを無効にする方法も flake8 式にする必要があり、# noqa
で記載します。
# bandit_sample.py import sys eval(sys.args[1]) # noqa S307
設定については .bandit
ファイルを使用できます。BXXX
、SXXX
両方のコード体系に対応しているようです。.flake8
ファイルの場合は SXXX
のコード体系のみ、pyproject.toml
ファイルには対応していないようです。
まとめ
Bandit を使って Python コードのセキュリティをチェックする方法をご紹介しました。継続的にチェックしてセキュアなコーディングをしていきましょう!