My Photo

« September 2013 | Main | November 2013 »

October 30, 2013

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

関連記事:
クロスサイトリクエストフォージェリ(CSRF)その1

脆弱性が生まれる原因

背景として、以下がある。

  1. form要素のaction属性にはどのドメインURLでも指定できる。
  2. クッキーに保管されたセッションIDは、対象サイトに自動的に送信される。

1は罠サイトからでも攻撃対象攻撃対象サイトにリクエストを送信できる、2は罠サイト経由のリクエストでもセッションIDのクッキー値が送信されるので、認証された状態で攻撃リクエストが送信されるという意味である。

正規利用者が意図したHTTPリクエストとCSRF攻撃のHTTPリクエストは、Referer以外、クッキーも含めて同じである。よって、アプリケーションが意識して正規利用者の意図したリクエストであることを確認しない限り、両者は区別できない。
また、クッキー以外にも自動的に送信されるパラメータを使ってセッション管理しているサイトにはCSRF脆弱性の可能性がある。具体的には以下。

  • HTTP認証
  • SSLクライアント認証
  • 携帯電話の携帯ID(iモードID、EZ番号、端末シリアル番号など)

参考文献:体系的に学ぶWebアプリケーションの作り方 4.5.1 クロスサイト・リクエストフォージェリ(CSRF)

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 29, 2013

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

発生箇所
  • クッキーのみでセッション管理が行われているサイト
  • HTTP認証、SSLクライアント証明書、径庭電話の携帯IDのみで利用者の識別が行われているサイト
影響を受けるページ CSRF脆弱性のあるページのみ
影響の種類 被害者の権限で「重要な処理」が実行させられる。

上記にある「重要な処理」とは、例として以下のようなものである。

  • 利用者のアカウントによる物品の購入
  • 利用者のアカウントによる退会処理
  • 利用者のアカウントによる掲示板への書き込み
  • 利用者のアカウントによるパスワードやメールアドレスの変更

入力-実行パターンのCSRF攻撃

以下のスクリプトはパスワード変更処理の概要を示したものである(概要であり、色々省略してある)。

スクリプト1:ログイン
<?php	// ログインしたことにする確認用スクリプト
session_start();
$id = @$_GET['id'];
if (!$id) { $id = 'yamada'; }
$_SESSION['id'] = $id;
?>
<html><body>
ログインしました。(id:<?php echo htmlspecailchars($id, NET_NOQUOTES, 'UTF-8'); ?>)<br>
</body></html>
スクリプト2:パスワード入力画面
<?php
session_start();
// ログイン確認は省略
?>
<html><body>
<form action="change_password.php" method="post">
新パスワード<input type="password" name="pwd"><br>
<input type="submit" value="パスワード変更">
</form>
</body></html>
スクリプト3:パスワード変更
<?php
function ex($s) {	// XSS対策用のHTMLエスケープと表示関数
	echo htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
}
session_start();
$id = $_SESSION['id'];	// ユーザIDの取り出し
// ログイン確認は省略
$pwd = $_POST['pwd'];	// パスワードの取得
// パスワードの変更処理は省略
?>
<html><body>
<?php ex($id); ?>さんのパスワードを<?php ex($pwd); ?>に変更しました。
</body></html>

スクリプト1でログイン状態となり、スクリプト2で新しいパスワードを入力し、スクリプト3でパスワードを変更する、という流れである。
スクリプト3でパスワードが変更される条件は以下の3つ。

  • POSTメソッドでスクリプト3にリクエストされること
  • ログイン状態であること
  • POSTパラメータpwdとしてパスワードが指定されていること

上記の条件を満たすリクエストを送信させるのがCSRF攻撃である。
以下にCSRF攻撃用スクリプトを示す。

スクリプト4:攻撃用スクリプト
<html>
<body onload="document.forms[0].submit()">
<form action="http://example.jp/change_password.php" method="post">
<input type="hidden" name="pwd" value="cracked">
</form>
</body>
</html>

このスクリプトによる攻撃の流れは以下のようになる。

  1. 利用者が攻撃者による攻撃対象サイトにログインしている。
  2. 攻撃者が罠(前述のスクリプト4)を用意する。
  3. 利用者が罠を閲覧する。
  4. 罠のJavaScriptがされ、利用者のブラウザ上で攻撃対象サイトへPOSTメソッドで新しいパスワードが送信される。
  5. パスワードが変更される。

実際の攻撃では見えないiframeに攻撃用スクリプトを入れて攻撃します。
iframeの外側(罠サイトのドメイン)からiframe(攻撃対象サイトのドメイン)の内容は読み取れないので、CSRF攻撃では情報を盗み出すことはできない。
しかし、CSRF攻撃でパスワードが変更できる場合は、攻撃者は変更したパスワードで不正にログインして情報を盗み出すことができる。

CSRF攻撃とXSS攻撃の比較

CSRF攻撃と反射型のXSS攻撃は似ている。先の攻撃の流れで言うと1~3は同じで、それ以降が異なる。
CSRF攻撃は3のリクエストに対するサーバ側の処理を悪用するもので、やれることはサーバ側で用意された処理に限定される。
一方、XSS攻撃は3のリクエストに含まれるスクリプトはレスポンスにも含まれ、ブラウザ上で実行される。つまりブラウザ上でできることは何でもできる。
このように攻撃の広範さという点でXSSの脅威の方が大きいが、CSRFは以下の点で注意すべき脆弱性である。

  • CSRFは設計段階から対策を盛り込む必要がある。
  • 開発者の認知度がXSSに比べて低く、対策が進んでいない。

確認画面がある場合のCSRF攻撃

hiddenパラメータでパラメータを受け渡ししている場合は、確認画面がない場合と同じ方法で攻撃できる。

セッション変数でパラメータを受け渡ししている場合は、以下のような2段階の攻撃となる。

  1. 確認画面に対してパラメータをPOSTしてセッション変数にパラメータをセットさせる。
  2. タイミングをを見計らって実行画面を呼び出す。

これを実現する方法として、iframeを2つ使うことが考えられる。
第1のiframeはロードされるとすぐにPOSTする。これでセッション変数にパラメータがセットされる。
第2のiframeはロードされてから一定時間後にリクエストを送る。この時点でセッション変数にパラメータがセットされていれば攻撃が成功となる。
ウィザード形式のように最終実行が面まで多段になっていても、iframeを増やすことで対応できる。

参考文献:体系的に学ぶWebアプリケーションの作り方 4.5.1 クロスサイト・リクエストフォージェリ(CSRF)

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 27, 2013

SQLインジェクション その8

関連記事:
SQLインジェクション その1
SQLインジェクション その2
SQLインジェクション その3
SQLインジェクション その4
SQLインジェクション その5
SQLインジェクション その6
SQLインジェクション その7

SQLインジェクションの保険的対策

  • 詳細なエラーメッセージの抑止
  • 入力値の妥当性検証
  • DBの権限設定

詳細なエラーメッセージの抑止

PHPの場合、詳細なエラーメッセージを抑止するには php.ini で以下のように設定する。

display_error = Off
DBの権限設定

アプリケーションを実現するのに必要最低限な権限のみを与えておけば、万が一SQLインジェクション攻撃を受けても被害を最小限にとどめることができる。
例えば商品情報を表示するだけのアプリケーションの場合、商品テーブルの読み出し権限だけを与える。これで商品データや他のデータの改ざんはできない。

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

釣り番組まとめ

今住んでいるところ無料で見れる釣り番組。

火曜日 20:00~21:00 tvk 釣りビジョンアワー
木曜日 22:00~22:54 BS-TBS 釣り百景
金曜日 05:00~05:30 東京MX(サブ) フィッシング倶楽部
土曜日 08:30~08:45 東京MX GO!GO!九ちゃんフィッシング
土曜日 17:00~17:30 BS日テレ 夢釣行~一魚一会の旅~
土曜日 17:30~18:00 テレビ東京 THE フィッシング

October 25, 2013

UbuntuサーバのApache2でURLリライトを試してみる

ディストリビューション、OSの情報を取得する

セキュリティの本のテスト環境として付録CD-ROMに入っていたVMwareの仮想OSでURLリライトを有効にしてみる。
まず、ディストリビューション、Apacheのバージョンを確認してみた。

root@aaaa:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 10.04.1 LTS
Release:        10.04
Codename:       lucid
root@aaaa:~# uname -a
Linux aaaa 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux
root@aaaa:~# apache2ctl -v
Server version: Apache/2.2.14 (Ubuntu)
Server built:   Apr 13 2010 19:28:27
root@aaaa:~# 

ディストリビューションはUnbuntu 10.04.1だった。
lsb_release のLSBとは "Linux Standard Base" の略で、多くのディストリビューションはこのLSBに準拠している。準拠していれば lsb_release コマンドでディストリビューションの情報を取得できる。
"uname -a" の2つ目の項目はホスト名。
apache2ctl はApacheのバージョンを調べたりできる。-M オプションでロードされているモジュールを列挙する。

mod_rewriteを有効にする

Apache2の設定ファイルは /etc/apache2 以下にあって、大本の設定ファイルは apache2.conf。このファイルからさらにいろんな設定ファイルがインクルードされている(参考3,4)。
サーバ管理者が設定を追加する場合は httpd.conf に書き加える。初期状態で httpd.conf は空である。apache2.conf や他の設定ファイルは基本的には触らないらしい。
デフォルトではURLリライトのモジュールmod_rewriteは有効になっていない。
mode_rewrite を有効にするには設定ファイルを手で編集するのではなく、以下のコマンドを使用する(参考5)。このコマンドの名前は "Apache2 enable module" ということかな。

sudo a2enmod rewrite

すると /etc/apache2/mods-available ディレクトリに rewrite.load が作成され、/etc/apace2/mods-enabled ディレクトリに rewrite.load へのリンクである rewrite.conf が作成される。
そしてサーバを再起動。以下のコマンドを実行。

sudo /etc/init.d/apache2 restart

URL書き換えルールの記述

URLリライトの書き換えルールはApache2の設定ファイルか、URLの書き換えをしたいディレクトリの .htaccess に書き換えルールを記述する。
<ドキュメントルート>/test/rewrite に下記の内容の .htaccess を作った。

RewriteEngine ON
RewriteBase /test/rewrite

RewriteRule ^index.html$|^$ top.html [L]

RewriteRule ^y.*$ http://www.yahoo.co.jp/ [R,L]

RewriteCond %{HTTP_USER_AGENT} MSIE
RewriteRule ^.+$ ie.html [L]

実際の運用は想定しておらず、単に試してみようと思っただけ。以下のような書き換えを期待した。

書き換え前書き換え後備考
1/test/rewrite/index.html/test/rewrite/top.html
2/test/rewrite/ /test/rewrite/top.html
3/test/rewrite/y.*http://www.yahoo.co.jp/301リダイレクト
4IEの場合/test/rewrite/ie.html1,2,3以外でIEの場合

しかし、実際にはIEで1,2の書き換え前にアクセスすると想定通りにはならず、4の書き換え後の ie.html が表示される。3の書き換え前にアクセスした場合は想定どおりyahooにリダイレクトされる。
ググって調べたところ、.htaccess や apache2 設定ファイルの "<Directory>" 内にURLリライトのルールを書いた場合、書き換えられたURLはさらに頭から書き換えルールを適用されてループするそうだ(参考12)。ループが止まるのは書き換えの必要がなくなるか、ループ回数の上限に達してエラーになるまで。
よって、1,2の場合では想定どおり1,2のルールで top.html に書き換えられる(1回目)。その後にまた頭から書き換えルールが適用されて4のルールで ie.html に書き換えられる(2回目)。そしてまたさらに頭から書き換えルールが適用されて、今度はマッチするものがないのでそこでURLリライトは終了する(3回目)。
3のルールは1度適用されると外部サイトに転送されるのでそこでURLリライトは終わり、想定どおりの結果となる。
うーん。"[L]" でURLリライトのループは防げるもんだと思ってたんだが、"[L]" は1つの設定の中でマッチしたらそれ以降は無視という意味で、書き換えルール全体を再適用することには関係ないようだ。難しい。
以下のように修正したら、想定どおりに動いてくれた(参考13)。4行目の内容を追加した。
'-' は置換禁止を意味する。これでなんでループが止まるのか良くわからないが。

RewriteEngine on
RewriteBase /test/rewrite/

RewriteRule ^top.html - [L]

RewriteRule ^index.html$|^$ top.html [L]

RewriteRule ^y.*$ http://www.yahoo.co.jp/ [R,L]

RewriteCond %{HTTP_USER_AGENT} MSIE
RewriteRule ^.+$ ie.html [L]

RewriteLogの設定

前記のように最初はURLリライトが想定どおりに動かなかった。そこで、調査のためにURLリライトのログを出力するようにした。
RewriteLog,RewriteLogLevel を設定に追加するのだが、mod_rewriteのリファレンスを見ると

  • RewriteCond,RewriteRule などのContext:server config, virtual host, directory, .htaccess
  • RewriteLog,RewriteLogLevel のContext:server config, virtual host

とある。.htaccess に書いても動かない。
編集するファイルは /etc/apache2/httpd.conf である。内容は以下。

RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3

RewriteLogLevel の後ろの数字が大きいほど詳細なログを吐くが、実際2とか3で十分らしい。
また、パフォーマンスが悪化するのでテストの時以外は基本はURLリライトのログ出力は止めるべきである。

ftpサーバのインストール

VMware上のUbuntuサーバとWindowsとの間でファイルをやりとりするためにFTPサーバをインストールした。
起動/停止は以下のコマンド。<コマンド>は start/stop。

service vsftpd <コマンド>

参考サイト:
・lsb_release,unameについて
1.超初心者がubuntuでWebサーバーを作る
2.unameコマンド|Linuxコマンド
・UbuntuサーバのApache2について
3.超初心者がubuntuでWebサーバーを作る
4.Apache 2.2 設定 Ubuntu Server: IT備忘録
5.Apache2 mod_rewrite と AllowOverride | マキシマ文庫
・mod_rewriteのリファレンス
mod_rewrite - Apache HTTP Server
・URLリライトの設定について
6..htaccessのRewriteCond、RewriteRule:超初心者向けメモ|ぼくんちのバックステージ
7.Apache : mod_rewriteリファレンス - dawgsdk.org
8.mod_rewriteの考え方。 - こせきの技術日記
9.Apache module mod_rewrite
10.Apache Rewrite 設定のサンプル(1) : 個別のURLを転送する | OSCALOG
11.ApacheウェブサーバーのRewrite設定で使える正規表現サンプル集 | Web担当者Forum
12..htaccess ファイルで mod_rewrite の設定をする時の注意点 | ミヤビッチの穴
13., mod_rewriteによるURLの書換えに関するメモ - 忘れたときに備えた記録(2007-07-18) 14.ユーザーエージェントを利用したデバイスの振り分け[Apache,mod_rewrite,.htaccess] | yama-log
15.【apache】mod_rewriteの設定について|My ProjectWEB
16.mod_rewriteの[L]フラグの挙動がよくわかりません。 下記のよう.. - 人力検索はてな
・ftpサーバについて
17.Ubuntu 10.04 LTS - FTPサーバー - Vsftpd インストール : Server World
18.[ubuntu][さくらVPS]vsFtpdインストール | ino Hanging Around

October 21, 2013

WEBページのタイトルとURLを一発コピーするFirefoxのアドオン

ブラウザのコピー機能を拡張する。 - こせきの技術日記

ブログを書いていて参考にしたサイトのタイトルとURLをリンクとして記述するときに便利。
SleipnirではJavaScriptを登録してキー割り当てをして簡単に呼び出すことができた。それを使っていた上記のアドオンと同じ機能を実装していた。クリップボードにあるタイトルとURLからそのサイトのリンクを出力する秀丸マクロを作っていたのでリンクの作成は非常に簡単に行えた。
だがブラウザをFirefoxに変えてからは「スクリプトを登録して呼び出す」みたいな単純な機能がないので、いちいちアドレスバーからURLをコピーし、ページの情報orソースコードを表示してタイトルをコピーと原始的なことをいていた。
いい加減面倒になったので自分でアドオンを作ろうと思いググっていたら、すでにやりたいことを実現できるアドオンがあった。
めっちゃ便利だ^^

October 20, 2013

auのLTEでhttpとhttpsでIPアドレスが違った

前の記事でiPadを使って調査をしていたときにハマってしまったことのメモ。

問題が起きていたWEBアプリはIPアドレスでアクセス制限をかけていた。http用とhttps用に.htaccessがあって両方にIPアドレスを書くとWEBアプリを使えるようになる。
クライアントのIPアドレスを調べるのは便利なサービスがあったので利用した。
自分のグローバルIPアドレスを確認するツール IP君

上記のサービスで表示されたIPアドレスをそれぞれの.htaccessに記入して、さて調査開始、と思ったらうまく行かない。httpの部分にはアクセスできるが、httpsの部分にアクセスできない。403エラーになる。
403ってことはアクセス制限に引っかかってるということだ。IPアドレスを確認したが、間違いはない。

数時間ハマってしまった。原因は、httpでアクセスしたときとhttpsでアクセスしたときでリモートのIPアドレスが違っていたことだった。
上記のサービスはhttpなのでそこで判明したIPアドレスはhttpの方の.htaccessに書くのは正しいが、httpsの.htaccessには別のIPアドレスを書かなければならなかったのだ。両方にhttpのIPアドレスを書いていたからhttpの方はOKでhttpsの方は403になる。当たり前だ。

WEBサービスのログからhttpsでアクセスしているときのIPアドレスを調べてhttpsの.htaccessに書いたら問題なくhttpsの部分にもアクセスできるようになった。
なんですぐに気付かないんだ。実は最初にこのことに気付くヒントがあった。最初にhttpsのところで取っているログを見て、直後に上記のサービスの画面を見たのだがそれぞれのIPアドレスが違っていたのだ。でもその時は「時間が経ってIPアドレスが割り当て直されたんだろう。」と思ってしまった。俺のバカorz

これはiPadとか端末の問題じゃなくてWANの問題?
回線はauのLTE。httpのIPアドレスは数分単位でコロコロと変わった。ちょっとアクセスしないとすぐにアドレスが再割り当てされるようだ。対してhttpsのアドレスは少なくとも日中はずっと同じだった。翌日になると変わっていたが。
ちなみにドコモの3G回線でAndroid端末からアクセスした場合はhttpとhttpsで同じIPアドレスだった。

October 19, 2013

iOS7に更新したiPadのSafariで問題発生

iPadのiOS7に更新したら今まで動いていたWEBアプリが動かなくなった。
JavaScriptがうまく動いてないっぽい。コードはこんな感じ。間違ってるところがあるかもしれないが。

var flag = isIOSSafari();
subWin = window.open("empty.html", "sub", "width=200,height=200");
if (flag) {
	window.focus();
}
$.post("getServiceUrl.php", {"param": param }, function(data) {
	subWin.location.href = data['url'];
	if (focusFlag) {
		subWin.focus();
	}
}

上記のJavaScript、jQueryのコードは空の子ウィンドウを開いてそこに別のドメインのWEBサービスの画面を表示する処理。
最初の isIOSSfari() はユーザエージェントを調べてiPad,iPhoneのSafariの場合に true を返す。
2行目で子ウィンドウを開く。
3~5行目はiPhone、iPadのSafariに対する特別対応で、子ウィンドウを開いた後に親ウィンドウにフォーカスを戻している。
6行目からはAjax。"getServiceUrl.php" は別ドメインのWEBサービスのURLを返す。そしてそれを子ウィンドウにセットしている。
そして8~9行目でまたSafari対応で子ウィンドウにフォーカスを移している。
Safari対応の部分は後から書かれた。その部分がなくてもPCのブラウザやAndroidからは問題なく動くが、iPhoneとiPadのSafariでうまくいかなく追加された。iPad、iPhoneのSafariではウィンドウにフォーカスがないとそのウィンドウのJavaScriptがうまく動いてくれないらしい。

これがアップデート前は動いていたが、iOS7にアップデートしたら動かなくなった。
具体的にはサブウィンドウは開くが開いたまま
調べると、isOSSfari()はちゃんと動いているようだ。window.focus() が効いていないのだろうか?

対応策として子ウィンドウを開いてからURLをセットするのではなく、子ウィンドウを開くときにはじめからURLをセットすることを考えた。しかしそれだと子ウィンドウが開いてくれない。Safariのポップアップブロック機能が効いているのかも。URLが親ウィンドウのとは別ドメインだからか。

また別の対応策として、子ウィンドウの方でWEBサービスのURLを取得するように修正するというのも考えた。つまり現在は

  1. 子ウィンドウを開く。
  2. 親ウィンドウでサービスのURLを取得。
  3. 子ウィンドウにURLをセット。

としているのを、

  1. サービスのURL取得に必要なパラメータをつけて子ウィンドウを開く。
  2. 子ウィンドウ内のJavaScriptでURLを取得して子ウィンドウのURLを書き換える。

と修正することを考えた。
しかしこれだと修正がJavaScriptだけにとどまらなずPHPの方にも及ぶ。
なんとかJavaScriptだけの修正できないか、思案中。
iPadのSafariにはPCのブラウザのような開発者ツール(FireBugsなど)的なものがあればなあ。そうすれば調査は楽になるしもっとはっきりとしたことがわかると思うのだが。

October 16, 2013

「ダークゾーン」読了

花野屋の若旦那の読書感想で面白そうだったので読んでみた。
そこそこ面白かった。読後感がすごく悪いww
ホラーなので褒めてます。
でも、「黒い家」が5点だとしたら3.5点かなあ。

以下、作品の内容に多少触れるので未読の方はお気をつけください。


「黒い家」はスーパーナチュラルなものが一切出てこなかった。現実世界の出来事を描いているので作品を読んでいると「自分の身にも起こり得る」という怖さがある。
それに対して「ダークゾーン」はまったくアンリアルの世界。異世界なのかゲームなのか、とにかく現実世界ではないところが舞台。「自分の身にも」感がない分、恐怖が「黒い家」より少なかった。

また、「いきなりゲーム(またはゲームみたいな)世界に放り込まれる」っていう作品は、設定としてはよくあるので「なんか読んだことある」感があった。

そして、これは自分がそう思っただけで実際はどうかわからないが、将棋と囲碁にまったく知識がない人はこの作品がすんなり理解できただろうかということ。
自分は将棋は話にならないほど弱いがルールや将棋界の知識だけはある。とくに奨励会については。主人公と同じように奨励会の三段リーグに所属していた瀬川晶司四段の「泣き虫しょったんの奇跡」を読んだから。三段リーグがどれほど苦しいのかよくわかっている(つもり)。
また、「目が2つないと生きていけない」という話。自分は父親が囲碁をやっていて子供の頃に「レッツ碁」(今は違う名前になったんだっけ)をパラパラと見ていたので「目が2つ…」など囲碁関連の知識もあったが、囲碁の知識ゼロの人はどうだろう?
将棋・囲碁のことがわからない人にも読めるような説明を入れて話は書かれているが、詳細な説明ではない。
もちろん「詳しい説明を入れるべき」なんて思っていない。ただ、将棋・囲碁を知らない人はところどころで「あれ、これどういう意味?」って引っかかりながら読むことにならないだろうかと思った。


ダークゾーンダークゾーン
貴志祐介

祥伝社 2011-02-11
売り上げランキング : 51941

Amazonで詳しく見る
by G-Tools

黒い家 (角川ホラー文庫)黒い家 (角川ホラー文庫)
貴志 祐介

角川書店 1998-12
売り上げランキング : 3774

Amazonで詳しく見る
by G-Tools
泣き虫しょったんの奇跡 完全版<サラリーマンから将棋のプロへ> (講談社文庫)泣き虫しょったんの奇跡 完全版<サラリーマンから将棋のプロへ> (講談社文庫)
瀬川 晶司

講談社 2010-02-13
売り上げランキング : 105279

Amazonで詳しく見る
by G-Tools

2013秋アニメ第1話感想

第1話は色々と見てそれから何本か切ろうと思っていたが、切れる作品がない。やばい。

キルラキル
グレンラガンが好きな自分としては一番期待していた作品。期待にたがわぬ面白さ。
ケレン味あふれる演出。面白い。
これは最後まで見る。

のんのんびより
まあこういう作品は1つは必要だ。心の安らぎのために^^
あそこまで生徒は少なくなかったがかなりの田舎出身者なので懐かしさを感じる。
ただ1つ、第1話で疑問に思ったことが。バスに乗り遅れて「次のバスは2時間後」とあったが、あれくらいの田舎だとバスは「朝夕に1本ずつ」だと思う。というか、バス路線自体が廃止になっているのが普通。
あのバス路線が民間じゃなくて自治体が運営するバス路線なら「次2時間後」もあり得るが、だったら小中学生が乗り遅れそうになったら待ってくれるはず。
と、リアルなことを考えながら見ていたww
OPのnano.RIPEの歌も気に入った。最後まで視聴決定。

境界の彼方
京アニ作品。第1話ではまだなんともだが、期待して視聴継続。

コッペリオン
これはだいぶ前にアニメ化が決定していたが、F1の事故のせいで放送が今になった作品。
アニメには「原発」とか「放射能」といった言葉は一切出てこない。まあ仕方ないよね。
でも今、色々言われそうなのにこの作品をTVアニメでやるスタッフに拍手を送りたい。

ログホライズン
「THEフィッシング」と同じ時間なので見るつもりなかったが、第1話のときの「THEフィッシング」があまり興味ない内容だったので見てみたら、意外と面白かった。
アカツキ(CV:加藤英美里)かわいい^^
みる前に「.hack」とか「SAO」みたいとか言ってごめんなさい^^;
通常は「ログホライズン」を録画・視聴して、「THEフィッシング」は面白そうな内容のときだけ録画しよう。

夜桜四重奏~ハナノウタ~
OPのUNISON SQUARE GARDEN、「TIGER&BUNNY」のTVのOPも映画の主題歌もこのアニメのOPも、なんか同じに聞こえる。あの感じが好きなら全部いい歌ってことになるんだろうけど、別に好きでも嫌いでもないので単に「同じ歌」という感想しか出ないな。
まあ話は、これから面白くなるかどうか見守りたい。


という訳で、以上6作品+夏から続いている物語シリーズのあわせて7作品を今期は視聴する。多い。
「境界の彼方」、「コッペリオン」、「夜桜四重奏」はすごく面白いとは言わないが、なんか切るには惜しい気がするのでたぶん全部見続けそうだ。
7作品は多いがアニメ以外のテレビ番組の視聴を減らして乗り切るつもり。
具体的には釣り番組2つと「コズミックフロント」は興味ある内容のときだけ(釣りならキス釣り、堤防釣り、シーバス釣りなど)見るようにして、あと将棋フォーカスの講座は録画だけしておいて祝日とか年末年始とか時間があるときにまとめて見るようにする。
なんとかなるかな。

二度と上州屋には行かない

上州屋にRYOBI(上州屋のショップブランド)のリールを修理に出していた。
一部の部品がガタついていたのでそこを直すように依頼した。修理代金が3000円以内なら修理してしまっていい。もし3000円を超えるなら一度連絡をくれと言っておいた。
で、先日、修理が終わったというので受け取りに行った。代金は1000円未満。「組み直し」と書いた紙のタグが付けてあった。
「俺の組み立て方が悪かったのか。しかし思ったより安くて良かった^^」などと思いながら帰宅し、梱包を開いてリールのハンドルを回したら、全然直ってねえー!!
「ここを直してくれ」と言っておいた部品のガタつきはまったくそのまま。「修理代金が3000円以内なら修理してしまっていい」ってとこだけ守ってる。ふざけんな!!
もしそのガタついているのが仕様なら(そんなことありえないと思うが)そのまま「これはこういうもので直りません」と言って返せばいい。あるいはリール全体が歪んでいるとか(それもありえないと思うが)で修理不能ならばそれもそう言って何もせず返却すればいい。
それなのに修理を依頼したところはそのままで、ただ分解して組みなおして代金請求ってどういうこと?ありえないだろう。
クレームを言って上州屋と戦おうかと思ったが、なんかあそこの店員、態度悪そうで文句を言ってもまともに取り合ってくれそうにない感じがした。それに戦って勝っても戻ってくるのは1000円にも満たない額。バカらしいのでやめた。
このリールは捨てよう。
そして2度と上州屋に行かないことを誓った。
行ったことないけどタックルベリーに行ってくるか。

October 14, 2013

SQLインジェクション その7

関連記事:
SQLインジェクション その1
SQLインジェクション その2
SQLインジェクション その3
SQLインジェクション その4
SQLインジェクション その5
SQLインジェクション その6

脆弱性への対策

文字列リテラルの問題

SQLインジェクション脆弱性を快勝するにはSQLを組み立てる際にSQLの変更を防ぐことである。その具体策には以下の2つがある。

  1. プレースホルダによりSQL文を組み立てる。
  2. アプリケーション側でSQL文を組み立てる際に、リテラルを正しく構成するなどSQL文が変更されないようにする。

bは完全な対応は難しく、aのプレースホルダを使用することを強く推奨する。
PHPからDBを利用する場合、PEARのMDB2を推奨する。

プレースホルダを利用すると先の蔵書検索のSQLは以下のようになる。

SELECT * FROM books WHERE author = ? ORDER BY id

SQL文中の"?"がプレースホルダ(place holder)で、変数屋敷など可変パラメータの場所に埋め込んでおくものである。"place holder" とは「場所取り」という意味。
MDB2を利用してコードは以下のようになる(DBはPostgreSQL)。

<?php
require_once 'MDB2.php';
header('Content-Type: text/html; charset=UTF-8);
$author = $_GET['author'];
// 接続時に文字コード(UTF-8)を指定
$mdb2 = MDB2::connect('pgsql://<ユーザ名>:<パスワード>@<ホスト名>/<データベース名>?charset=utf8');
$sql = 'SELECT * FROM books WHERE author = ? ORDER BY id';
// SQL呼び出し準備。第2引数の配列でプレースホルダの方を指定する。
$stmt = $mdb2->prepare($sql, array('text'));
// SQL文実行
$rs = $stmt->execute(array($author));
// 表示処理は省略
$mdb2->disconnect();	// 切断
?>

プレースホルダ

プレースホルダには2種類の方式がある。

静的プレースホルダ

プレースホルダを含むSQLがそのままDBエンジンに送られ、DBエンジン側でコンパイルされる。次にバインド値がDBエンジンに送られてDBエンジン側で値を当てはめられた後にSQL文を実行される。
バインド値を入れる前にコンパイルされるのでSQLインジェクション脆弱性は原理的に発生ない。

動的プレースホルダ

アプリケーションのライブラリ側で値をバインドしてからDBエンジンに送られる。リテラルは適切に処理されるので処理系にバグがなければSQLインジェクション脆弱性は発生しない。

原理的にSQLインジェクションの可能性がない静的プレースホルダ方式の方が安全である。
動的プレースホルダの実装の問題でSQLインジェクション脆弱性が発生した例がある。

LIKE述語とワイルドカード

LIKE述語を使って "_" や "%" を検査する場合はこれらの文字をエスケープする必要がある。
このエスケープを怠ることとSQLインジェクションは別物だが混同している人が多い。

"_" や "%" を自体を検索したい場合はESCAPE句を指定してWHERE句はいかのようになる。

WHERE name LIKE '%#%%' ESCAPE '#'

DBエンジンによってエスケープが必要な文字は異なる。OracleやDB2では全角文字の "%" と "_" もエスケープの必要がある。

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 13, 2013

SQLインジェクション その6

関連記事:
SQLインジェクション その1
SQLインジェクション その2
SQLインジェクション その3
SQLインジェクション その4
SQLインジェクション その5

脆弱性の原因

文字列リテラルの問題

SQLでは文字列リテラルはシングルクウォートで囲む。文字列リテラル中にシングルクウォートを含めたい場合はシングルクウォートを重ねてエスケープする。
SQLインジェクション脆弱性のあるアプリケーションではエスケープが抜けているため、例えば "O'Reilly" をパラメータとして渡した場合に以下のようなSQLが組み立てられる。

SELECT * FROM books WHERE author='O'Reilly'

"O"の後ろでシングルクウォートで文字列リテラレルが終了され、後続の"Reilly'"が文字列リテラルをはみ出した状態となる。このはみ出した部分に意味を持つ文字列を入れるのがSQLインジェクション攻撃である。

数値項目に対するSQLインジェクション

数値リテラルに起因するSQL脆弱性もある。
スクリプト言語の多くは変数に方の制約がない。例として以下のSQLを考える。$ageには数値が入ることを想定している。

SELECT * FROM employees WHERE age < $age

ここで変数$ageに以下の文字列が入った場合、SQLインジェクション攻撃となる。

1;DELETE FROM employees

この場合、組み立てられるSQLは以下のようになるり、表employeesが全削除される。

SELECT * FROM employees WHERE age < 1;DELETE FROM employees

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

SQLインジェクション その5

関連記事:
SQLインジェクション その1
SQLインジェクション その2
SQLインジェクション その3
SQLインジェクション その4

その他の攻撃

  • OSコマンドの実行
  • ファイルの読み出し
  • ファイルの書き出し
  • HTTPリクエストにより他のサーバを攻撃

ファイルの読み出しについて実例をあげる。
SQLインジェクション その1のスクリプトを

author=';COPY+book(title)+FROM+'/etc/passwd'--

というクエリストリングを付けてアクセスすると、以下のSQLが実行される(見やすいように適宜改行を入れてある)。

SELECT id, title, author, publisher date, price
FROM books
WHERE author ='';COPY books(title) from '/etc/passwd'

COPY文はPostgreSQLの拡張機能でファるをテーブルに読み込む。
上記の例では /etc/passwd をテーブルbooksの列titleに読み込む。読み込まれた後にさらにSQLインジェクション脆弱性を使って以下のクエリストリングを付けてアクセスするとpasswdファイルの内容を見ることができ、DBを通じてサーバ上のファイルの内容が外部に漏洩することになる。

DB中の表名・列名の調査方法

標準SQLにinformation_schemaというデータベースが規定されている。その中のtablesやcolumnsというビューで表や列の定義を参照できる。
下記はビューcolumasを使用してテーブルusersの定義をSQLインジェクション その1のスクリプトへのSQLインジェクション攻撃により表示するためのクエリストリングである。

author='+UNION+SELECT+table_name,column_name,data_type,null,null,null,null+FROM+information_schema.columns+ORDER+BY+1--

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 07, 2013

正規表現メモ

正規表現についてのメモ。確認はPHPのPCRE関数と秀丸エディタ(Ver.8.13)の検索ダイアログで行った。
それ以外の処理系(JavaScript、Javaなど)でも大体同じように使えると思う。

グループ化、非キャプチャグループ

正規表現を "(" と ")" で囲んでグループ化することができる。例えば "ab" が1回以上繰り返しさらに "cd" が1回以上繰り返すという正規表現は "(ab)+(cd)+" となる。
グループ化した部分にマッチする文字列は、グループより後ろの部分で\1,\2,\3,etc...の形式で後方で参照できる。"\"の後ろの数字はグループ化された順番。

正規表現 意味 文字列 マッチする部分
(ab)+(cd)+ "ab" が1回以上繰り返し
その後に"cd"が1回以上繰り返す。
ababcdcdcd ababacdcdcd
([a-zA-Z]+)
(\d+)-\2\1
英字1個以、次に数字1個以上、次にハイフン、
次に前に現れたのと同じ数字1個以上の文字列、 最後に前に出現した英字1個以上の文字列
abc123-123abc,xyz789-xyz789 abc123-123abc

また、グループ化されたものは\1,\2,\3,etc...の形式で後方で参照できる。
"(?:" と ")" で囲めばグループ化はされるが、後方参照はできなくなる。これを非キャプチャグループと呼ぶ。後方参照が不要な場合はこれを用いてもよい。
非キャプチャグループについては検索して調べると、非キャプチャグループの方が単なるグループより動作が速いと記述も変わらないという記述もある。

OR条件

正規表現で "|" はOR(論理和)を表している。
単純に "ABまたはCD" という意味の正規表現は "AB|CD" でよい。"(AB|CD)" や "((AB)|(CD))" のようにグループ化する必要ない。
しかし、"ABC123" または "ABC456" という風に共通部分があって一部がOR条件の場合は、"ABC(123|456)" のようにグループ化する必要がある。グループ化せずに "ABC123|456" では「"ABC123" または "456"」という意味になる。

後方一致指定、後方不一致指定、前方一致指定、前方不一致指定

後方一致指定(肯定先読み)
特定のパターンの後にさらに特定のパターンが続くことを表すアンカー。
"(?=<パターン>)" と記述する。
後方不一致指定(否定先読み)
特定のパターンの後にさらに特定のパターンが続かないことを表すアンカー
"(?!<パターン>)" と記述する。
前方一致指定(肯定後読み)
特定のパターンの前にさらに特定のパターンが存在することを表すアンカー
"(?<=<パターン>)" と記述する。
前方不一致指定(否定後読み)
特定のパターンの前にさらに特定のパターンが存在しないことを表すアンカー
"(?<!<パターン>)" と記述する。
正規表現 意味 文字列 マッチする部分
abc(?=xyz) 後ろに"xyz"が来る"abc" abcxyz abc
abc(?!xyz) 後ろに"xyz"以外のパターンが来る"abc" abcijk abc
(?<=abc)xyz 前に"abc"がある"xyz" abcxyz xyz
(?<!abc)xyz 前に"abc"以外のパターンがある"xyz" lmnxyz xyz

これらの正規表現はアンカーであり、マッチする文字列には含まれない。アンカーとは位置を指定する正規表現である。
#他のアンカーには "^" や "$" などがある。
また、これらの正規表現の後に正規表現を続けると、AND条件になる。例を示す。

正規表現 意味 文字列 マッチする部分
abc(?=\w+) 後ろに単語が続く"abc" abcdef123 abc
abc(?=\w+)[a-z]+ abcdef123 abcdef
正規表現"abc(?=\d+)[^a-z] abcdef123 マッチしない
2014/06/16追記:JavaScriptの正規表現では前方一致指定と前方不一致指定が使えない。

\w

"\w" は単語文字の文字クラス。半角の単語文字は英数字とアンダースコア、と思っていたら、処理系やロケールによっては2バイト文字やアルファベット以外の文字も含まれるらしい。
「"\w" と "[:alnum:]" は同じ」という明らかに間違った説明をしているサイトがいくつかあった。何かのドキュメントに「"\w" と "[:alnum:]" は同じ」とあって、それをもとにして書かれた内容なのかなと思う。

秀丸エディタの検索ダイアログとマクロの検索コマンドの違い

秀丸エディタの検索ダイアログで正規表現を使った検索する場合、「正規表現」だけをチェックして実行すると大文字と小文字の違いを無視する。大文字と小文字を区別したい場合は「大文字/小文字の区別」にもチェックを入れる。
一方、マクロでの検索系コマンド(searchdownなど)では "regular" オプションを付ければ大文字と小文字を区別する。

searchdown <検索文字列>, regular;
"regular" があれば "casesense" はあってもなくても同じ。"regular" かつ "nocasesense" の場合は大文字と小文字を区別しない。
searchdown <検索文字列>, regular, nocasesense;
同じ検索処理なのに検索ダイアログとマクロで使い方の統一性がない。

秀丸エディタの正規表現の注意事項

秀丸エディタの検索・置換・マクロで使う正規表現(以下、「秀丸Regexp」と呼称)と自分がよく使うPHPのPCRE関数や、その他なプログラミング言語の正規表現(以下、「一般Regexp」と呼称)は大体は同じだが、一部独特なところがあるのでメモっておく。
一般Regexpでは前に書いたように "\w" は英数字とアンダースコアを含む文字クラスである。
#それ以外の文字を含む場合もある。
秀丸Regexpでは "\w" は英字とアンダースコアであり、数字を含まない。一般Regexpの "\w" にあたる英数字とアンダースコアからなる文字クラスは "\c" である。
また、一般Regexpでは "\b" は空白文字の文字クラスワード境界のアンカーまたはバックスペース文字であるが、秀丸エディタではバックスペース文字という意味しかない。一般Regexpで "\b" がアンカーがバックスペースかの区別は、角括弧("[]")で囲まれた文字クラスの中ではバックスペース、文字クラスの外ではアンカーである。秀丸エディタには空白文字のもクラスがないが、"\<" と "\>" という英単語の始まり・終わりのアンカーがある。
例として、下記の文字列を考える。

_ab3 C_D4,eF_5

この文字列の単語を検索したい。つまり "_ab3", "C_D4", "eF_5" の3つがマッチするように検索したい。
PHPのPCRE関数であれば以下のコードでできる。難しくはない。

preg_match_all('/\b\w+\b/', "_ab3 C_D4,eF_5", $matches);

秀丸Regexpで同じことをする正規表現は、結構考えてしまった。文字クラス "\b" は使えない。
"\c+" では単語全体だけではなく単語の部分文字列("ab3", "b3", "3", "_D4",etc...) にもマッチする。
"\<\c+\>" では、"\<""\>" が「単語」の始まり・終わりではなく「英単語」の始まり・終わりなので、数字を除いた "_ab", "C_D", "eF_" にマッチする。
"(?<=[^\c])\c+(?=[^\c])" とすると行頭の単語以外はマッチする
"(?<=^|[^\c])\c+(?=[^\c])" で行頭の単語を含めてすべての単語にマッチする。期待した動作だ。前方一致指定の中に行頭を表すアンカー "^" が入っているのが若干変な気もするが。

以前の記事の補足説明

以前に秀丸エディタの正規表現について書いた記事で例として挙げた下記の正規表現について、説明不足だったのでここで説明する。
下記の正規表現は "all""see" などの「1文字目≠2文字目かつ2文字目=3文字目の3文字の英単語」にマッチする。

\<([a-z])((?!\1)[a-z])\2\>

・先頭の "\<" と末尾の "\>" はそれぞれ単語の始まり、終わりを示す。これらがあるため、例えば3文字の単語にはマッチするが4文字以上の単語の一部分の3文字("tall" や "seem" など)にはマッチしない。
"([a-z])" はアルファベット小文字の1文字。後方参照のためグループ化してある。
"((?!\1)[a-z])" は後方不一致指定(否定先読み)を使っている。"\1" は前のグループ、つまりこの部分の前にあるアルファベット小文字1文字である。"(?!\1)" はその1文字ではないものが後にあるというアンカー。"[a-z]" はアルファベット小文字1文字であり、後方不一致指定の後にパターンがある場合は、後方不一致指定とそのパターンがAND条件となる。よって、"(?!\1)[a-z]" は「前のアルファベット小文字1文字とは異なるアルファベット小文字1文字」となる。アンカーである後方不一致指定だけではなく後に "[a-z]" があるため1文字とマッチし、後方参照のためにグループ化されている。
"\2" は前のグループ、"(?!\1)[a-z]" にマッチしたアルファベット小文字1文字である。
・以上から、全体としては「1文字目と2文字が異なり、2文字目と3文字が同じである3文字のアルファベット小文字の単語」にマッチする。

参考文献:秀丸エディタヘルプ

参考サイト:
正規表現言語 - クイック リファレンス
Windowsユーザーに教えるLinuxの常識(6):使うほどに良さが分かる正規表現 (2/2) - @IT
正規表現(肯定先読み、否定先読み、肯定戻り読み、否定戻り読み) - satosystemsの日記:
正規表現の先読み・後読みを極める! - あらびき日記

SQLインジェクション その4

関連記事:
SQLインジェクション その1
SQLインジェクション その2
SQLインジェクション その3

SQLインジェクションによるデータの改ざん

SQLインジェクション その1のスクリプトを

author=';UPDATE+books+SET+title%3d'<i>cracked</i>'+WHERE+id%3d'1001'--

というクエリストリングを付けてアクセスすると、データが改ざんされる。
以下のSQLが実行されることが原因である。

SELECT id, title, author, publisher, publisher, date, price
FROM books
WHERE author = '';UPDATE books SET title='<i>cracked</i>' WHERE id='1001'--' ORDER BY id

改ざん後にこのスクリプトで書籍を検索すると、改ざんされた値のHTMLタグが有効になっている。
よって実際の攻撃ではiframe要素やscript要素でマルウェアを感染させたりすることが行われる。

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践 体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ  2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 06, 2013

SQLインジェクション その3

関連記事:
SQLインジェクション その1
SQLインジェクション その2

SQLインジェクションによる認証回避

以下はログイン画面(formタグの部分)とログイン処理をするスクリプトである。スクリプトにはSQL脆弱性がある。

<form action="<スクリプトへのパス>" method="post">
ユーザ名<input type="text" name="id"><br>
パスワード<input type="text" name="pwd"><br>
<input type="submit" value="ログイン">
</form>
session_start();
header('Content-Type: text/html; charset=UTF-8');
$id = @$_POST['id'];	// ユーザID
$pwd = @$_POST['pwd'];	// パスワード

// DBに接続
$con = pg_connect(<接続文字列>);

// SQL文の組み立て
$sql = "SELECT * FROM users WHERE id='{$id}' AND pwd = '{$pwd}'";
$rs = pg_query($con, $sql);
?>
<html>
<body>
<?php
if (pg_num_rows($rs) > 0) {	// SELECTした行が存在する場合ログイン成功
	$_SESSION['id'] = $id;
	echo 'ログイン成功です';
} else {
	echo 'ログイン失敗です';
}
pg_close($con);
?>
</body>
</html>

パスワードがわからなくても、パスワードに

' OR 'a'='a

と入力すると、ログインできてしまう。
原因は、実行されるSQLが下記であることが原因である(見やすいように適宜改行を入れてある)。 ユーザ名(この場合 "abc")が合っていれば検索結果は0件でなくなり、ログイン成功となる。

SELECT * FROM users WHERE id = 'abc' AND pwd = '' OR 'a' = 'a'

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

鋼の錬金術師の最終巻を読み直した

ハガレンは全巻持ってたんだけど、だいぶ前にBOOKOFFに売ってしまった。
少し前にマンガ喫茶に行った時、久しぶりに読み返してみようと思って探したが最終巻だけ無かった。盗まれたのか?
で、TSUTAYAで借りて読んでみた。そしたら疑問が。
エドは自分の扉を対価にしてアルの肉体といっしょに戻ってきたけど、大佐は「自分の扉を対価にしたら戻って来れない」と言っていた。なんでアルにできて大佐にできないんだ?

ネットで調べたら、知恵袋にこんな答えがあった。

鋼の錬金術師最終巻について教えてください。 最後マスタング大佐が、「エドは自分...

なる。納得いく回答だ。一緒に人体練成をして戻ってきたアル・エドだからできた、あの2人は特別だと。
しかし、読んだ人はみんなここのところに疑問など持たずにスッと理解できたんだろうか?

October 03, 2013

連続した日付、時間の文字列を作る秀丸マクロのサブルーチン

CSVファイルや大量のINSERT文などテストデータを作る際に連続した日付や時間を作りたかったので秀丸マクロのサブルーチンを3つ作ってみた。
いずれもformatNumという数値に前ゼロを付けて固定長の文字列を返すサブルーチンを使用している。

incrementDate1は年、月、日を渡してその+1日のYYYY/MM/DD形式の日付文字列を返す。年、月をまたぐことができる。閏年にも対応。
ただ、引数が3つあって使い方や下のincrementDate2よりやや面倒。

// 日付をインクリメント。月跨ぎ、年跨ぎ可。
// 引数1:年(整数)
// 引数2:月(整数)
// 引数3:日(整数)
// 戻り値:YYYY/MM/DD形式の文字列の日付
dateIncrement1:
	##iYear  = ##1;
	##iMonth = ##2;
	##iDay   = ##3;
	
	##iDay = ##iDay + 1;
	
	// 31日ある月
	if (##iMonth == 1 || ##iMonth == 3 || ##iMonth == 5
	    || ##iMonth == 7 || ##iMonth == 8 || ##iMonth == 10
	    || ##iMonth == 12) {
		if (##iDay == 32) {
			##iDay = 1;
			if (##iMonth == 12) {
				##iYear = ##iYear + 1;
				##iMonth = 1;
			} else {
				##iMonth = ##iMonth + 1;
			}
		}
		
	// 30日ある月
	} else if (##iMonth == 2) {
		if (##iDay == 31) {
			##iDay = 1;
			##iMonth = ##iMonth + 1;
		}
	
	// 2月
	} else {
		if (##iYear % 4 == 0) {
			if (##iDay == 30) {
				##iDay = 1;
				##iMonth = ##iMonth + 1;
			}
		} else {
			if (##iDay == 29) {
				##iDay = 1;
				##iMonth = ##iMonth + 1;
			}
		}
	}
	
	call formatNum ##iMonth, 2;
	$$strMonth = $$return;
	call formatNum ##iDay, 2;
	$$strDay = $$return;
	
	$$strDate = str(##iYear) + "/" + $$strMonth + "/" + $$strDay;
	return $$strDate;

incrementDate2は年の日数(1月1日なら1)を渡し、その日のMM/DD形式の日付文字列を返す。年はまたげない。月またぎは可能。
引数は1つで使い方は簡単。

// 日付をインクリメント。月跨ぎ可。
// 引数1:年の日数(整数)。
//         1ならば戻り値は"01/01"、32ならば"02/01"となる。
// 戻り値:MM/DD形式の文字列の日付
dateIncrement2:
	##iDayNum   = ##1;
	
	##uruuBi = 0;	// うるう年の場合、1
	
	if (##iDayNum >= 31) {
		$$strMonth = "01";
		##iDay = ##iDayNum % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (59 + ##uruuBi)) {
		$$strMonth = "02";
		##iDay = (##iDayNum - 31) % (28+ ##uruuBi);
		if (##iDay == 0) { ##iDay = (28+ ##uruuBi); }
	} else if (##iDayNum >= (90 + ##uruuBi)) {
		$$strMonth = "03";
		##iDay = (##iDayNum - (59 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (120 + ##uruuBi)) {
		$$strMonth = "04";
		##iDay = (##iDayNum - (90 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (151 + ##uruuBi)) {
		$$strMonth = "05";
		##iDay = (##iDayNum - (120 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (181 + ##uruuBi)) {
		$$strMonth = "06";
		##iDay = (##iDayNum - (151 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (212 + ##uruuBi)) {
		$$strMonth = "07";
		##iDay = (##iDayNum - (181 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (243 + ##uruuBi)) {
		$$strMonth = "08";
		##iDay = (##iDayNum - (212 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (273 + ##uruuBi)) {
		$$strMonth = "09";
		##iDay = (##iDayNum - (243 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (304 + ##uruuBi)) {
		$$strMonth = "10";
		##iDay = (##iDayNum - (273 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (334 + ##uruuBi)) {
		$$strMonth = "11";
		##iDay = (##iDayNum - (304 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (365 + ##uruuBi)) {
		$$strMonth = "12";
		##iDay = (##iDayNum - (334 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else {	// エラー
		$$strMonth = "99";
		##iDay = 99;
	}
	
	call formatNum ##iDay, 2;
	$$strDay = $$return;
	
	$$strDate = $$strMonth + "/" + $$strDay;
	return $$strDate;

incrementTimeは0時0分を0として日の分数を渡しHH:MM形式の時間の文字列を返す。

// 分をインクリメント
// 引数1:0時0分からの経過時間。分(整数)。
//         0なら戻り値は"00:00"、90ならば"01:30"となる。
// 戻り値:HH:MM形式の時間の文字列
timeIncrement:
	##i  = ##1;
	
	##i = ##i % (60 * 24);
	##iHour = ##i / 60;
	##iMin  = ##i % 60;
	
	call formatNum ##iHour, 2;
	$$strHour = $$return;
	
	call formatNum ##iMin, 2;
	$$strMin = $$return;
	
	$$strTime = $$strHour + ":" + $$strMin;
	return $$strTime;

以下はこれらのサブルーチンを使ってINSERT分をたくさん作るマクロ。

// 日付、時間をインクリメント
disabledraw;
#start = 1;	// 初期値
#end =   15;	// 終了値

// dateIncrement1用パラメータ
#year  = 2013;
#month = 12;
#day   = 25;

// dateIncrement2用パラメータ
#day2 = 55;

// timeIncrement用パラメータ
#min = 290;

#i = #start;
while (#i <= #end) {
	call formatNum #i, 2;
	$num = $$return;

	call dateIncrement1 #year, #month, #day;
	$strDate = $$return;

	call dateIncrement2 #day2;
	$strDate2 = $$return;

	call timeIncrement #min;
	$strTime = $$return;

	insert "INSERT INTO table1 (id, date1, date2, time1, amount) VALUES ";
	insert "(10" + $num + ", '" + $strDate + "', '"
           + $strDate2 + "', '" + $strTime + "', " + str(#i) + ");\n";

	#i = #i + 1;
	
	#day2 = #day2 + 1;	// +1日
	
	#min = #min + 5;	// +5分
	
	#year  = val(midstr($strDate, 0, 4));	// 年を整数で抽出
	#month = val(midstr($strDate, 5, 2));	// 月を整数で抽出
	#day   = val(midstr($strDate, 8, 2));	// 日を整数で抽出
}
enabledraw;
endmacro;


// 数値に前ゼロを付けて指定した桁数の文字列を作成する。
// 引数1:素になる数値
// 引数2:桁数
// 戻り値:引数1に必要ならば前ゼロを付けて引数2の桁数にした文字列。
formatNum:
	##num1 = ##1;
	$$str1 = str(##num1);
	##keta = ##2;
	##len1 = strlen($$str1);
	while (##len1 < ##keta) {
		$$str1 = "0" + $$str1;
		##len1 = ##len1 + 1;
	}
	return $$str1;


// 日付をインクリメント。月跨ぎ、年跨ぎ可。
// 引数1:年(整数)
// 引数2:月(整数)
// 引数3:日(整数)
// 戻り値:YYYY/MM/DD形式の文字列の日付
dateIncrement1:
// 省略

// 日付をインクリメント。月跨ぎ可。
// 引数1:年の日数(整数)。
//         1ならば戻り値は"01/01"、32ならば"02/01"となる。
// 戻り値:MM/DD形式の文字列の日付
dateIncrement2:
// 省略

// 分をインクリメント
// 引数1:0時0分からの経過時間。分(整数)。
//         0なら戻り値は"00:00"、90ならば"01:30"となる。
// 戻り値:HH:MM形式の時間の文字列
timeIncrement:
// 省略

出力は以下のようになる。

INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1001, '2013/12/26', '02/24', '04:50', 1);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1002, '2013/12/27', '02/25', '04:55', 2);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1003, '2013/12/28', '02/26', '05:00', 3);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1004, '2013/12/29', '02/27', '05:05', 4);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1005, '2013/12/30', '02/28', '05:10', 5);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1006, '2013/12/31', '03/01', '05:15', 6);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1007, '2014/01/01', '03/02', '05:20', 7);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1008, '2014/01/02', '03/03', '05:25', 8);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1009, '2014/01/03', '03/04', '05:30', 9);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1010, '2014/01/04', '03/05', '05:35', 10);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1011, '2014/01/05', '03/06', '05:40', 11);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1012, '2014/01/06', '03/07', '05:45', 12);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1013, '2014/01/07', '03/08', '05:50', 13);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1014, '2014/01/08', '03/09', '05:55', 14);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1015, '2014/01/09', '03/10', '06:00', 15);

SQLインジェクション その2

関連記事:
SQLインジェクション その1

UNION SELECTを用いた情報漏洩

SQLインジェクション その1のスクリプトを
「author='+UNION+SELECT+id,pwd,name,addr,NULL,NULL,NULL+FROM+users--」
というクエリストリングを指定して実行すると、書籍の情報が表示されるべきところにID、パスワードなどが表示される。
以下のSQLが実行されることが原因である。

SELECT id, title, author, publisher date, price
FROM books
WHERE author ='' UNION SELECT id,pwd,name,addr,NULL,NULL,NULL FROM users--' ORDER BY id

参考文献:体系的に学ぶWebアプリケーションの作り方 4.4.1 SQLインジェクション

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

October 01, 2013

2013夏アニメ感想

以前の記事:2013夏アニメ

・空の境界
黒幕の手先を倒して終わりなんだ。黒幕は放置?原作小説のアニメ化されていないところで決着付いてるのかな。
作画はさすが劇場公開作品だけあって非常に良かったし、Kalafinaの歌も良かった。それだけで見る価値があった。
あと、式と幹也のCV坂本真綾と鈴村健一がこれがきっかけで(?)結婚するんだなと思うと「イチャついてるなあ」とニヤニヤが止まらなかったww

・サーバント×サービス
面白かった。気楽に楽しめた。
エンディングの歌を茅野愛衣、中原麻衣、豊崎愛生がそれぞれ歌ってたが、中原麻衣が一番良かったな。
豊崎は歌あんまりうまくないだろう。
鯖祭の会場結構近い。行かないけどww


ヤマト2199は途中1回録画に失敗したのでまだ最後まで見ていない。録画に失敗した回はバンダイチャンネルでみるか。
物語シリーズは視聴継続。
もっと色々みてたつもりだけど、意外と少ないなあ。ああ、結構切ったからか。
犬ハサ、ファンタジスタドール、プリズマイリヤ、有頂天家族、銀の匙。
ファンタジスタドール以外は面白くなかったわけじゃないけど色々理由つけて切ってしまった。1つくらい見続けても良かったと、今更ちょっと後悔。

« September 2013 | Main | November 2013 »

April 2017
Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof