ActionMailerの添付ファイルをRspecでテストする

ActionMailerで添付ファイルを送るようにしたのですが、そのテストをするときの情報があまりなかったのでまとめました。

以下のようにテストしました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
RSpec.describe AppMailer, type: :mailer do

before(:all) do
Archive::Zip.archive(
"tmp/app.zip",
"README.md")
end

after(:all) do
FileUtils.rm "tmp/app.zip"
end

let(:mail) do
AppMailer.send_email(zip_path)
end

it 'assings the attachment file' do
attachment = mail.attachments[0]
expect(attachment).to be_a_kind_of(Mail::Part)
expect(attachment.content_type).to be_start_with('application/zip')
expect(attachment.filename).to eq "app.zip"
end
end

まず、before(:all)でテストに使うzipファイルを作成します。
zipファイルはarchive-zipを使って作成しています。
アーカイブするファイルは何でもよいですが、この記事ではREADME.mdとしています。
テストで作成したzipファイルはafter(:all)で消しています。
mail.attachments[0]に添付ファイルが入っているので、その種類やファイル名を確認しています。

参考リンク

TCPWrappersとは何なのかを調べてみた

sshのハンドシェイクでこけてしまい、その原因がわからずハマりました。
原因としては、TCPWrappersで拒否されていました。
TCPWrappersとはなんだろうということで調べてみました。

TCPWrappersとは

TCPWrapperとは、ネットワークのアクセス制御をする機能です。
デフォルトでインストールされています。

Linuxの基本的なアクセス制御の仕組み

Access Control to Network Services

アクセス制御の仕組みとして、FirewallとTCP Wrappersがあります。
Firewallはiptablesで制御します。
なぜ2つの仕組みが存在するのかはわかりません(誰か教えてください)。

クライアント→Firewall→TCPWrappers→サービスという流れで処理を行います。
TCPWrappersは接続を許可するかどうかのファイルを参照し、許可リストにあればサービスに処理を受け渡します。

処理のログはsyslog daemon (syslogd)によってクライアントと接続先サービスの情報を /var/log/secure または /var/log/messages に書き込まれます。

TCPWrappersのアドバンテージ

TCPWrappersを使うことで、以下のメリットがあるとのこと。

  • 接続したクライアントとそのサービスがログに残る
  • 様々なプロトコルを集約して管理できる

##TCPWrappersの設定ファイル

以下の2つのファイルがあります。

  • /etc/hosts.allow
  • /etc/hosts.deny

TCPWrappersはクライアントからのリクエストを受けると、以下の処理を行います。

  • /etc/hosts.allow を参照します。クライアントがリストに存在した場合、接続を許可します。リストに存在しない場合は次のステップに進みます。
  • /etc/hosts.denyを参照します。クライアントがリストに存在した場合、接続を拒否します。リストにない場合は接続を許可します。

注意点

  • /etc/hosts.allowから先に評価され、そこでリストに一致した場合/etc/hosts.denyは評価されません。/etc/hosts.allow/etc/hosts.denyに同じクライアントを指定した場合は/etc/hosts.allowが優先されます。
  • ファイルの上から評価されていき、一致した時点で評価を打ち切ります。順番が重要です。
  • TCPWrappersはキャッシュを持ちません。ですので、設定ファイルを書き換えたら即反映されます。デーモンをリスタートする必要はありません。
  • 最後に改行を含めるとエラーメッセージが出るとのことなので、含めないほうがよさそうです。warning: /etc/hosts.allow, line 20: missing newline or line too longというメッセージが /var/log/messages または /var/log/secureに出力されるとのことです。

参考リンク

クロスサイトスクリプティングの脆弱性とその対策

クロスサイトスクリプティングはユーザーの入力を受け付ける動的なアプリケーションに含まれる脆弱性を利用した攻撃です。
英語ではCross Site Scriptingと呼ばれその頭文字をとるとCSSとなりますが、「Cascading Style Sheets」との区別が紛らわしいため一般的にXSSの表記が使われています。

クロスサイトスクリプティングとはどのような脆弱性であるのか

XSSとは動的なWebサイトのプログラムの脆弱性を利用した攻撃です。
この脆弱性を利用することで、攻撃者は悪意のあるJavaScriptコードをサイトに埋め込み、そのコードを実行することができるようになります。

例えば、XSSの脆弱性がある掲示板があったとします。
攻撃者はフィッシングサイトにページを遷移させるJavaScriptコードを投稿しました。
すると、他の利用者がこの掲示板を閲覧したときにそのJavaScriptコードが実行され、フィッシングサイトが表示されるようになります。

この脆弱性を持ったサイトが攻撃されるとどのような被害が起き得るのか

上記の例のようにXSSはWebサイトの利用者を攻撃の対象とします。

例えば、以下のような被害が出る可能性があります。

  • ユーザーのcookie情報を盗み、その情報を利用してそのユーザーでログインする
  • 不正なページへユーザーを誘導する
  • 不正なファイルをダウンロードさせる

Webサイトを直接攻撃することも可能です。
JavaScriptのhtmlを書き換える機能を使って、攻撃対象のサイトの内容を書き換えることができてしまいます。

なぜそのようなセキュリティホールが作り込まれてしまうのか

HTMLには特殊文字が存在します。
具体的には「’”<>&」といった文字です。
これらの特殊文字をHTML上に表示するには適切な方法で変換しなければなりません。
しかし、XSSの脆弱性のあるプログラムはこのような特殊文字を意識せずに作られてしまっています。

どのように対策をすればよいのか

入力時のチェックと出力時のチェックを行います。

入力時のチェック

具体的には、入力時に入力できる文字を制限します。
例えば、郵便番号を入力するフォームの場合は、数字と-しか入力できないようにします。
そうすれば、悪意のあるコード自体が入力できなくなります。
入り口でとめる作戦です。

注意点としては、入力文字のチェックは必ずサーバサイドで行わなければなりません。
PHP等のサーバサイドで実行されるプログラムでチェックする必要があるということです。
クライアントサイド(JavaScript)のチェックでは攻撃者がそのチェック機構自体を無効にすることが可能です。

出力時のチェック

実際には仕様上、入力文字を制限することができないことがあります。
例えばフリーフォームの場合、無闇に入力文字を制限するとユーザーは自由に文章を入力することができなくなってしまいます。

そこで、出力時に文字を無害化(サニタイジング)します。

サニタイジングについては、プログラム言語やフレームワークに専用の機能が用意されています。
その機能を使うことを強く推奨します。

例えばPHPであれば、htmlspecialcharsというhtmlの特殊文字を無害化する関数が用意されています。
http://php.net/manual/ja/function.htmlspecialchars.php

また、フレームワークはデフォルトでサニタイズされた文字が出力される仕組みになっているはずなので、そのようなフレームワークを使ったほうがよいでしょう。

参考リンク

Shellで日本語ドメインをPunycode(IDNドメイン)に変換する方法

Shellで日本語ドメインをIDNフォーマットに変換する方法を紹介します。
対象のOSはMacです。

libidnというGNUのライブラリがあるので、それを使います。

libidnのインストール

homebrewでインストールします。

1
$ brew install libidn

インストールしたら、idnコマンドが使えるようになります。

1
2
3
4
5
6
7
8
$ idn
libidn 1.32
Copyright 2002-2015 Simon Josefsson.
GNU Libidn is free software with ABSOLUTELY NO WARRANTY. For more
information about these matters, see <http://www.gnu.org/licenses/>.
Type each input string on a line by itself, terminated by a newline character.

idn: tld_check_4z: Missing input

libidnでPunycodeに変換する

以下のようにPunycodeに変換できます。

1
2
$ idn しょーやん.xyz
xn--68jwei3c27a.xyz

unicodeに変換する場合は-uオプションを使います。

1
2
$ idn -u xn--68jwei3c27a.xyz
しょーやん.xyz

digとidnを組み合わせて使う

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  dig `idn しょーやん.xyz` NS

; <<>> DiG 9.8.3-P1 <<>> xn--68jwei3c27a.xyz NS
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59533
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;xn--68jwei3c27a.xyz. IN NS

;; ANSWER SECTION:
xn--68jwei3c27a.xyz. 3578 IN NS dns01.muumuu-domain.com.
xn--68jwei3c27a.xyz. 3578 IN NS dns02.muumuu-domain.com.

;; Query time: 34 msec
;; SERVER: 192.168.74.20#53(192.168.74.20)
;; WHEN: Fri Jun 3 09:25:40 2016
;; MSG SIZE rcvd: 94)>>>>>>

Capistranoの導入手順とプラグインの紹介

Capistranoのインストールと設定、プラグインの紹介をします。

Capistranoのインストール

bundle initコマンドでGemfileを作成します。

1
2
$  bundle init
Writing new Gemfile to /Users/shoyan/app/Gemfile

以下のGemfileが作成されます。

1
2
3
4
5
$ cat Gemfile
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"

GemfileにCapistranoを定義します。

1
2
3
group :development do
gem "capistrano", "~> 3.4"
end

bundle installコマンドでインストールします。

1
$ bundle install

Capistranoの設定

Capistranoがインストールできたので、設定をしていきます。
cap installコマンドで雛形を作成します。

1
$ bundle exec cap install

config/deploy.rbconfig/deploy/{production, staging}.rb が作成されているので適宜編集します。

config/deploy.rb

1
2
3
4
5
6
7
8
9
10
11
# config valid only for current version of Capistrano
lock '3.5.0'

set :application, ‘app'
set :repo_url, 'git@github.com:shoyan/shoyan.git'

ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
set :deploy_to, '/var/www/app'
set :scm, :git
set :format, :airbrussh
set :keep_releases, 5
  • application: アプリケーション名
  • repo_url: リポジトリのURI
  • branch: デプロイ対象のブランチ
  • deploy_to: サーバーのアプリケーションのディレクトリ
  • scm: git or svn
  • format: ログのフォーマット
  • keep_releases: 過去にデプロイしたアプリケーションを何世代保持するか

デプロイするサーバーを定義します。
事前に鍵認証でsshログインできるようにしておくととスムーズです。

config/deploy/production.rb

1
role :app, %w{shoyan@server001.example.jp shoyan@server002.example.jp}

Capistranoでデプロイする

以下のコマンドでデプロイします。

1
$ bundle exec cap production deploy

便利なCapistranoプラグイン

Capistranoには様々なプラグインがあります。
ここでは私が使っているプラグインを紹介します。

capistrano-github-releases

デプロイ時にタグをつけたり、PRにリリースコメントをつけれます。

https://github.com/linyows/capistrano-github-releases

capistrano/slack_notification

デプロイ時にSlackに通知できるようになります。

https://github.com/linyows/capistrano-slack_notification

capistrano_banner

デプロイ時にASCIIアートを設定できます。
また、productionにデプロイする前に確認をしてくれるようになります。

https://github.com/holysugar/capistrano_banner

capistrano-bundler

デプロイ時にbundle installを実行できるようになります。

https://github.com/capistrano/bundler

capistrano3-unicorn

デプロイ時にunicornのコマンドを実行できるようになります。

https://github.com/tablexi/capistrano3-unicorn

###capistrano-withrsync

rsyncでデプロイしてくれるプラグインです。
デプロイするファイルが大きい場合に便利です。

https://github.com/linyows/capistrano-withrsync