HTTPSのお勉強
どうも用語が散乱してて、分かりにくかった。
公開鍵、秘密鍵、共通鍵、サーバー証明書、認証局、その他もろもろ。
1つにつなげてみたい。
HTTPSとは
HTTPSとは、SSLという公開鍵暗号方式(PKI)でサーバー/クライアント間の通信内容を暗号化する通信方式のこと。
または、SSLという公開鍵暗号方式(PKI)と共通鍵でサーバー/クライアント間の通信内容を暗号化する通信方式のこと。
後者だと思うけど、はっきりそうだと言っている文章が見つからない。
SSLによる暗号化通信
これで片方向、つまりクライアントからサーバーへの送信内容を暗号化できる。
サーバーからクライアントへの送信内容は暗号化できない。
なぜなら、公開鍵は誰でも手に入れられるから、いくらサーバーが秘密鍵で暗号化したところで誰でも復号できてしまうし、サーバーが公開鍵で暗号化したって、サーバー以外に誰も復号できなくて、意味がない。
双方向、つまりサーバーからクライアントへの送信内容も暗号化するには、共通鍵も使う必要がある。(クライアントも公開鍵と秘密鍵を作って、公開鍵を送ったらいいようなもんだけど、そういうことはあまりしない様子)
それは次のように行なわれる。
- サーバー側にSSLの公開鍵と秘密鍵を置く
- サーバーはクライアントに公開鍵を送る
- クライアントは共通鍵を作る
- クライアントは共通鍵を公開鍵で暗号化する
- サーバーはクライアントから送られた共通鍵を秘密鍵で復号化する
- これ以降、サーバーとクライアントは送信内容を共通鍵で暗号化し、受信内容を共通鍵で復号化する
共通鍵を使う理由として、よく「秘密鍵での復号化は負荷が高いので、負荷の低い共通鍵で通信する」と言われているが、共通鍵はなにもそれだけのために使うわけじゃなくて、共通鍵によって初めて双方向の送信内容を暗号化できるようになる。それが書いていない文章があって、とても混乱した。だって、それじゃあ、僕の口座を表示した瞬間残高がダダ漏れってことになる。
実在証明のためにSSLを利用する
ここから、暗号の話というよりは、社会的な信頼関係の話になる。
通信内容を内緒にするには、あれでよかった。
でも、通信先のサーバーが信用できるとは限らない。よく似せられたフィッシングサイトかもしれない。
そこで、SSLを使って、そのサーバーが信用できる主体に運営されているか確かめるための仕組みができた。これが実在証明といわれている。
実在証明にはサーバー証明書を使う。
サーバー証明書は、サーバーの公開鍵のメッセージダイジェストとサーバー運営者の情報に認証局が署名したものといわれているが、認証局とは?署名とは?
認証局は、要するになんだか知らないけど、とてつもなく信用できるすっごい主体のこと。ベリサインとか、そういうの。国営もありえる。
署名は、要するに暗号化のこと。いきなり署名とか言われても困る。
認証局は自分用のSSL公開鍵/秘密鍵を持っていて、我々を信用するにふさわしい人物/組織なのか審査したうえで問題なければ、我々のサーバーの公開鍵のメッセージダイジェストと我々の情報をまとめて認証局の秘密鍵で暗号化してくれる。この暗号化でできたファイルがサーバー証明書だ。
第三者にサーバー証明書が渡らない限り、すっごく信用できる認証局の公開鍵で復号したメッセージダイジェストと同じメッセージダイジェストの公開鍵を持っているサーバーは信用していいことにするという仮定が実在証明ということみたい。
ちまたのブラウザには認証局の公開鍵(よく認証局証明書とか言われる)が初めから組み込まれていて、いちいち探さなくていい。
本当は誰でも認証局になれるし、その認証局の公開鍵をあとからブラウザにインストールすることもできるけど、おれが認証局だ!と言ったところで、信用してもらえるかどうかはまた別の話。
実在証明の手続きをまとめると、次のようになる。
- サーバー運営者は、SSLの公開鍵と秘密鍵を用意する
- サーバー運営者は認証局に公開鍵のメッセージダイジェストとサーバー運営者の情報(個人情報とか会社情報)を預けて、自分を審査するように頼む
- 認証局はサーバー運営者が信用するにふさわしい人物/組織なのか審査して、問題なければ次に進む
- 認証局はサーバーの運営者から預かった公開鍵のメッセージダイジェストとサーバー運営者の情報をまとめて認証局の秘密鍵で暗号化して、サーバー証明書にする。
- 認証局は、サーバー証明書をすっごく厳重な郵便かなにかでサーバー運営者に渡す
- サーバー運営者は、サーバーにサーバー証明書と公開鍵と秘密鍵を置く
- クライアントがサーバーにアクセスする
- サーバーはクライアントにサーバー証明書と公開鍵を送る
- クライアントはサーバーの公開鍵のメッセージダイジェストを作る
- クライアントはあらかじめ持っていた認証局の公開鍵でサーバー証明書を復号する
- サーバー証明書を復号して得たメッセージダイジェストが、サーバーから送られてきた公開鍵で作ったメッセージダイジェストと一致すれば、クライアントはそのサーバーを信用していいと判断する
あとはSSLによる暗号化通信だ。
- クライアントは共通鍵を作る
- クライアントはサーバーの公開鍵で共通鍵を暗号化して、サーバーに送る
- サーバーは共通鍵を秘密鍵で復号する
- 以降、共通鍵で双方向の暗号化通信をする
というわけで、HTTPSというか、むしろSSLのお勉強をした。
SSLは通信の暗号化と、実在証明に使われているのだった。
CSRF対策がないフォームへの投稿は、投稿者を特定できなくて困る
投稿内容が犯行予告だったりすると困る。(最近ニュースになってる一件がCSRFがらみなのかは知らない)
ちょっと調べて考えてみたところ、条件がよくても投稿可能だったひとを絞り込めるのは2人までで、特定は無理じゃないか?という結論に至った。
リファラーのホストにXSSでCSRFページが仕込まれていたら?とか、無線LANをただ乗りされたら?とか、考え始めるときりがないけど「CSRF対策がないフォームへの投稿者を特定できるか?」という問いのためには、この2人について考えれば十分だと思う。
リファラーがあるなら2で決まりじゃん?と思うけど、決まらないんだなこれが。
リファラーを根拠に投稿者を特定しようとする限り、手書きでウソのリファラーをつけたHTTPヘッダをsocketで送れば、ウソのリファラーが指すURIの持ち主を投稿者に仕立てられてしまう。
この点をはっきりさせるには、どうすればいいんだろう。
- 法令で、レンタルサーバー事業者に、ユーザーのドキュメントルート配下の全ファイルについて、一定期間さかのぼれるSubversion的な記録を義務づける。
- 法令で、ISPに個人回線のInbound Port 80 Blockingを義務づける。
決定打って思いつかないけど、こういうことって警察庁や総務省のえらいひとが考えてそうだなあ。
残念だけど、インターネットってもう少し不自由でないといけないのかも。
vimのメモ
愛用のvimrc
前にも書いたけど、1ページにまとめたい。
colorscheme delek nohlsearch syntax on set nolist set nocursorline set guicursor=a:blinkon0 set guifont=Osaka−等幅:h12 set lines=46 set columns=80 set nobackup set fileformat=unix set laststatus=2 set statusline=%<%f\ %m%r%h%w%{'['.(&fenc!=''?&fenc:&enc).']['.&ff.']'}%=%l,%c%V%8P set tabstop=2 expandtab shiftwidth=2 set autoindent autocmd FileType python setl textwidth=80 autocmd FileType python setl smartindent cinwords=if,elif,else,for,while,try,except,finally,def,class autocmd FileType html setl autoindent autocmd FileType html setl smartindent
qbuf.vim
qbuf.vimは、複数のファイルを開いているとき、編集するファイルをホットキーで切り替えられるようにするプラグイン。
ホットキー>jkでファイル選択>Enter
ここでダウンロード
http://www.vim.org/scripts/script.php?script_id=1910
~/.vim/plugins/qbuf.vimに置く。
デフォルトのホットキーはF4だけど、好きなキーを設定できる。
自分はセミコロンを割り当てている。
let g:qb_hotkey = ";"
編集中のスクリプトを実行するホットキー
どこで見つけたんだか、vimrcに次を追記するとCtrl+Pで編集中のスクリプトを実行できる。
function! s:Exec() exe "!" . &ft . " %" :endfunction command! Exec call <SID>Exec() map <silent> <C-P> :call <SID>Exec()<CR>
さくらのレンタルサーバーでvimを使う。
インストール
wget ftp://ftp.vim.org/pub/vim/unix/vim-7.2.tar.bz2 wget ftp://ftp.vim.org/pub/vim/extra/vim-7.2-extra.tar.gz wget ftp://ftp.vim.org/pub/vim/extra/vim-7.2-lang.tar.gz tar jxvf vim-7.2.tar.bz2 tar zxvf vim-7.2-extra.tar.gz tar zxvf vim-7.2-lang.tar.gz cd vim72 mkdir patch cd patch curl -O 'ftp://ftp.vim.org/pub/vim/patches/7.2/7.2.[001-446]' cd .. cat patch/7.2.* | patch -p0 ./configure --enable-multibyte --enable-xim --enable-fontset --with-features=big --prefix=$HOME/vim make; make install
バックスペースやCtrl+Hが効かないとき
どこで見つけたんだか、.vimrcに次を追加するといい。
set backspace=indent,eol,start
Pythonでアクセスカウンターを作る
なぜかカウントが1に戻ってしまったり、キリ番報告が3人もいるとかいうミステリアスなカウンターを作らないためには、排他制御のおさらいが必要だった。
わりとマトモなカウンターになったと思う。
ものすごく分かりやすかった排他制御の解説。
CGIやDBのロックと同時実行制御: CANO-Lab
http://jn.swee.to/cano/lock/index.shtml
#!/usr/local/bin/python #coding: utf-8 import fcntl, os, sys, time COUNTER = os.path.join(os.path.dirname(os.path.abspath(__file__)), "counter.txt") def counter(): lock_ex = False fd_open = False try: fd = os.open(COUNTER, os.O_CREAT|os.O_RDWR, 0755) fd_open = True for i in range(100): try: fcntl.flock(fd, fcntl.LOCK_EX|fcntl.LOCK_NB) lock_ex = True except IOError: # IOError: ロックを取得できなかった time.sleep(0.1) continue else: break if not lock_ex: sys.stderr.write("access_counter.cgi: File lock timeout.") return "File lock timeout." eof = os.lseek(fd, 0, os.SEEK_END) os.lseek(fd, 0, os.SEEK_SET) count = os.read(fd, eof) try: count = str(int(count) + 1) except ValueError: count = "1" os.lseek(fd, 0, os.SEEK_SET) os.ftruncate(fd, len(count)) os.write(fd, count) os.fsync(fd) except AttributeError: # AttributeError: OSの制約で利用できない関数や定数があった return "Your environment not supported." finally: if lock_ex: fcntl.flock(fd, fcntl.LOCK_UN) if fd_open: os.close(fd) return count if __name__ == "__main__": sys.stdout.write("Content-Type: text/html\n\n%s\n" % counter())
test_main.py
完全に同時とはいかないけど、コンテキストスイッチの影響を確かめるには、これでいいと思う。
#!/usr/local/bin/python #coding: utf-8 import os, time from subprocess import * execute = os.path.join(os.path.dirname(os.path.abspath(__file__)), "access_counter.py") total_start = time.time() timeout = 0 for i in range(10000): rap_start = time.time() childs = [Popen(execute, stdin=PIPE, stdout=PIPE, stderr=PIPE) for ii in range(150)] popen_done = time.time() for p in childs: p.wait() (out, err) = p.communicate() if err: print err timeout = timeout + 1 rap_done = time.time() print "%d rap done, childs born=%f, rap time=%f" % (i, popen_done - rap_start, rap_done - rap_start) total_done = time.time() print "total time %f" % (total_done - total_start) print "timeout = %d" % timeout
test_sub.py
#!/usr/local/bin/python #coding: utf-8 import os, time from subprocess import * execute = os.path.join(os.path.dirname(os.path.abspath(__file__)), "access_counter.py") total_start = time.time() timeout = 0 for i in xrange(500000): rap_start = time.time() p = Popen(execute, stdin=PIPE, stdout=PIPE, stderr=PIPE) p.wait() (out, err) = p.communicate() if err: print err timeout = timeout + 1 rap_done = time.time() print "%d rap, time=%f" % (i, rap_done - rap_start) total_done = time.time() print "total time %f" % (total_done - total_start) print "timeout = %d" % timeout
テスト方法
端末Aで次を実行 $ python test_main.py > main.log その後まもなく、端末Bで次を実行 $ python test_sub.py > sub.log mainで150万回、subで50万回、合計200万回カウンターを回す。 main.log、sub.logに記録されたtimeoutの数と、counter.txtの数を合わせて200万になればOK。
テスト結果
counter.txt 1999703 main.log timeout = 295 sub.log timeout = 2 1999703 + 295 + 2 = 2000000 main.log, sub.logともにタイムアウトしたと思える実行時間が記録されていた。 >>> import pprint >>> lines = open("main.log").readlines() >>> raptimes = [] >>> for l in lines: >>> if not l.startswith("access_counter.cgi"): >>> raptimes.append(float(l.split(",")[2].strip().split("=")[1])) >>> raptimes.sort() >>> pprint(raptimes[-10:]) [6.8269399999999996, 6.8799440000000001, 7.1228569999999998, 7.3894970000000004, 7.6184669999999999, 8.3026210000000003, 9.5717949999999998, 10.554797000000001, 10.622182, 10.824996000000001]>>> times = [] >>> for l in lines: >>> if not l.startswith("access_counter.cgi"): >>> times.append(float(l.strip().split("=")[1])) >>> times.sort() >>> pprint(times[-10:]) [5.1106069999999999, 5.1604809999999999, 5.2510870000000001, 5.4495550000000001, 5.643491, 6.1125689999999997, 7.9487379999999996, 9.470364, 10.060624000000001, 10.087431]
Pygmentsに好きなスタイルを追加する。
こっちにも書いたけど、Tracに限った話でもなさそうだし、記事一覧に出なくて見失いそうなので、改めて別記事にしておく。
ここではhonzaさんのvim2pygmentsを使って、気に入っているVimのカラースキームを追加してみた。
vim2pygments.tar.gzのダウンロードページはここ。
tar zxvf honza-vim2pygments-19a9339.tar.gz cd honza-vim2pygments-19a9339 cp $HOME/usr/local/share/vim/vim73/colors/desert.vim ./ python vimpygments.py desert.vim > vim_desert.py cp vim_desert.py path/to/site-packages/Pygments-1.5-py2.7.egg/pygments/styles/
エディターでpath/to/site-packages/Pygments-1.5-py2.7.egg/pygments/styles/__init__.pyを開く。
#: Maps style names to 'submodule::classname'. STYLE_MAP = { ... "Vim_Desert": "vim_desert::DesertStyle", # この行を追加する。vim_desert.pyにDesertStyleというクラスがあるってこと。 }