2010年8月16日月曜日

Dropboxの仕様

こんにちは、シナプスソフトのプログラマよしだです。
Dropboxの裏側、セキュリティ部分について調べてみました。

C:\Documents and Settings\USERNAME\Application Data\Dropbox\host.db


1行目


16進数表記の文字列。
16進数表記で40バイト。
バイナリ表記で20バイト。

2行目


Base64エンコードされた、Dropbox Folder Location。

C:\Documents and Settings\USERNAME\Application Data\Dropbox\dropbox.db


sqlite形式のデータベースファイル。
テーブルは以下3個。

block_cache
config
file_journal


configテーブル


設定が保存されてる。
1行でKeyとValue。

CREATE TABLE config (
id INTEGER PRIMARY KEY,
key TEXT NOT NULL UNIQUE,
value TEXT
);


config.keyカラム


設定項目名。

sqlite> select id, key from config;
1|schema_version
2|last_revision
3|root_ns
4|host_id
5|email
6|ns_p2p_key_map
7|recently_changed3


config.valueカラム


PythonのPickleでシリアライズされたものをBase64でエンコードしてある。
デコードするPythonプログラム。

#!/usr/local/bin/python
# set encoding=utf8

import sys, base64, pickle

def main():
input = sys.argv[1]
input = base64.b64decode(input)
print pickle.loads(input)

def usage():
print 'Usage: %s encoded_str' % sys.argv[0]

if __name__=='__main__':
if (len(sys.argv)==2):
main()
else:
usage()


config.value (WHERE key='schema_version')


型 : int
値 : 6

config.value (WHERE key='last_revision')


型 : Dict
値 : {ルートNS: 見せられないlong}
ファイルを追加したりすると増えるのかもしれない。
リビジョンにしては数がでか過ぎる。
全ユーザ共通?とも考えたけど、コミットしても値は変わらない。

config.value (WHERE key='root_ns')


型 : long
いろんな所でキーとして使われている。
見せていいものか悪いものか解らないから、とりあえず「ルートNS」と表現する。

config.value (WHERE key='host_id')


型 : str
16進数表記で32バイト。
MD5っぽい。

config.value (WHERE key='email')


型 : str
メールアドレス。

config.value (WHERE key='ns_p2p_key_map')


型 : Dict
値 : {ルートNS: (u'-----BEGIN CERTIFICATE-----\n見せられない\n-----END CERTIFICATE-----\n', u'-----BEGIN RSA PRIVATE KEY-----\n見せられない\n-----END RSA PRIVATE KEY-----\n', '見せられない')}
1つ目のstrは、RSA公開鍵。
2つ目のstrは、RSA秘密鍵。
3つ目のstrは、パスフレーズ?初期ベクトル?
MD5っぽい。
詳細不明。

公開鍵と秘密鍵はペアになっていた。
この使い方、公開鍵暗号方式として間違ってる気がする。
このペア、サーバ側にも置いてあるはず。
こんな使い方をするなら、Diffie-Hellmanとかでその都度鍵交換してもいいような気がする。
けど、それだと1回の通信の為に2回接続しなきゃいけない。
多分そこが問題だったから、こういう奇妙な形になったんだと予想。

サーバにサーバ秘密鍵とクライアント公開鍵、クライアントにクライアント秘密鍵とサーバ公開鍵、を入れれば、RSAを使って1回の通信で更にセキュアになる。
サーバとクライアントでRSAキーを分けない理由は不明。
多分面倒だったんだと思う。
再発行出来なくなるし。

クライアントを再インストールしたら、このRSAキーが復活してた。
もしかしたら、サーバから送られてきてるのかもしれない。

ペアになっている事を確認するPHPのプログラム。

<?php
$public = '-----BEGIN CERTIFICATE-----
見せられない
-----END CERTIFICATE-----';

$private = '-----BEGIN RSA PRIVATE KEY-----
見せられない
-----END RSA PRIVATE KEY-----';

// 文字列からキーリソース生成
$public = openssl_pkey_get_public($public);
$private = openssl_pkey_get_private($private);

// 暗号化するデータ
$data = 'あいうえお!';

// public keyで暗号化
openssl_seal($data, $sealed_data, $env_keys, array($public));

// private keyで復号化
openssl_open($sealed_data, $open_data, $env_keys[0], $private);

echo $open_data."\n";


公開鍵の詳細。
どうやらこのキーはクライアント側で生成してるらしい。

$ openssl x509 -in public.crt -text
Certificate:
Data:
Version: 4 (0x3)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=dropbox-client
Validity
Not Before: Jan 1 00:00:00 1970 GMT
Not After : Jul 18 17:14:19 2030 GMT
Subject: CN=dropbox-client
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1536 bit)
Modulus (1536 bit):
見せられない
Exponent: 見せられない (見せられない)
Signature Algorithm: sha256WithRSAEncryption
見せられない
-----BEGIN CERTIFICATE-----
見せられない
-----END CERTIFICATE-----


$ openssl asn1parse -in public.crt
0:d=0 hl=4 l= 548 cons: SEQUENCE
4:d=1 hl=4 l= 333 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :03
13:d=2 hl=2 l= 1 prim: INTEGER :01
16:d=2 hl=2 l= 13 cons: SEQUENCE
18:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
29:d=3 hl=2 l= 0 prim: NULL
31:d=2 hl=2 l= 25 cons: SEQUENCE
33:d=3 hl=2 l= 23 cons: SET
35:d=4 hl=2 l= 21 cons: SEQUENCE
37:d=5 hl=2 l= 3 prim: OBJECT :commonName
42:d=5 hl=2 l= 14 prim: PRINTABLESTRING :dropbox-client
58:d=2 hl=2 l= 30 cons: SEQUENCE
60:d=3 hl=2 l= 13 prim: UTCTIME :700101000000Z
75:d=3 hl=2 l= 13 prim: UTCTIME :300718171419Z
90:d=2 hl=2 l= 25 cons: SEQUENCE
92:d=3 hl=2 l= 23 cons: SET
94:d=4 hl=2 l= 21 cons: SEQUENCE
96:d=5 hl=2 l= 3 prim: OBJECT :commonName
101:d=5 hl=2 l= 14 prim: PRINTABLESTRING :dropbox-client
117:d=2 hl=3 l= 221 cons: SEQUENCE
120:d=3 hl=2 l= 13 cons: SEQUENCE
122:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
133:d=4 hl=2 l= 0 prim: NULL
135:d=3 hl=3 l= 203 prim: BIT STRING
341:d=1 hl=2 l= 13 cons: SEQUENCE
343:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
354:d=2 hl=2 l= 0 prim: NULL
356:d=1 hl=3 l= 193 prim: BIT STRING


config.value (WHERE key='recently_changed3')


型 : list
値 : [(u'ルートNS:/Public/How to use the Public folder.rtf', 0), (u'ルートNS:/Photos/Sample Album/Pensive Parakeet.jpg', 0), (u'ルートNS:/Photos/Sample Album/Costa Rican Frog.jpg', 0), (u'ルートNS:/Photos/Sample Album/Boston City Flow.jpg', 0), (u'ルートNS:/Photos/How to use the Photos folder.rtf', 0)]
file_journal.server_pathが5個入ってた。
keyの通り、最近更新されたファイル。
新しいものが先頭に追加されるらしい。
タプル2つ目はリビジョンかと思ったけど違うらしい。
ファイルを追加・更新したら0。
ファイルを削除したらNULL。

block_cacheテーブル


ファイルのキャッシュ(ログ)。
ファイルを追加・更新すると1行増える。
ファイルを削除しても、追加も削除もされない。

CREATE TABLE block_cache (
id INTEGER PRIMARY KEY,
hash VARCHAR(43) NOT NULL UNIQUE,
sig TEXT,
size INT,
delete_after INT,
needed_for INT
);


block_cache.hashカラム


43文字のASCII文字列。
file_journal.active_blocklistと一致。
Base64エンコードされた文字列かと思いきや、なんとなく違う。
多分プログラムがユニークになるように生成してる。

block_cache.sigカラム


Base64エンコードされた文字列。
ファイルのサイズに応じて長くなるらしい。
keyから察するに、署名だと思われるが、長さが違う気がする。
最初の16文字(バイナリ表記で12バイト)は全ての行で一致していた。

block_cache.sizeカラム


ファイルのサイズ。(バイト数)
コミット時、file_journal.active_sizeと一致。

block_cache.delete_afterカラム


全部0。
詳細不明。

block_cache.needed_forカラム


全部NULL。
詳細不明。

file_journalテーブル


ファイル一覧。
ファイルを追加すると、このテーブルに1行追加される。
ファイルを削除すると、このテーブルから物理削除される。

CREATE TABLE file_journal (
id INTEGER PRIMARY KEY,
server_path TEXT NOT NULL UNIQUE,
active_server_path TEXT,
active_blocklist TEXT,
active_mtime INT,
active_size INT,
active_sjid INT UNIQUE,
active_dir INT,
active_attrs TEXT,
updated_server_path TEXT,
updated_blocklist TEXT,
updated_mtime INT,
updated_size INT,
updated_sjid INT,
updated_dir INT,
updated_attrs TEXT,
on_disk TINYINT
);


file_journal.server_pathカラム


値 : ルートNS:/photos/how to use the photos folder.rtf
日本語はそのまま日本語で入っていた。

file_journal.active_server_pathカラム


file_journal.server_pathと一致。

file_journal.active_blocklistカラム


block_cache.hashと一致。
file_journal.server_pathがディレクトリの場合は空文字列。

file_journal.active_mtimeカラム


ファイルを最後に更新(コミット?)した時間。
Unixtime。

file_journal.active_sizeカラム


ファイルのサイズ。(バイト数)
最終更新のblock_cache.sizeと一致。
file_journal.active_dirが1の場合は0。

file_journal.active_sjidカラム


詳細不明。
UNIQUEのint。

file_journal.active_dirカラム


ディレクトリだったら1。
ファイルだったら0。

file_journal.active_attrsカラム


config.valueと同様、PickleでシリアライズされたものをBase64でエンコードしたもの。
デフォルトで入ってたファイルは{}で、自分で追加したファイルはNULLだった。
LinuxとかMacとかで使った時に、パーミッションとか属性とかが入るのかも知れない。

file_journal.updated_server_pathカラム


詳細不明。
全部NULLだった。

file_journal.updated_blocklistカラム


詳細不明。
全部NULLだった。

file_journal.updated_mtimeカラム


詳細不明。
全部NULLだった。

file_journal.updated_sizeカラム


詳細不明。
全部NULLだった。

file_journal.updated_sjidカラム


詳細不明。
全部NULLだった。

file_journal.updated_dirカラム


詳細不明。
全部NULLだった。

file_journal.updated_attrsカラム


詳細不明。
全部NULLだった。

file_journal.on_diskカラム


詳細不明。
全部1だった。

登録からセキュアな通信までの流れ(妄想)


登録


・クライアントソフトインストール。
・RSA生成。
・ユーザ情報・RSA登録。

入力するemail/passは人間(ブラウザ)認証用。
バックグラウンドで生成されるRSAはデータ暗号化用。

通信


・RSA公開鍵でデータ暗号化。
・送る。
・RSA秘密鍵でデータ復号化。

パケットを見る限り、443ポートとやりとりしてる。
暗号化したデータをHTTPSで流してる気がする。(2重で暗号化)
RSA-encryption over HTTP over SSL。
HTTP(アプリケーション層)の上に仮想的なレイヤー(Dropboxの独自仕様)を作ってその上で動かす。
レイヤーと言える程きっちりしたものではないかもしれないけど。
これは前に作ったOAuth Encryptionに似てる。

データ部分は2重で暗号化されているとして、何かユニークなIDを普通のHTTPSで見れる状態で送ってるはず。
考えられるユニークIDは、email位しかなさそう。

2010年8月9日月曜日

[PHP]関数にわたす引数の順番を調べる

こんにちは。
シナプスソフトのおばさんプログラマ、略しておばグラマのかわせです。
おばグラマとは言ってもプログラミング歴は社内で一番短い、超下っ端です。
この「SynapseLabo」は株式会社シナプスソフトのエンジニアによる様々なアウトプットの場として活用していきます。どうぞよろしく。

もっと高度な投稿は他のエンジニアにまかせて(ぉ、私は軽めの内容を投稿していきます。
で、タイトルの件。

PHPユーザのみなさん、関数にわたす引数の順番を覚えてますでしょうか? strpos() とか。ヘイスタック、ニードル?ニードル、ヘイスタックだっけ?おばグラマは記憶力もすっかり衰えてるので、全く覚えられません。
実は毎回マニュアルのお世話になっていましたが、 --rf オプションでコマンドラインから表示させる方法がわかったので便利に使っています。

$ php --rf strpos
Function [ function strpos ] {
- Parameters [3] {
Parameter #0 [ $haystack ]
Parameter #1 [ $needle ]
Parameter #2 [ $offset ]
}
}