2012-12-16

[windows] ESD インストール ファイル を再登録する

C:\ESD\Windows が存在するのにシステムから認識されなくなってしまった場合、以下のレジストリ断片を結合すると再度認識されるようになりました。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\WebSetup]
"ImageIndex"=dword:00000001
"ImageLocation"="C:\\ESD\\Windows\\sources\\install.esd"
"MediaLocation"="C:\\ESD\\Windows"

このまま foobar.reg などと保存して結合できます。

Windows ESD インストールファイル C:\ESD\Windows はインストールディスクの内容をコピーしたり、Windows8-Setup.exe を実行後プロダクトキーを入力して再ダウンロードすることで再取得できます。

Windows8-Setup.exe は自動的に既存の C:\ESD\Windows とレジストリの WebSetup キーをクリアするようなので既に利用可能な Windows ESD インストールファイルを壊したくない人は実行に注意してください。

関連情報
プロダクト キーのみを使用して Windows 8 にアップグレードする方法 - Microsoft Windows
hidekiy blog: Windows 8 のリフレッシュ機能を試してみました

2012-12-08

[Node.js] man-in-the-middle 攻撃にご用心

Node.js は基本的にはサーバーサイドで使うものなので、まともなホスティング業者のサービスを利用する限りは通信環境についてそこまでセキュリティリスクは高くないと思います。しかしナントカ API に https でアクセスしているから安全に違いないと信じ込むのは考え物で、

HTTPS Node.js Manual & Documentation - https.request(options, callback)
rejectUnauthorized: If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent. Default false.
を見ると分かりますが、Node.js v0.8.18 時点で未だに rejectUnauthorized のデフォルト値が false なのでデフォルトのまま使うと中間者攻撃の余地があります。

証明書の検証をしない SSL なんて存在しない方がましだと思うので、きちんと検証できているかどうかを必ず確認しましょう。例えば https://www.l.google.com/ など接続はできて証明書の検証に失敗するアドレスにリクエストして、エラーになることを確認すると簡単だと思います。さらに心配性な方は Node.js が正当なルート証明書を使っているかどうかも調べておきましょう。

[windows] BitLocker を有効にしてみました

Windows 8 Pro にしたら一緒に付いてきた、保護されないアクセスからデータを守ってくれるらしい BitLocker を有効にしてみました。

保護されないアクセスとは例えば以下のようなアクセス許可が正常に適用されない状況です。
  • LiveCD で起動して USB メモリにデータをコピーして持って帰る
  • HDD を取り外して持ち出し、別の OS からアクセス許可を無視してオフラインアクセスする
  • オフラインで OS にルートキットを秘密裏に書き込んでおきパスワードを盗む

暗号化の処理について、作業を中断することなくバックグラウンドで処理が進行し、暗号化が途中まで完了しているという状態が許容されているようです (暗号化進行中にシャットダウンしても大丈夫)。暗号化も Low-Priority I/O でおしとやかに処理されるのでとても良くできている印象を受けました。

BitLocker による保護とは意図したとおりの OS のみがディスクにアクセスするよう制限することにあるので、TPM 付きのマシンなら同じマシンで起動していることを自動的に確認できるので起動時のパスワード入力は無しにできます (PIN も設定可能)。TPM 無しの場合でも USB メモリもしくはパスワードでも BitLocker を有効にできます (要ポリシー変更)

また起動時に起動用のパスワードを打ち込めば必ずフルアクセス出来るようになるのではなく、正常に保護されない恐れのあるアクセス (正規の Windows 以外からアクセスされる場合) の時は回復キー (数字48桁) を要求されます。この理由はフルコントロール可能な回復キーは管理者が管理し、標準ユーザー権限のユーザーは正規の認証を経ることになっているからだと思います。

この仕組みはデータが複数個に増えるわけではなく、データを暗号化する鍵を別々の方法で暗号化して保管することで実現されています。内部で実際にデータを暗号化しているマスターキーは目に見える形では表示されません。

WinRE を使いたい時も回復キーを要求され少しめんどくさいですが、あらかじめ Windows 内で「保護の中断」を管理者権限で実行しておくと回復キー不要でアクセスできます。BIOS のアップデートも回復キーを要求されるはずなので、行う際は同様に保護の中断を行っておくとスムーズに行えると思います。

「保護の中断」と「BitLocker を無効にする」の違いは、保護の中断の場合はディスクの暗号化はそのままで、鍵が誰でも使用可能な状態で書き込まれるということで、次の Windows 起動時に自動的にパスワード不要の鍵は削除され保護が再開します。BitLocker を無効にするを行った場合は全てのセクタの暗号化が解除され BitLocker を有効にする前の何も保護されていない状態に戻ります。解除には使用中の領域全体への読み取りと書き込みアクセスを伴うので少し時間がかかります。

スリープ状態の場合は BitLocker では保護されませんが休止状態ならば保護されます。スリープ状態のパソコンからメモリダンプを取ることができれば鍵を盗むことができるかもしれません。休止状態の場合は保護されるので、自動的に休止状態に移行するように設定することもより安全性を高めるために役に立つかもしれません。

関連情報
BitLocker ドライブ暗号化でファイルを保護する
BitLocker に関してよく寄せられる質問 (FAQ)

[windows] Windows 回復環境 (WinRE) を修理する

Windows 本体が起動しない場合は WinRE もしくはインストールメディアから利用可能なスタートアップ修復によって自動修復することができます。しかしインストール済みの WinRE 自体が起動しなくなった場合は自分で修理する必要があるようです。

winre.win の入ったパーティーションの開始位置をずらした場合や、BCD (ブート構成データ) から WinRE 自体のエントリをうっかり消去してしまった場合は、bootrec /rebuildbcd を使っても自動修復できません。そこで bcdedit を使ってちまちま再設定するのではなく reagentc を使うと以下の手順で簡単に再設定出来ることが分かりました。
  1. winre.win というイメージをインストールディスクから抽出するか設置先から救出してきて、C:\Windows\System32\Recovery に戻します
  2. reagentc /enable というコマンドで BCD エントリとディスクイメージの設置 (winre.win のコピーではなく移動w) が完了します
  3. 内部状態 ReAgent.xml には設定していると記録されていて、実際には BCD エントリが存在していない矛盾した状態になっているとエラーが出ます。その場合は ReAgent.xml を初期化すると無事動きます。
    初期状態の ReAgent.xml は C:\Windows\WinSxS 内のどこかにあるようなので検索してみてください。手元の Windows 8 では C:\Windows\WinSxS\x86_microsoft-windows-winre-recoveryagent_... みたいな所にありました。
Windows 回復環境の現在の設定状況については reagentc /info で調べることができ、WinRE の無効化は reagentc /disable で行えます。

壊れる原因について
Windows において MBR 経由の従来型のブート方式ではパーティーションごとの UUID 的な何かではなく、ディスク内でのパーティーションの開始位置によってどの OS を起動するべきかを BCD 内で指定しています。

よって gparted などで C:\Windows の入った領域の開始位置をずらす操作 (右側への拡張と縮小ではなく、左側への移動) を行うとブート不能になり、スタートアップ修復が必要になります。

スタートアップ修復を行おうにも何もブートしない場合はインストールメディアから起動すればスタートアップ修復が行えます。

スタートアップ修復に頼らずに BCD を修復するには
Windows に関しては WinRE 内に収録されている bootrec /rebuildbcd でおそらく復活します。

手動で直したい場合には BCD を bcdedit で更新すると直ります。具体的には以下の手順で行えます。
  1. WinRE のコンソールを開きます。
  2. bcdedit /enum osloader で identifier を確認します。device の所が Unknown になっているエントリが壊れています。
  3. diskpart を使って list volume などでドライブレターを確認し、dir /a c:\ などで内容を確認します。
  4. bcdedit {identifier} /set device partition=C: などと OS の入っているドライブを指定して正しいパーティーション開始位置をセットします

関連情報
スタートアップ修復 - Microsoft Windows
GParted FAQ - 14: After resizing my Windows 7 or Vista partition, my computer won't boot. How can I fix this?
win7 システムの修復
Installing WinRE to hard disk - MSFN Forum

2012-12-07

[Express] req.next は場合により壊れている

Express で何か作る際に、探検好きな人はレスポンスオブジェクト res には リクエストオブジェクト req が入っていて、req には次に呼び出すべき関数に goto する関数 next が入っていることをご存知かと思います。

この req.next について、値はミドルウェア中では正しくてもリクエストルーター中ではちょっと間違っている (リクエストルーター自体を skip する next になっている) ように見えるので、保守的な方は常に引数として渡される方の next を使うようにしましょう。

var express = require('express'),
    app = express();

app.use(function (req, res, next) {
    console.log('middleware:', req.next === next, res.req.next === next);
    next();
});

app.get('/', function (req, res, next) {
    console.log('router:', req.next === next, res.req.next === next);
    // req.next(); doesn't work correctly.
    next();
}, function (req, res, next) {
    res.send('ok');
});

app.listen(3000);

/*
middleware: true true
router: false false
*/

[Express] エラーの伝搬と処理について

Express でリクエストを処理しているとき、発生したエラーを適切に処理する仕組みについてドキュメントではあまり詳しく取り扱われていませんが、少し理解しておくともっと楽しくコードが書けると思います。

express において設定するコールバック関数は受け取る引数の数で勝手に分類され、3個以下の場合は正常系、4個の場合は異常系となります。正常系のとき引数リストは req, res, next で、異常系のときは err, req, res, next です。

正確には connect の範疇となるミドルウェアも、express に内蔵されているリクエストルーターと同じような振る舞いをするように作られています。

正常系でエラーが発生した場合はコールバック関数と同期的に発生したエラーは throw でも良いですが非同期的に発生したエラーは next(err) として伝搬させる必要があります。区別が面倒な方はすべて next(err) と書くことにしてもよいと思います。

異常系のコールバック関数は必ずエラーに対処しないといけないわけではなく、next(err) を呼び出してエラーを伝搬することもできます。

全てのミドルウエアを処理してもエラーを処理しきれなかったとき connect によりデフォルトのエラー画面が表示されます。あらかじめミドルウェアの最後の方に errorHandler を入れておくと素敵なエラーページを出せます。connect のモジュールを使っているのにタイトルが Express となる理由は express の lib/express.js を見ると分かります。

以下のコードで色々試してみてください。

/*
Q. 以下のアドレスのレスポンスはどうなるでしょうか
http://localhost:3000/
http://localhost:3000/?error=1
http://localhost:3000/?error=1&catch=1
http://localhost:3000/?pass=1
*/

process.env.DEBUG = '*';

var express = require('express'),
    app = express();

// middleware
app.use(express.logger());
app.use(function middlewareA(req, res, next) {
    req.trace = [0];
    next();
});
app.use(app.router);
app.use(function middlewareB(err, req, res, next) {
    req.trace.push(5);
    res.send({msg:'final: error', trace: req.trace, error: String(err)});
});
app.use(function middlewareC(req, res, next) {
    req.trace.push(6);
    res.send({msg:'final: not error', trace: req.trace});
});

// request router
app.get('/', function (req, res, next) {
    req.trace.push(1);
    next();
});
app.get('/', function (req, res, next) {
    req.trace.push(2);

    if (req.param('error')) {
        next(new Error('something happened'));
    } else {
        next();
    }
}, function (req, res, next) {
    req.trace.push(3);

    if (req.param('pass')) {
        next();
    } else {
        res.send({msg:'ok', trace: req.trace});
    }
}, function (err, req, res, next) {
    req.trace.push(4);

    if (req.param('catch')) {
        res.send({msg:'caught error', trace: req.trace, error: String(err)});
    } else {
        next(err);
    }
});

app.listen(3000);

リンク
node.js - Error handling principles for NodeJS + Express apps? - Stack Overflow
express/test/app.routes.error.js

[Express] static と directory を組み合わせて使う

正確には Express というより connect についての話です。staticdirectory を使ってあるディレクトリの中を全て公開するような設定の仕方について考えます。

このように書くと http://localhost:3000/some/directory への要求は static によって http://localhost:3000/some/directory/ へのリダイレクトになり、index.html があればその内容が static により返され、無ければ次のミドルウェアに fallback して directory によるファイル一覧が返ります。

Apache 風の動作はこちらが該当します。

var express = require('express'),
    app = express();

app.use(express.static('/path_to_dir'));
app.use(express.directory('/path_to_dir'));
app.listen(3000);

一方でこのように先ほどとは逆順でミドルウェアを追加すると、http://localhost:3000/some/directory と http://localhost:3000/some/directory/ の返答は必ず directory によるファイル一覧になり、index.html があっても返りません。

ソースツリーをそのまま公開したい場合など index.html ではなくファイル一覧が出てほしい場合はこっちのコードが役に立つかもしれません。

var express = require('express'),
    app = express();

app.use(express.directory('/path_to_dir'));
app.use(express.static('/path_to_dir'));
app.listen(3000);

static について、ディレクトリが trailing slash 付きでリクエストされたとき自動的に index.html を探す動作は send というライブラリが行っています。

水面下での動作が気になる人は express, connect, send で活用されている debug によるデバッグ出力を環境変数 DEBUG を * に設定することで有効にできます。

$ env DEBUG='*' node app.js

2012-12-02

[facebook] 空の写真アルバムを作成する方法

または、簡易アップローダーを復活させる方法について。

Facebook で例えば携帯アップロード内の写真を別のアルバムを新しく作って移動させたいとき、移動先のアルバムが既に存在している必要があって、さらに空のアルバムを作る方法が存在しないように見えるので詰みます。

ユーザーを信頼していない Facebook の UI にイライラしていた方も今日からは以下の方法で空のアルバムを大量生産しましょう。

方法
  1. Flash Player を一時的に無効にする。
    chrome://plugins (Chrome), アドオンマネージャー (Firefox), アドオンの管理 (Internet Explorer) から一時的に無効にできます
  2. [+写真を追加] ボタンを押すといつもと違う「簡易アップローダー」の画面が出るのでアルバムを作成する
  3. 写真を5枚までアップロードできるページが開くが何もしなくてよい (この画面の時点で既に空のアルバムが出来ています)

関連情報
アルバム間で写真を移動するにはどうすればよいですか。 | Facebookヘルプセンター

ノート PC のバッテリーを安全に 0% まで使い切る方法

バッテリーの真の状態とコントローラーが想定している状態にずれが生じると残量パーセント表示が少なめに出るようになる (0% 表示になっても本当はもう少し使える) ことが起こりえます。

この状況を何とかするためにはバッテリをいったん使い切ってもう一度フル充電すると良いのですが、ディスクをマウントしたまま電源断を起こすのは心苦しく、省電力設定をいじるのも面倒くさいので、安全に電池を使い切る方法について考えてみました。

方法
いったん再起動して、BIOSセットアップやブートメニューを出してその画面で放置する。

解説
この画面ではまだストレージは読み書きモードでマウントされておらず、省電力機能も有効ではないので安全確実に残量 0% 表示のあと勝手に電源が落ちるところまで進みます。電源が落ちた後は過放電を防ぐために速やかに充電してください。