ばぁど・うぉっちんぐ

セキュリティに強いWeb屋。自由と春を求めて羽ばたく渡り鳥。

このブログはGoogle Analyticsを利用しています

【備忘録】フリーの脆弱性スキャナーNiktoの備忘録

最近、仕事でNiktoを使うようになっているので、Niktoに対しての備忘録。

Niktoとは?

github.com

概要①(英語)

Nikto is an Open Source (GPL) web server scanner which performs comprehensive tests against web servers for multiple items, including over 6700 potentially dangerous files/programs, checks for outdated versions of over 1250 servers, and version specific problems on over 270 servers. It also checks for server configuration items such as the presence of multiple index files, HTTP server options, and will attempt to identify installed web servers and software. Scan items and plugins are frequently updated and can be automatically updated. Nikto is not designed as a stealthy tool. It will test a web server in the quickest time possible, and is obvious in log files or to an IPS/IDS. However, there is support for LibWhisker's anti-IDS methods in case you want to give it a try (or test your IDS system).

概要①(日本語訳(基本はGoogle翻訳))

NiktoはオープンソースGPL)ライセンスで作成されている、Webサーバースキャナーであり、6700を超える潜在的に危険なファイル/プログラムを含む複数のアイテムについて、Webサーバーに対して包括的なテストを実行し、1250を超えるサーバーの古いバージョンをチェックし、270を超えるサーバーでバージョン固有の問題をチェックします。また、複数のインデックスファイルの存在、HTTPサーバーオプションなどのサーバー構成項目をチェックし、インストールされているWebサーバーとソフトウェアの識別を試みます。スキャンアイテムとプラグインは頻繁に更新され、自動的に更新できます。

概要①の要約

要約すると、Niktoは一言で言うとWebサーバースキャナー。 オープンソース(GPL)ライセンスで作成されているOSS。 6700を超える危険なファイル、プログラムなどのアイテムを知っており、それらの知見をもとにWebサーバーに対して、それらの危険なファイル、プログラムが存在しないかの包括的なテストを実行することができます。

6700というのは、危険なファイルの定義は定かではないが、他のものとして多種多様なWebサーバー(Apache, Nginx)、プログラム言語(PHPJava・・・)、Webフレームワーク(lalavel, Struts)、CMS(WordPressDrupal)などのデフォルトの公開設定しているファイルが公開状態になっていないかの検査パターンが用意されているという認識でいいだろう。

概要②(英語)

Not every check is a security problem. There are some items that are "info only" type checks that look for things that may not have a security flaw, but Pentester may not know are present on the server. These items are usually marked appropriately in the information printed. There are also some checks for unknown items which have been seen scanned for in log files. Not every check is a security problem. There are some items that are "info only" type checks that look for things that may not have a security flaw, but Pentester may not know are present on the server. These items are usually marked appropriately in the information printed. There are also some checks for unknown items which have been seen scanned for in log files.

概要②(基本はGoogle翻訳))

Niktoはステルスツールとして設計されていません。可能な限り迅速にWebサーバーをテストし、ログファイルまたはIPS / IDSで明らかになります。ただし、試してみたい(またはIDSシステムをテストしたい)場合に備えて、LibWhiskerのアンチIDSメソッドがサポートされています。 すべてのチェックがセキュリティの問題であるとは限りません。セキュリティ上の欠陥がない可能性のあるものを探す「情報のみ」のタイプのチェックである項目がいくつかありますが、Pentesterはサーバーに存在することを知らない可能性があります。これらのアイテムは通常、印刷される情報に適切にマークされています。ログファイルでスキャンされた不明なアイテムのチェックもいくつかあります。

概要②の要約

Niktoはステルスツールではない。なので、サーバーを監視している人に気づかれないように扱うようなツールではない。以前、SOC研修を受けた時、SIEMでサーバーへのリクエスト量を計測していたら、急激にリクエスト数が跳ね上がって監視役の人があたふたしたのを思い出した。確か、サーバーへ大量アクセスするときの手法としてNiktoを使ったって講師の人言っていたなぁ。。。 その時はスモークスクリーンとして使用されていて、Niktoのアクセスで監視役があたふたしているときに、本当にやりたい攻撃(XSSやSQLi)などを入れ込んでいた記憶。

Niktoのゴール(英語)

The goal of the project is to examine a web server to find potential problems and security vulnerabilities, including: ・Server and software misconfigurations ・Default files and programs ・Insecure files and programs ・Outdated servers and programs ・Pointers to lead a human tester to better manual testing Nikto is built on LibWhisker2 (by Rain Forest Puppy) and can run on any platform which has a Perl environment. It supports SSL, proxies, host authentication, attack encoding and more.

Niktoのゴール(基本はGoogle翻訳))

Niktoの目標は、Webサーバーを調べて、次のような潜在的な問題とセキュリティの脆弱性を見つけることです。 ・サーバーとソフトウェアの構成ミス ・デフォルトのファイルとプログラム ・安全でないファイルとプログラム ・古いサーバーとプログラム 人間のテスターをより良い手動テストに導くためのポインター Niktoは(Rain Forest Puppyによる)LibWhisker2上に構築されており、Perl環境を持つ任意のプラットフォームで実行できます。 SSL、プロキシ、ホスト認証、攻撃エンコーディングなどをサポートします。

Niktoのゴールの要約

引用形式では箇条書きが崩れてしまっているが、箇条書きの部分は下記。

  • サーバーとソフトウェアの構成ミス
  • デフォルトのファイルとプログラム
  • 安全でないファイルとプログラム
  • 古いサーバーとプログラム

人間のミスによる意図せず公開してしまっているファイルやディレクトリを見つけることや、安全ではないプログラムなどを見つけることができる。また、Perl環境で動いているので、Niktoを動かす際にPerl環境が必要な場合が存在する。

基本的なオプション

   Options:
       -ask+               Whether to ask about submitting updates
                               yes   Ask about each (default)
                               no    Don't ask, don't send
                               auto  Don't ask, just send
       -Cgidirs+           Scan these CGI dirs: "none", "all", or values like "/cgi/ /cgi-a/"
       -config+            Use this config file
       -Display+           Turn on/off display outputs:
                               1     Show redirects
                               2     Show cookies received
                               3     Show all 200/OK responses
                               4     Show URLs which require authentication
                               D     Debug output
                               E     Display all HTTP errors
                               P     Print progress to STDOUT
                               S     Scrub output of IPs and hostnames
                               V     Verbose output
       -dbcheck           Check database and other key files for syntax errors
       -evasion+          Encoding technique:
                               1     Random URI encoding (non-UTF8)
                               2     Directory self-reference (/./)
                               3     Premature URL ending
                               4     Prepend long random string
                               5     Fake parameter
                               6     TAB as request spacer
                               7     Change the case of the URL
                               8     Use Windows directory separator (\)
                               A     Use a carriage return (0x0d) as a request spacer
                               B     Use binary value 0x0b as a request spacer
        -Format+           Save file (-o) format:
                               csv   Comma-separated-value
                               htm   HTML Format
                               msf+  Log to Metasploit
                               nbe   Nessus NBE format
                               txt   Plain text
                               xml   XML Format
                               (if not specified the format will be taken from the file extension passed to -output)
       -Help              Extended help information
       -host+             Target host
       -IgnoreCode        Ignore Codes--treat as negative responses
       -id+               Host authentication to use, format is id:pass or id:pass:realm
       -key+              Client certificate key file
       -list-plugins      List all available plugins, perform no testing
       -maxtime+          Maximum testing time per host
       -mutate+           Guess additional file names:
                               1     Test all files with all root directories
                               2     Guess for password file names
                               3     Enumerate user names via Apache (/~user type requests)
                               4     Enumerate user names via cgiwrap (/cgi-bin/cgiwrap/~user type requests)
                               5     Attempt to brute force sub-domain names, assume that the host name is the parent domain
                               6     Attempt to guess directory names from the supplied dictionary file
       -mutate-options    Provide information for mutates
       -nointeractive     Disables interactive features
       -nolookup          Disables DNS lookups
       -nossl             Disables the use of SSL
       -no404             Disables nikto attempting to guess a 404 page
       -output+           Write output to this file ('.' for auto-name)
       -Pause+            Pause between tests (seconds, integer or float)
       -Plugins+          List of plugins to run (default: ALL)
       -port+             Port to use (default 80)
       -RSAcert+          Client certificate file
       -root+             Prepend root value to all requests, format is /directory
       -Save              Save positive responses to this directory ('.' for auto-name)
       -ssl               Force ssl mode on port
       -Tuning+           Scan tuning:
                               1     Interesting File / Seen in logs
                               2     Misconfiguration / Default File
                               3     Information Disclosure
                               4     Injection (XSS/Script/HTML)
                               5     Remote File Retrieval - Inside Web Root
                               6     Denial of Service
                               7     Remote File Retrieval - Server Wide
                               8     Command Execution / Remote Shell
                               9     SQL Injection
                               0     File Upload
                               a     Authentication Bypass
                               b     Software Identification
                               c     Remote Source Inclusion
                               x     Reverse Tuning Options (i.e., include all except specified)
       -timeout+          Timeout for requests (default 10 seconds)
       -Userdbs           Load only user databases, not the standard databases
                               all   Disable standard dbs and load only user dbs
                               tests Disable only db_tests and load udb_tests
       -until             Run until the specified time or duration
       -update            Update databases and plugins from CIRT.net
       -useproxy          Use the proxy defined in nikto.conf
       -Version           Print plugin and database versions
       -vhost+            Virtual host (for Host header)
              + requires a value

実際にやってみる

コマンド

$ perl nikto.pl -h 127.0.0.1 -p 8000 -nolookup -Format txt -o 

なんかPerlが既に入っていたのでPerlコマンドで実行。

ローカルにDjangoで簡易的なWebアプリを作成し試行しました。なので、portも通常のhttp/https通信で使われる80や443ではなく、Webアプリを立ち上げているportを指定。フォーマットとしては、txt形式で出力されるように指定しました。

実行結果(ターミナル)

$ perl nikto.pl -h 127.0.0.1 -p 8000 -nolookup -Format txt -o test_2021_0717_valtan_text.txt
- ***** SSL support not available (see docs for SSL install) *****
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          127.0.0.1
+ Target Hostname:    127.0.0.1
+ Target Port:        8000
---------------------------------------------------------------------------
+ SSL Info:        Subject:  
                   Ciphers:  
                   Issuer:   
+ Start Time:         2021-07-18 15:50:01 (GMT9)
---------------------------------------------------------------------------
+ Server: WSGIServer/0.2 CPython/3.7.2
+ The site uses SSL and the Strict-Transport-Security HTTP header is not defined.
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Hostname '127.0.0.1' does not match certificate's names: 
+ OSVDB-17113: /SilverStream: SilverStream allows directory listing
+ ///etc/hosts: The server install allows reading of any system file by adding an extra '/' to the URL.
+ Server banner changed from 'WSGIServer/0.2 CPython/3.7.2' to 'WSGIServer/0.2 Python/3.7.2'
+ /wp-content/themes/twentyeleven/images/headers/server.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /wordpress/wp-content/themes/twentyeleven/images/headers/server.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /wp-includes/Requests/Utility/content-post.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /wordpress/wp-includes/Requests/Utility/content-post.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /wp-includes/js/tinymce/themes/modern/Meuhy.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /wordpress/wp-includes/js/tinymce/themes/modern/Meuhy.php?filesrc=/etc/hosts: A PHP backdoor file manager was found.
+ /assets/mobirise/css/meta.php?filesrc=: A PHP backdoor file manager was found.
+ /login.cgi?cli=aa%20aa%27cat%20/etc/hosts: Some D-Link router remote command execution.
+ /shell?cat+/etc/hosts: A backdoor was identified.
+ 8078 requests: 0 error(s) and 14 item(s) reported on remote host
+ End Time:           2021-07-18 15:51:13 (GMT9) (72 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

フィイルとしてアウトプットされたのも上記と同じ内容が出力されていた。

内容を見てみると、ヘッダーにStrict-Transport-Securityがついていないとの指摘が。ローカルの開発なので勘弁・・・

気になるのが、Djangoのプロジェクトなのにwordpressに関連するwp-includeswordpressなどが検知されていること。ローカル環境、変な汚れ方しているのかな・・・わからん。

【おまけ】実行結果(サーバー側)

当たり前のようだが、Niktoから大量のリクエストを受け付けていることを確認。

一部抜粋。nginxのファイルや、Docker、GitHubのReadmeなど考えられるありとあらゆる公開されていると思われるファイルへのリクエストを投げて404が返ってくる(ファイルが存在しない)ことを確認している。

[18/Jul/2021 06:51:13] "GET /nginx_status HTTP/1.1" 404 2089
Not Found: /Dockerfile
[18/Jul/2021 06:51:13] "GET /Dockerfile HTTP/1.1" 404 2083
Not Found: /cdn-cgi/trace
[18/Jul/2021 06:51:13] "GET /cdn-cgi/trace HTTP/1.1" 404 2092
Not Found: /v1/tasks
[18/Jul/2021 06:51:13] "GET /v1/tasks HTTP/1.1" 404 2077
Not Found: /v2/tasks
[18/Jul/2021 06:51:13] "GET /v2/tasks HTTP/1.1" 404 2077
Not Found: /v3/tasks
[18/Jul/2021 06:51:13] "GET /v3/tasks HTTP/1.1" 404 2077
Not Found: /v4/tasks
[18/Jul/2021 06:51:13] "GET /v4/tasks HTTP/1.1" 404 2077
Not Found: /.dockerignore
[18/Jul/2021 06:51:13] "GET /.dockerignore HTTP/1.1" 404 2092
Not Found: /tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp
[18/Jul/2021 06:51:13] "GET /tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/hosts HTTP/1.1" 404 2237
Not Found: /
[18/Jul/2021 06:51:13] "GET / HTTP/1.1" 404 2035
Not Found: /var/
[18/Jul/2021 06:51:13] "GET /var/ HTTP/1.1" 404 2065
Not Found: /var/log/
[18/Jul/2021 06:51:13] "GET /var/log/ HTTP/1.1" 404 2077
Not Found: /etc/
[18/Jul/2021 06:51:13] "GET /etc/ HTTP/1.1" 404 2065
Not Found: /.ftpconfig
[18/Jul/2021 06:51:13] "GET /.ftpconfig HTTP/1.1" 404 2083
Not Found: /.remote-sync.json
[18/Jul/2021 06:51:13] "GET /.remote-sync.json HTTP/1.1" 404 2104
Not Found: /.vscode/ftp-sync.json
[18/Jul/2021 06:51:13] "GET /.vscode/ftp-sync.json HTTP/1.1" 404 2116
Not Found: /.vscode/sftp.json
[18/Jul/2021 06:51:13] "GET /.vscode/sftp.json HTTP/1.1" 404 2104
Not Found: /deployment-config.json
[18/Jul/2021 06:51:13] "GET /deployment-config.json HTTP/1.1" 404 2119
Not Found: /ftpsync.settings
[18/Jul/2021 06:51:13] "GET /ftpsync.settings HTTP/1.1" 404 2101
Not Found: /sftp-config.json
[18/Jul/2021 06:51:13] "GET /sftp-config.json HTTP/1.1" 404 2101

まとめ

  • NiktoはWebサーバーのスキャナー
  • 大量の多種多様な検査パターンのリクエストを投げて、意図しないファイルが公開されていないかを確認できる
  • 案外使ってみて楽だった

ドキュメントが公式で非常によくまとまっており、とても分かりやすかった。これを経たとして、Niktoマスターになったわけではないが、もう少し業務で扱えるようになりたいなぁ・・・

見上げるほどに高い意識を持ってい た20代前半とダメな自分を認めることにしたアラサー

最近色々なことを頑張れなくなったなと感じています。 なんでなんだろうなぁ。。。うーん。 ポツリポツリ、ダラダラと書いていこうと思います。

見上げるほどに高い意識を持っていた20代前半

社会人になりたての頃は見上げるほどに高い意識を持っていたなと振り返る。

まず説明しなければいけないことがあって、筆者は学生時代から朝型だった。 大学まで1時間30分かかっていたが、8時40分から開始する大学の一時限の講義に寝坊などを理由として一度も遅刻をしたことはない。環境サークル!国際サークル!特撮サークル!教職!など様々なことにエネルギーを費やしていた。 必要な大学の単位を取り終わった後は、バイトも朝を選択しており毎朝5:00に起きて7:00-10:00にアルバイト。アルバイト終わりに大学の講義や研究に参加する日々を送っていた。要は典型的な朝型だったのだ。

社会人になってからも、朝方の体質は功を成した。 新卒時に入社した企業の始業時間は当時8:30だったので、5:30、6:00に起きると始業まで単純に暇だった。 電車遅延による遅刻を回避するという目的もあり、朝の7:30には会社近くのマックに入りビジネス本や技術本を読む、資格取得を目標に勉強をするという日々を過ごす。 そんな折、たまたま帰宅する時間が一緒で、駅まで一緒に帰った当時の副部長のアドバイスで会社のオフィスに朝7:30に入り勉強することが許可された。これは非常に嬉しいことで毎朝席代として払っていた100円のマックコーヒーを買う必要がなくなった。

また、朝だけでない。 次第にランチの必要性を感じなくなり、昼休みも昼飯を食べる癒しの時間ではなく、自己研鑽のための勉強をする時間になった。朝と昼だけに飽きたらず業務終了後も勉強時間になった。経緯は省くが、入社1年目の筆者は当時の部長により残業が禁止されていた。業務後の時間もプライベートの時間として勉強に充てることができた。非常に幸運だった。

これらの行動原理は、劣等感からくるものだったと振り返る。 学生時代筆者は何もない空っぽの学生だった。 よく言えば、周りから言われたことをスポンジのように吸収する素直さを持ち合わせていたが、全てにおいて他人譲りのものであり、筆者自身の考えは皆無に近い。唯一持っていた筆者自身の考え(個性)は真面目な部分のみであったと振り返る。 大学入学時に受験勉強などをせず、これといった知識を持ち合わせていない。そんな虚空だったかもしれない学生生活を送っていたからなのか、社会人になって周りを見渡して全てに劣っていたと感じ激しい劣等感を感じざる終えなかった。 情報科出身であるにもかかわらず技術的な知識は皆無であり、圧倒的に周りに遅れをとって非常に焦りを感じたのを覚えている。 そんなこんなで20代前半の自身のストイックさは、劣等感からくるものに他ならない。

はぁ・・・20代前半はただただ見上げるほどに高かった自身の意識の高さに自分自身戦々恐々している。。。

頑張れなくなった自分。劣等感から好奇心への変換が迫られる30代。

最近、自己研鑽をする心のガソリンがなくなっていると感じる。 自身を前に進めるためのエネルギーであるガソリンが足りず、ガス欠になり、アクセルをいくら踏んでも前に進まない状況である。

朝早く起きてもひたすらSNS閲覧やネットサーフィン、スマートフォンゲームで時間が過ぎていく日がたまにある。スマートフォンは害悪だなぁと、スマートフォンを一方的に悪者にしている自分がいることに驚愕する。

そんなことを気にしている折、 Twitter で興味深いツイートを発見した。

上記ツイートを発見した時、確かに20代は劣等感をエネルギーとして、自己研鑽に励んでいたと振り返った。非常に納得し、首がちぎれるかと思うくらい首を縦に振った。 なにせ"何も"なかったのだから。周りに対する劣等感しかなかった。当時は自身でも気づかずに劣等感をバネにひたすら自己研鑽を行っていた。新卒の会社から去った後も、同年代のエンジニアに負けないために、唯一の個性を発揮できるエンジニアになるために、ひたすら自分自身が抱いた劣等感と戦っていた。

何と闘っているかわからず、何と争っているかわからず、ただただ頑張り続けていた。 しかし前述した通り劣等感からくる勉強意欲には歳をとるに経て限界を迎えたのだと自覚した。 理由は劣等感がある程度満たされた体と考えている。 流石に8年も経つと、学歴による偏見で足切りされる機会が少なくなり、技術力も身についたことから一定の評価を周りから得られるようになった。

30代は好奇心をバネにできる人が強い。 同じような好奇心がバネになるという言葉を筆者は、色々な媒体で聞いたことがあるので好奇心がエネルギーになるのは正しいと考える。

じゃあ現在、30代を迎えようとしている筆者が好奇心をバネに20代前半ほどのストイックな自己研鑽をできているかどうか問われると答えは、否だ。 間違いなく、20代前半と比べて自己研鑽する時間は減っており、熱意も減っている。 頑張らなければ自己研鑽をする方向に心が動かない。 好奇心からくる行動原理で「頑張る」という単語は登場しないはずだ。 頑張るという言葉を発するたびに自身の社会人やエンジニアとしての未熟さを痛感する。

とはいえ、好奇心なんて”頑張ること”で、芽生えてくるものではない。 また、今別にIT技術に対して興味が全くないわけではない。 全く興味がなければ、転職を繰り返し、新しい環境に身を置くなどの決断はしない。 今はただ好奇心のエネルギーが溜まりきっていないだけ。 自身の心の思うままに生きることを心掛けることで、自分自身の欲求に素直になり、即行動、実現できる。 また、好奇心が芽生えた時に素早く行動に移せるように、今はダラダラとアンテナの範囲を広げて、行動をする準備をしようと考えている。

ダメな自分を認めることを覚えたアラサー

コロナ前。コロナが流行しだす2020年の1月や2月頃のお話。学生時代に非常にお世話になった先輩とお酒を飲む機会があった。 大学時代に所属していたサークルの先輩だ。筆者が1年生の時の4年生。 後輩と接する態度や学生時代のサークル人としてのマナーなど沢山の大切なことをその背中から教わった。

久々にお会いした先輩にかけてもらえた言葉として「ダメな自分を認められるようにした方がいいよ」と言ってもらえた。正直、この言葉には非常に救われている。 筆者は根本的には真面目である。真面目が祟り、自身が決めた目標を達成できない時は非常に落ち込んでしまう自身がいることもうなづける。

だからこそ休日に10:00まで寝て貴重な休日の時間を無駄にした自分を認めることにした。 毎朝6:00 からのオンライン英会話に寝坊したとしても、一切自分自身を責めず「しっかりと休養をとれた。日中の業務では力を発揮できる」とプラスの方向に持っていくことができるようになった。(講師の方には非常に申し訳ないと思っています。ごめんなさい。) 毎朝行っているジムも、雨が降ったからといってサボっても良いことにした。 ただただお酒と美味しいものを食べて一方的に腹囲の数値が増えることも許容している。社会人としての成長が鈍化しているにも関わらず、お腹周りだけが日々急速に成長していくことは悩みのタネでしかない。

そんなダメダメな自分も1人の自分として認めることにしよう。 好奇心を持てずに勉強に思うように励むことができない自分がいることも認めることにする。 何も焦る必要はない。なんならこれ以上頑張る必要はないのかもしれない。それでもおそらく筆者は頑張ると思う。 人生100年時代。まだまだ先は長い。ただひたすらに長い人生に絶望しつつ、これからもダメな自分を認めつつただひたすらに歩んでいこうと思う。

【備忘録】GitHubのDjangoプロジェクトにCIを設定した話

どーも。ばぁどです。 CI 最高ーーー。

ということで、GitHubDjango プロジェクトにCIを設定した話を備忘録として残そうと思います。

GitHub Actions

詳細は下記の"さくらのナレッジ"さんの記事がわかりやすかったです。

knowledge.sakura.ad.jp

触った感じだと、クリックだけでCIを設定することができ非常に簡易的にCIの設定をすることができました。 Actionsタブの中にはGitHub側で一定のテンプレートは用意してくれており、Djangoのテンプレートを選ぶことで基本的な初期設定が導入されていました。

f:id:UltraBirdTech:20210704154248p:plain

生成された設定ファイル

上記フローでクリックした後、 ./github/workflows/django.yml として設定ファイルが作成されます。

name: Django CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.7, 3.8, 3.9]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install Dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run Tests
      run: |
        python manage.py test

ポイントとして、Djangoのテスト実行コマンドであるpython manage.py testが最初から書かれていることとか感動したポイントです。 Djangoプロジェクトだと言えば、ユニットテストの実行コマンドも最初から設定ファイルに記述しておいてくれるって、すごい便利になりましたね。

実行結果

PullRequestの作成時にCIが起動することを確認

masterブランチへのPullRequestの作成時に、CIで設定したいくつかの動作が起動することを確認しました。 f:id:UltraBirdTech:20210704154635p:plain

ユニットテストが実行されていることを確認

python manage.py testが実行されたことも確認しました。 f:id:UltraBirdTech:20210704154702p:plain

まとめ

コロナ前にGitHubだけでCI/CDができるようになっていたのが驚きでした。 CircleCIなどを活用しないとできない認識だったのですが、時代は前に進むんですね・・・

今後はカバレッジの確認やSASTなどもCIに追加していこうと考えています。

PythonのSASTツール、banditを触ってみる

今回はSASTを触ってみました。

SAST(Static Application Security Test) とは

静的にプログラムのセキュリティをチェックしてくれるツールです。 コマンドで実行することで、ソースコードがセキュアかどうかの確認をしてくれます。

CI/CDに組み込みやすく、PullRequestの作成時など特定のタイミングでユニットテストと同じように実行することが可能。エンジニアのミスでセキュアではない設定が組み込まれることを防ぐことができます。

bandit

github.com

Bandit is a tool designed to find common security issues in Python code. To do this Bandit processes each file, builds an AST from it, and runs appropriate plugins against the AST nodes. Once Bandit has finished scanning all the files it generates a report. Bandit was originally developed within the OpenStack Security Project and later rehomed to PyCQA.

日本語訳(Google翻訳)

Banditは、Pythonコードの一般的なセキュリティ問題を見つけるために設計されたツールです。 これを行うために、Banditは各ファイルを処理し、そこからASTを構築し、ASTノードに対して適切なプラグインを実行します。 Banditがすべてのファイルのスキャンを終了すると、レポートが生成されます。 Banditは元々OpenStackSecurity Project内で開発され、後にPyCQAにリホームされました。

インストール

pip install bandit

実行結果

試しに手元にあったvaltanという、Djangoプロジェクトに対してかけてみました。

実行コマンド

$ bandit -r ./

実行結果

実行した結果は下記です。 一つ一つの指摘事項に対して、リンクが貼られておりとてもみやすい。 基本的には設定ファイルへの指摘が多いですが、エンジニアが忘れてしまいそうな初期設定のままリリースというのを止めてくれそうなツールですね。

[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.7.2
Run started:2021-07-04 04:42:05.329107

Test results:
>> Issue: [B105:hardcoded_password_string] Possible hardcoded password: '★★★★★★★★★'
   Severity: Low   Confidence: Medium
   Location: ./django_manage/settings.py:23
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html
22  # SECURITY WARNING: keep the secret key used in production secret!
23  SECRET_KEY = '★★★★★★★★★'
24  
25  # SECURITY WARNING: don't run with debug turned on in production!
26  DEBUG = True

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
   Severity: Low   Confidence: High
   Location: ./valtan/models/server.py:5
   More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
4   import threading
5   import subprocess
6   
7   
8   class Server():

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
   Severity: Low   Confidence: High
   Location: ./valtan/tool/client.py:6
   More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
5   import threading
6   import subprocess
7   
8   # global verbs
9   listen = False

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   Location: ./valtan/tool/client.py:46
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html
45          else:
46              assert False, "Unhandled Option"
47  

--------------------------------------------------
>> Issue: [B104:hardcoded_bind_all_interfaces] Possible binding to all interfaces.
   Severity: Medium   Confidence: Medium
   Location: ./valtan/tool/client.py:56
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b104_hardcoded_bind_all_interfaces.html
55      if not len(target):
56          target = "0.0.0.0"
57  

--------------------------------------------------
>> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue.
   Severity: High   Confidence: High
   Location: ./valtan/tool/client.py:76
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html
75          output = subprocess.check_output(
76                  command, stderr=subprocess.STDOUT, shell=True)
77      except:
78          output = "Failed To excute command.\r\n"

--------------------------------------------------
>> Issue: [B310:blacklist] Audit url open for permitted schemes. Allowing use of file:/ or custom schemes is often unexpected.
   Severity: Medium   Confidence: High
   Location: ./valtan/views/check_vulnerability.py:25
   More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b310-urllib-urlopen
24          try:
25              with urllib.request.urlopen(check_request) as response:
26                  header_info = response.info()

--------------------------------------------------

Code scanned:
    Total lines of code: 519
    Total lines skipped (#nosec): 0

Run metrics:
    Total issues (by severity):
        Undefined: 0.0
        Low: 4.0
        Medium: 2.0
        High: 1.0
    Total issues (by confidence):
        Undefined: 0.0
        Low: 0.0
        Medium: 2.0
        High: 5.0
Files skipped (0):

Djangoの設定で x-xss-protection の設定を追加する

どーも。ばぁどです。

最近諸事情ありまして、より専門的なサイバーセキュリティの知見を得ております。 今回はXSSの対策の一つである X-XSS-ProtectionDjango に付与する方法です。

XSS とは?

開発者が意図しないJavaScriptが攻撃者により実行されてしまうWebアプリケーションの脆弱性です。

詳細は下記の筆者の過去記事を参考してください。

ultrabirdtech.hatenablog.com

そもそもDjangoXSS対策は?

基本はHTMLへのアウトプット時に Django の機能によりエスケープ処理がされています。Djangoが持っているHTMLテンプレートのルール通りに記述すれば大丈夫です。

詳しくは下記を参考にしてください。

ultrabirdtech.hatenablog.com

しかし、上記のDjangoの設定に加えて、サーバー側でx-xss-protectionの設定を付与しておくことで、さらにXSSに対して堅牢なシステムを構築することが可能です。

X-XSS-PROTECTION とは

サーバー側で設定することで、ブラウザ側で実行されそうな XSSスクリプトを無効化するような設定です。近年は対応していないブラウザもあるようなのですが、過去のブラウザを使用していることを考慮するならば、今でも付けておく必要がありそうです。

developer.mozilla.org

HTTP の X-XSS-Protection レスポンスヘッダーは Internet Explorer, Chrome, Safari の機能で、反射型クロスサイトスクリプティング (XSS) 攻撃を検出したときに、ページの読み込みを停止するためのものです。強い Content-Security-Policy をサイトが実装して、インライン JavaScript の使用を無効にしていれば ('unsafe-inline')、現在のブラウザーではこれらの防御は大枠で不要なものですが、まだ CSP に対応していない古いウェブブラウザーを使用しているユーザーには防御になります。

現在のブラウザーではこれらの防御は大枠で不要なものです

docs.djangoproject.com

英語

X-XSS-Protection: 1; mode=block Some browsers have the ability to block content that appears to be an XSS attack. They work by looking for JavaScript content in the GET or POST parameters of a page. If the JavaScript is replayed in the server’s response, the page is blocked from rendering and an error page is shown instead.

The X-XSS-Protection header is used to control the operation of the XSS filter.

To enable the XSS filter in the browser, and force it to always block suspected XSS attacks, you can pass the X-XSS-Protection: 1; mode=block header. SecurityMiddleware will do this for all responses if the SECURE_BROWSER_XSS_FILTER setting is True.

日本語訳

X-XSS-Protection: 1; mode=block 一部のブラウザにはXSS攻撃のように見えるコンテンツをブロックする機能があります。 これらはページのGETまたはPOSTパラメーターでJavaScriptコンテンツを探すことによって機能します。 サーバーの応答でJavaScriptが実行されると、ページのレンダリングがブロックされ、代わりにエラーページが表示されます。

X-XSS-Protectionヘッダーは、XSSフィルターの動作を制御するために使用されます。

ブラウザでXSSフィルタを有効にし、疑わしいXSS攻撃を常にブロックするように強制するには、X-XSS-Protection: 1; mode=block header を渡すことがで制御できます。 SecurityMiddlewareは、SECURE_BROWSER_XSS_FILTER設定がTrueの場合、すべての応答に対してこれを実行します。

SECURE_BROSER_XSS_FILTER

Djangoの全てのヘッダーに X-XSS-Protectionの設定を付与するためには、Djangoの設定ファイルに SECURE_BROWSER_XSS_FILTER の設定を TRUE にする必要があります。デフォルトでは False です。

docs.djangoproject.com

英語

Default: False

If True, the SecurityMiddleware sets the X-XSS-Protection: 1; mode=block header on all responses that do not already have it.

Modern browsers don’t honor X-XSS-Protection HTTP header anymore. Although the setting offers little practical benefit, you may still want to set the header if you support older browsers.

日本語訳

Default: False

Trueの場合、SecurityMiddlewareはX-XSS-Protection: 1; mode=blockを設定します。特別に設定されていないヘッダーにはすべてX-XSS-Protectionの設定がつきます。。

最近のブラウザは、X-XSS-Protection HTTPヘッダーを採用しなくなりました。 この設定は実用的なメリットはほとんどありませんが、古いブラウザをサポートしている場合は、ヘッダーを設定することをお勧めします。

実際につけてみる

django_manage/settings.py

~~~~~~~~~~~~~省略~~~~~~~~~~~~~

SECURE_BROWSER_XSS_FILTER = True

X-XSS-Protection を付与していない場合

Date: Wed, 19 May 2021 23:20:21 GMT
Server: WSGIServer/0.2 CPython/3.7.2
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Vary: Cookie
Content-Length: 15201
Set-Cookie: csrftoken=q6m56So4ENuJlxEJAtMJF64s8OoKyghbJBQh0odp7qAKK53OukP9tbZyM6Cprote; expires=Wed, 18-May-2022 23:23:18 GMT; Max-Age=31449600; Path=/

X-XSS-Protection を付与した場合

Date: Wed, 19 May 2021 23:23:18 GMT
Server: WSGIServer/0.2 CPython/3.7.2
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Vary: Cookie
Content-Length: 15201
x-xss-protection: 1; mode=block
Set-Cookie: csrftoken=q6m56So4ENuJlxEJAtMJF64s8OoKyghbJBQh0odp7qAKK53OukP9tbZyM6Cprote; expires=Wed, 18-May-2022 23:23:18 GMT; Max-Age=31449600; Path=/

Content-Length の後ろに x-xss-protection が付与されていることがわかりますね。

まとめ

  • X-XSS-Protection はフレームワークなどに関わらずできるXSS対策
  • 近年は対応していないブラウザもあるが、古いブラウザのサポートを含めるとサーバー側で設定した方が良い?

IFTTTから脱却してスマートホームを復活させたお話し

IFTTTにいつの間にかProアカウントができていた

数年前 GoogleHome が発売されたタイミングで、NatureRemo を購入し家をスマート化しました。

時が経ち約半年前に NatureRemo の Wi-Fi 設定ができず、しばらくスマート化を諦めていました。

数ヶ月が経ち、久々に NatureRemo の設定をしたところ、すんなりと NatureRemo の Wi-Fi の再設定はうまくいったのですが、数ヶ月前まで動いていたIFTTTを経由した操作のいくつかが動かないという問題がありました。

IFTTT の設定を無効化していたかな?と考えたのですが、IFTTT の有料アカウントができており、特定の操作や、一定数以上の処理は有料アカウントでないとできない仕様に変更されていました。

f:id:UltraBirdTech:20210510192056p:plain

IFTTTで担っていたこと

  • GoogleHome と NatureRemo の連携
    • GoogleHome から特定の単語による音声入力をIFTTTを経由してNatureRemo に送っていました。
  • 毎日特定のタイミングで NatureRemo に指示を出す
    • 毎朝6:00に電気をつけるなどの特定のタイミングの処理を IFTTT に任せていました

IFTTTが有料化されてしまったことにより、上記の処理が全て非アクティブ化されており使えない状況になっていました。

GoogleHomeとNatureRemoの連携

GoogleHome アプリにNatureRemoアプリとの連携機能が搭載されました。

GoogleHome アプリが独自に音声入力を解釈し、NatureRemo に指示を出してくれるようになっています。 IFTTT の時は特定のワードをトリガーにしていたのですが、GoogleHome と連携することで「それっぽい言葉」をGoogleHomeが解釈して NatureRemo に指示を出してくれるようになりました。

■Natureremo公式ドキュメントは下記

support.nature.global

他の方の記事によると GoogleHome の解釈によって左右されるので、使いづらさもあると事ですが「 TV をつけて」や「4チャンネルに変えて」と言うだけでよしなに解釈してくれるのはとても便利です。

NatureRemoアプリから設定可能になっていた

NatureRemo アプリから設定可能になっていました。(そうそうこの機能が欲しかったのよ)

f:id:UltraBirdTech:20210510194516p:plain

まとめ

文明の力(スマートホーム化)って素晴らしい

徳丸本第2版で脆弱性診断入門を勉強していたら、ユーザーの新規登録時に「只今サイトが大変混雑しています。もうしばらく立ってからアクセスしてください」と表示されてしまったお話

どーも、ばぁどです。

数年ぶりに徳丸本で勉強させていただいております。 2019年の年末に同僚に徳丸本を貸して、早2年久々に手元に戻ってきたので勉強し直しております。

XSSとはなんでしょう?や、CSRF脆弱性などは一通り把握している(つもり)ので、7章の脆弱性診断入門で脆弱性診断を行っていた時のお話です。

本の7章に脆弱性診断入門という章があり、今の僕にめちゃくちゃありがたい!!!と思って本を読みながら作業を進めていたのですが、ユーザー登録時に「只今サイトが大変混雑しています。もうしばらく立ってからアクセスしてください」と表示されてしまったので、それを解消した時のお話です。

エラーでユーザー登録ができない

脆弱性診断入門では脆弱性診断を行うためのTODOアプリが同梱されています。

ユーザー登録入力画面

f:id:UltraBirdTech:20210418182033p:plain
ユーザー登録入力画面

ユーザー情報の入力画面です。 入力する内容は、ユーザー名、パスワード、メールアドレス、アイコンの画像です。

ユーザー登録確認画面

f:id:UltraBirdTech:20210418182058p:plain
ユーザー登録登録画面

ユーザー登録入力画面で、入力されたデータを画面上で確認することができる画面です。

出力されたエラー

f:id:UltraBirdTech:20210418182232p:plain
エラー画面

あからさまにサーバー側で何かしらのエラーが出ているようなエラー文が表示されてしまいました。

基本的な入力値に対するバリデーションは入っているようなので、おそらくクライアント側からの入力値エラーではないと踏んでおり、また該当スクリプトは筆者のVM上で動いているスクリプトなので、大量に外部からアクセスが行われサーバーがダウンしている可能性はゼロです。何かしらのエラーがスクリプト側で起きていると判断していました。

只今サイトが大変混雑しています。もうしばらく経ってからアクセスしてください

うーむ。。少し苦手だが、PHPファイルを覗いてみるか・・・・

PHPスクリプトを覗いてみる

筆者はPHPが非常に苦手です。 変数前の$が苦手なのと、行末にある;(セミコロン)がアレルギーです。

とはいえ、アレルギーを起こしていても仕方がないので、PHPを読みます。

URLが下記なので、adduser.php という PHP ファイルが実行されていることは分かっていたので、該当ファイルをVMサーバーから探しました。

http://example.jp/todo/adduser.php

adduser.php ファイルの内容

下記が今回読んでみた adduser.phpです。

1  <?php
2  require_once './common.php';
3  $id    = $_POST["id"];
4  $pwd   = substr($_POST["pwd"], 0, 6);
5  $email = $_POST["email"];
6  $icon  = $_POST["iconfname"];
7
8  try {
9    $dbh = dblogin();
10 
11   $sql = 'INSERT INTO users VALUES(NULL, ?, ?, ?, ?, 0)';
12   $sth = $dbh->prepare($sql);
13   $rs = $sth->execute(array($id, $pwd, $email, $icon));
14 } catch (PDOException $e) {
15  $logger->add('?~B??~B??~C??~A?失?~U~W?~A~W?~A??~A~W?~A~_: ' . $e->getMessage());
16   die('?~O??~J?~B??~B??~C~H?~A~L大?~I混?~[~Q?~A~W?~A??~A~D?~A??~A~Y?~@~B?~B~B?~A~F?~A~W?~A??~B~I?~A~O?~L?~A??~A??~A~K?~B~I?~B??~B??~B??~B??~A~W?~A??~A~O?~A| ?~A~U?~A~D');
17 }
18 ?>
19 <html>
====== 以下省略 ===============
xx </html>

$_POSTからIDやパスワードを受け取っており(3行目~6行目)、その後DB関連の処理(9行目~13行目)がtry-catchで囲われています。 今回はエラーが起きているので、catchしている後の14行目の後にvar_dump()を使って$eの中身を覗いてみます。

====== 省略 ===============
13   $rs = $sth->execute(array($id, $pwd, $email, $icon));
14 } catch (PDOException $e) {
15  var_dump($e);
16  $logger->add('?~B??~B??~C??~A?失?~U~W?~A~W?~A??~A~W?~A~_: ' . $e->getMessage());
17   die('?~O??~J?~B??~B??~C~H?~A~L大?~I混?~[~Q?~A~W?~A??~A~D?~A??~A~Y?~@~B?~B~B?~A~F?~A~W?~A??~B~I?~A~O?~L?~A??~A??~A~K?~B~I?~B??~B??~B??~B??~A~W?~A??~A~O?~A| ?~A~U?~A~D');
18 }
19 ?>
20 <html>
====== 以下省略 ===============
xx </html>

IDにNULLはダメだよ!!というエラーが出てきている

上記、var_dump()の結果が下記でした。

object(PDOException)#5 (8) { ["message":protected]=> string(80) "SQLSTATE[23000]: Integrity constraint violation: 1048 
Column 'id' cannot be null" ["string":"Exception":private]=> string(0) "" ["code":protected]=> string(5) "23000"
 ["file":protected]=> string(30) "/var/www/html/todo/adduser.php" ["line":protected]=> int(13) ["trace":"Exception":private]=> 
array(1) { [0]=> array(6) { ["file"]=> string(30) "/var/www/html/todo/adduser.php" ["line"]=> int(13) ["function"]=> string(7)
 "execute" ["class"]=> string(12) "PDOStatement" ["type"]=> string(2) "->" ["args"]=> array(1) { [0]=> array(4) { [0]=>
 string(4) "test" [1]=> string(4) "test" [2]=> string(15) "test@example.jp" [3]=> string(14) "black_bird.png" } } } }
 ["previous":"Exception":private]=> NULL ["errorInfo"]=> array(3) { [0]=> string(5) "23000" [1]=> int(1048) [2]=> string(26)
 "Column 'id' cannot be null" } } 

一部抜粋

"Column 'id' cannot be null"

なるほど。ID列にNULLを渡しているからエラーになっていのか。

11   $sql = 'INSERT INTO users VALUES(NULL, ?, ?, ?, ?, 0)';
12   $sth = $dbh->prepare($sql);

確かに、11行目のINSERT句を見てみるとVALUESにNULLを渡していそうな匂いがしますね。

試しにNULL以外を明示的に指定してみる

試しにNULLと記述されている部分に明示的に数値を固定で割り当ててみます。

11   $sql = 'INSERT INTO users VALUES(11, ?, ?, ?, ?, 0)';
12   $sth = $dbh->prepare($sql);

実行結果

できた!!!

f:id:UltraBirdTech:20210418183841p:plain

もちろん、ユーザー登録後はTODOアプリケーションにログインをすることもできます。

f:id:UltraBirdTech:20210418184003p:plain

マイページをみてみると、固定で割り当てた11がユーザーのIDとして割り当てられているのも確認できました。

f:id:UltraBirdTech:20210418184124p:plain

※赤枠内のid11になっていることを確認。

http://example.jp/todo/mypage.php?rnd=607bfe5917cd6&id=11

これでユーザーアカウントの登録と登録したユーザーによるログインができました。一安心。

まとめ

久々にPHPを読みました。 var_dump()って今でも使われている関数なんですかね? 5年くらい前に教わったPHPの古いデバック手法を引きずっている気がする。

とはいえ、なんで本事象が起きたのですかね? ソースコードでは明示的にNULLが渡されていたので、解決はできたのですが腑に落ちません。NULLが渡されたら自動的に採番されるような仕組みがうまく動いていないとか??うーん、、、

私のVM特有の事象でしょうか。なんか軽くGoogleで調べたのですが、同じような事象にあった人を確認できず、公式サイトを見ても同じような事象について記述がなかったため、PHPファイルを読みましたが、もし何か私のPHP側の設定忘れとかであれば非常に申し訳ないです。

何はともあれ、明日以降引き続き脆弱性診断入門を進めていこうと思います。