ばぁど・うぉっちんぐ

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

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

【備忘録】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社目で本内容に関するレポートの作成に携わったこともあります。

【番外編】1度の就職活動、3度の転職活動を通してお断りさせていただいたヤバイと感じた企業

どーも、ばぁどです。 自称セキュリティに強いWeb屋です。

この度3度目の転職を行いました。 約2ヶ月ほどの転職活動を行い無事に内定をいただきました。

今回は1度の新卒採用時の就職活動、3度の転職活動を通して個人的に「あの企業はヤバかったな」と感じた企業を振り返ろうと思います。

※本記事はフィクションが適度に含まれております

今までの就職活動、転職活動でヤバイなと思った企業

【ヤバめ度5%】履歴書を紙で郵送してくれと言ったIT企業

2年ほど前に選考させていただいた企業様のお話です。

非常に良い印象の会社で採用技術は割と最新(Gitはもちろんのこと、CI/CDによるテストの自動化)だし、アメリカの某シリコン谷の会社とも会議をするなど英語力も活かせそうな会社でした。

ただ、如何せん古すぎました。 一次面接とても良い雰囲気で、一次面接通過の案内もいただいたのですが、 二次面接以降は履歴書を紙で郵送するということに違和感を感じてしまいました。

一次面接はエージェント経由で渡した履歴書のPDFで面接していただけたのに、二次面接は紙を郵送してもらう理由が理解できませんでした。 一次面接時に面接官が手元に持っていた履歴書は誰の履歴書ですか?

一次面接のタイミングで質問として「紙で郵送する理由」を聞いたのですが、納得できる答えをもらえず、求職者という外部との接点を持つ部分でも紙の文化が残っているということは、内部に入ったらもっと古い文化が残っておりストレスを感じるだろうと判断したため、こちらからお祈りさせていただきました。

紙の文化が残りすぎているところで業務改善は見込めないですよね。

【ヤバめ度10%】「時間を延長して欲しいですか?」という魔の言葉を囁く選考員

筆者が新卒採用の頃のお話です。 選考の過程でグループディスカッションを行っておりました。

グループディスカッションの定石として、話し合うテーマがあり、時間制限があり、時間内にまとめてグループ毎に発表していくと言った選考でした。グループは筆者のグループ含めて5グループあったと記憶しています。

グループディスカッションが時間内に終わるようにタイムテーブルを決めて、まとめに入っていましたが、選考担当者から「もうすぐ制限時間ですが、延長して欲しいグループはありますか?」と言っていただけました。

意見のまとめ、発表の仕方を討論するために時間があった方がいいと判断した筆者が所属していたグループは迷わず手をあげました。筆者のグループ以外にも2、3個のグループが手をあげたのを覚えています。

しかし講評のタイミングで「時間を延長して欲しい?と聞いた時に手をあげたグループは、社会人として時間を守れていないので失格」と言い始めました。 まぁ確かに社会人として時間を守ることは大事ですよね。その点に関しては間違いなく賛成です。 社会人であるならば、決められた時間までに出社するべきですし、締め切り期限を厳守するように仕事は進めていく必要がありますよね。時間を守ることはとても重要です。

しかし自分たちから「時間を延長してほしいグループはありますか?」と聞いておいて素直に手をあげたら、後から「時間を守れないのは社会人として失格」は少々酷くありませんか? 未だにもやもやする選考だったなと強く記憶しております。

ちなみに、その選考は企業側の不手際(選考に必要な書類が揃っておらず、選考中に印刷して学生に配っていた)により、10分ほど後ろに延長して終わりました。 「あれ?社会人として時間を守れないのは失格・・・?あれ?」

【ヤバめ度50%】なぜかキレる、質の低い圧迫面接を行う面接官

新卒で就職活動を行っていた時の企業様です。 当時の就職活動は面接などの先行活動は4月から解禁というスケジュールでした。

1月にグループディスカッションを終え「次回選考は選考解禁となる4月になるという」お話を受けて一度解散したのですが、その後「2月中に面接に進んで欲しい」という連絡が来たので、面接に進んだ時のお話です。

当時、面接を経験していなかったので練習がてら、面接に進む意を伝え意気揚々と面接会場に向かったのですが、面接中に面接担当者様がなぜか機嫌が悪かったです。

面接官:「グループディスカッションで優秀だと思った人物に声をかけたのに、この程度なんだ?」

お話を聞いてみると、1月のグループディスカッションで積極的に発言したり、リーダーシップをとっている学生を中心に4月に先んじて選考を進めてしまおうという魂胆だったそうです。グループディスカッションを経て、よりすぐった学生だったのにもかかわらず面接が思った通りの人材ではなかったらしく、意味もわからず怒られました。

いやー。。。そんなの知らんがな。

【ヤバめ度80%】自社の研修先完備のはずが、実際はSESの現場です

新卒で就職活動をしていた時のお話です。 50名ほどの企業様でSES 事業を中心に行っている企業様でした。

当時、IT系の大学に所属していながらプログラミングなどの専門的なナレッジはなく、社内研修が充実している中小企業or 大手企業を中心に選考を受けていました。

そんな時選考を受けさせていただいた企業様は「入社していただき次第、外部の研修先で学んでもらいます」ということをおっしゃっていて、研修場があるなんていい会社だと考えていました。しかし、よくよく聞いてみると、その研修先というのはSESの常駐先であり、入社1日目から自社での研修は一切なくSES先へ派遣され、技術を学んで成長して欲しいというお話でした。

学生時代であった当時SESという業態の危うさなどを理解できていませんでしたが、何か嫌な予感がしたのと当時第一志望だった企業様(新卒で4年間お世話になった企業)から内定をいただけたタイミングが重なったので、お断りさせていただきました。

これは後日談なのですが、選考途中で知り合い、同会社に入社した知人から、大学を卒業して入社する4月より前の2月や3月からSESの派遣先企業との面接のために時間を拘束されたという話も聞いており、なんか色々とアウトな匂いがする企業様でした。

【ヤバめ度100%】今御社で行っているサービスを弊社に入って立ち上げることができますか?と言ってきた企業

今回の転職活動で面接官からあった質問です。

面接官:「御社で行っているサービスを弊社に入ってから、あなた主導で立ち上げることができますか?」

え?それ逮捕されません? なんか不正競争防止法みたいなやつに引っかかりません?

選考させていただいた企業様は、受託開発及びSESを行っている企業様で、自社サービスの開発に力を入れていく方針を立てていた企業様でした。 ただ、ただ、上記質問に限らず全体的な質問の内容が変だった印象があります。

やたら筆者が所属していた企業で運営しているサービスが売れているか?を聞いてくるんですよね。 なんかしつこすぎて「少し失礼だな」とも思いました。 企業秘密に関わることであり、秘密保持契約にも抵触しそうな内容だったので適度に断りながら、のらりくらりとかわしておりました。

よくよく話を聞いてみると、 受託開発やSESでエンジニアの技術力はある程度あるので、 自社サービスを立ち上げたいが、サービスの案がないとのこと。

開発力がある企業にありがちな思考なのですが、自分たちの開発力でサービスを作れないか?という発想になったようです。 サービスって世の中の課題の解決なので、そもそも世の中を広く見渡して、どう言った課題があるか発見して、それに対する解決策をITのサービスとして提供していかなければいけないのに、そもそものサービスの考え方としてズレていたのかなと今振り返ると思います。 技術はあくまで手段であって、サービスを作る動機ではないですよね。

1番ヤバイと思うポイントが自社内でサービスの案が出てこないからと言って、 転職活動中のエンジニアがもともと運営していたサービスを、自社のサービスにできないか?と考えるのは凄い発想だなと思いました。

いや、もしかしたらそう言った意図はなく、 自発的にサービス運営に参画できるエンジニアなのかどうかを見極めたいだけの質問だったのかもしれませんが、それであるならば、サービスが儲かっているかってしつこく聞きますかね?ちょっと不快感を感じざるをえない転職面接でした。

それと、カジュアル面談は合否判定はないはずなのに、面接の最後に「合否判定は2、3営業日以内に出します」と言われました。カジュアル面談に合否があるなんて初めて知りました。世の中の広さに驚愕しました。

また、突然体調面の話になります。 体調面の話、SESを行っている企業には死活問題ですよね。 下手に体調崩して休まれると下限の時間割って計画通りの収支になりませんから。 自社サービスを引っ張ることができるエンジニアが欲しいのか、SESに出すだけの駒が欲しいのかはっきりして欲しいですね。

SES事業をやっていることと、サービスに関する考え方が合わなかったので、こちらからお断りさせていただきました。 というかカジュアル面談する前に、企業のWebページ見てSESやっていることくらい気づいておけって話ですよね。すみません。こちらの不手際でした。

まとめ

良い会社の定義って難しいですね。

エンジニアの楽園を求めて、まだまだ私は渡りを続けます。