ばぁど・うぉっちんぐ

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

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

見上げるほどに高い意識を持ってい た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側の設定忘れとかであれば非常に申し訳ないです。

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

同一オリジンポリシーとCross-Origin Resource Sharing

どーも。ばぁどです。 5月に備えて徳丸本を復習しています。

久々に同一オリジンポリシーに遭遇したので、個人メモとしてまとめておきます。

同一オリジンポリシー

同一オリジンポリシーとは

同一オリジンポリシーとは、同一のサーバーから取得したリソースのみJavaScript などのクライアント側で動くスクリプトからアクセスすることができるというクライアント側のWebブラウザ上で実行可能という制限です。 なぜ、このような制限があるかというと攻撃者によりhtmlファイルが書き換えられ、攻撃者が用意した悪意のあるJavaScriptをhtmlに読み込ませ攻撃される場合があるためです。

具体的にはJavaScriptから異なるオリジンに存在するリソースのPOSTメソッドを呼ぼうとしたり、異なるオリジンに存在するJavaScriptを取得して実行しようとする場合にエラーとするような挙動をします。 example.jp と example.com は違うドメインなので、example.jp のサーバーから得たJavaScriptなどのリソースが example.com に存在するサーバー対してリクエストを行うと同一オリジンポリシーでアクセスが拒否されます。

同一オリジンポリシーは、外部ファイルを読み込ませて攻撃されないためのWebブラウザ側のデフォルト設定です。

どのように同一オリジンと認識するか

  1. URLのホスト(FQDN)が一致している
  2. スキームが一致している
  3. ポート番号が一致している

上記の三つの要素が同一のものであれば、同一のオリジンとして認識がされます。 オリジンを認識するのは、ブラウザ側の設定です。

参考

developer.mozilla.org

GoogleChromeFirefox などは基本的に上記3つなのですが、IEのみポートを参照しないなど差異があります。 developer.mozilla.org

Cross-Origin Resource Sharing

Cross-Origin Resource Sharing とは

Cross-Origin Resource Sharing(CORS) とは、同一オリジンポリシーでサイト間のデータの扱いに制限がありつつも、そのサイト間データの扱いを許可するための仕組みのことです。

Webページの動的な動きは現在では欠かすことはできません。 動的な動きをWebページに加えることでUIの向上や、機能の拡充を行うことができます。 例えばGoogleMap を代表とするAjax技術です。 グルメサイトにGoogle Map が組み込まれているだけで、容易に現在位置からお店までの距離、道のりを確認しながらお店へとたどり着くことができます。

上記の例のようにJavaScriptで、動的な動きが記述されており、中には同一オリジンポリシーで制約がかけられてしまっているサイト間(サーバー間)のデータのやり取りを行いたいという需要があります。 そこで同一オリジンポリシーがありながらも、サーバー間のデータをやり取りできる仕様としてCross-Origin Resouce Sharing(CORS)が策定されました。

CORSは同一オリジンポリシーを遵守しているアプリケーションと互換を持ちながら、異なるオリジンのデータのやりとりを行うことができるようになります。

Access-Control-Allow-Origin

サーバー側の設定として Access-Controll Allow Origin という項目に許可するドメインを指定します。 HTTPレスポンスヘッダに指定することで XMLHttpRequest などのアクセス許可をすることができます。

まとめ

いったん自分向けのまとめでした。

異なるオリジンに対してのHTTPリクエストによるエラーは、社会人2年目のプログラマ時代にハマった記憶があります。 また、転職した2社目で本内容に関するレポートの作成に携わったこともあります。