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位しかなさそう。