GoogleがTLSでの採用を提唱している共通鍵暗号方式「ChaCha」についてまとめた
ChaCha(チャチャ)という一見ふざけた名前の暗号が最近(自分の中で)話題ということで,勉強がてらに記事にしてみました.
背景
2016年4月現在,TLSの新しいバージョンとしてTLS 1.3が提案されており,ドラフトが公開されている.
TLS 1.2からの大きな変更点として,以下の2つがある.
- ハンドシェイクの省略によるRTT(Round Trip Time)の削減
- 危殆化した暗号の廃止
「危殆化した暗号」とは,Forward SecrecyでないCipher Suite(RSAのみを用いたもの)や,認証つき暗号でないCipher Suite(CBCモードのブロック暗号やRC4を用いたもの)のことを指す.
上記のドラフトによれば,2016年4月現在で実装必須暗号として制定されている暗号方式は以下の通りとなっている.
A TLS-compliant application SHOULD implement the following cipher
suites:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
共通鍵暗号は,ブロック暗号としてAES,ストリーム暗号としてChaCha20の二つのみとなっており,3DESやRC4は廃止される見込みである.
ChaChaという,実績も少なく,聞き慣れない暗号方式がTLS 1.3で提案されている理由の一つとして,Googleがこの暗号方式を推奨していることが挙げられる.
- draft-ietf-tls-chacha20-poly1305-04 - ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)
- Google Online Security Blog: Speeding up and strengthening HTTPS connections for Chrome on Android
TLS1.3の他に,次世代のWeb通信規格として,HTTP/2がある.HTTP/2は元々はGoogleのSPDYを元に策定さていることもあり,GoogleのTLS 1.3に対する発言力もかなり大きいものと推察できる.
ChaChaの構造
Salsa20
Salsa20は,ダニエル・バーンスタインによって開発されたストリーム暗号であり,後に同氏がSalsa20の変種としてChaChaを発表した.両者は互いに似たような構造を持つが,今回はChaChaのアルゴリズムを中心に解説する.
Chacha
ChaChaは,RFC 7538によって標準化されている.
ChaChaの暗号化と復号は,まったく同じ手順によって実現可能である.暗号化・復号の流れの全体像は以下のとおりである.
2. 入力ストリームに対して20ラウンドの演算を行い,最終アレイを元のアレイに加えて64byteの出力ストリームを得る
3. 最後に平文と出力ストリームのXORをとり,暗号化(復号)する
初期状態
上記の手順1について,入力ストリームは以下のように定まる.
- 定数:16byte(4 words)->{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}
- 鍵:32byte (8 words)
- ブロックカウント:4byte(1 word)
- Nonce: 12byte(3 words)
ブロックカウントとは,暗号化(復号)するブロックが何番目のブロックかを示しており,1ブロック64byteなので,最大256GBのデータまで対応できる.
Nonceは1度だけ使われるワンタイムトークンを意味し,リトルインディアンで格納される.
入力ストリームの構造は以下のようになる.
cccccccc cccccccc cccccccc cccccccc
kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk
kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk
bbbbbbbb nnnnnnnn nnnnnnnn nnnnnnnnc=constant k=key b=blockcount n=nonce
ラウンド操作
ChaChaは"ChaCha20"とも呼ばれるように,合計20ラウンドの操作を行う.
ChaChaのラウンドは"column rounds"と"diagonal rounds"という二種類のラウンドの繰り返しとなる.またそれぞれのラウンドは以下のように4つの"quarter-rounds"で構成されている.QUARTERROUNDの引数はストリームの何番目のwordかを示す.
# column rounds QUARTERROUND ( 0, 4, 8,12) QUARTERROUND ( 1, 5, 9,13) QUARTERROUND ( 2, 6,10,14) QUARTERROUND ( 3, 7,11,15) # diagonal rounds QUARTERROUND ( 0, 5,10,15) QUARTERROUND ( 1, 6,11,12) QUARTERROUND ( 2, 7, 8,13) QUARTERROUND ( 3, 4, 9,14)
また,ラウンド関数QUARTERROUND()の演算は以下のように行う.なお,^
は排他的論理和,<<<
は左ローテート操作を示す.
QUARTERROUND(a, b, c, d): a += b; d ^= a; d <<<= 16; c += d; b ^= c; b <<<= 12; a += b; d ^= a; d <<<= 8; c += d; b ^= c; b <<<= 7;
最後に,手順3にある通り,ラウンド操作を施したストリームと入力ストリームを足しあわせて出力ストリームとし,暗号文(平文)とXORをとることで復号(暗号化)できる.
ChaChaの安全性
Salsa20およびChachaは,XOR,32-bit addition mod 2^32,32ビット長のワード16個のローテートを用いており,これにより,ソフトウェア実装におけるタイミング攻撃を回避することができる.
2013年時点では,Salsa20/12およびSalsa20/20の解読に成功した例はない.最良の攻撃法では12あるいは20ラウンド中8ラウンドまで解読されている.
ChaChaではラウンドごと,あるいはブロックごとの発散を大きくすることでSalsa20に比べパフォーマンスが向上している.ChaChaに対する攻撃も試みられたが,Salsa20よりも1ラウンド少ない結果となり失敗した.
- "New Features of Latin Dances: Analysis of Salsa, ChaCha, and Rumba," Jean-Philippe Aumasson, et al. http://eprint.iacr.org/2007/472.pdf
その後の経過について調べることができなかったが,AESのように十分な安全性評価を行ったのかどうかが疑問に残る.
実装してみた
2015年に開催されたTrend Micro CTFの演習問題で出題されたChaCha暗号の問題を解いてみる.
Hi this is chacha. I'm 20 years old.
I have a question. Could you answer a question below?...haha. Just kidding.
6bd00ba222523f58de196fb471eea08d9fff95b5bbe6123dd3a8b9026ac0fa84Key: 23AD52B15FA7EBDC4672D72289253D95DC9A4324FC369F593FDCC7733AD77617
nonce:5A5F6C13C1F12653
"Hi this is chacha."と言っていることから,これはChaChaで暗号化された文章であることがすぐに分かる.
Solver
なお,今回は暗号文が1ブロック分のデータ量しか無かったため,ブロックカウントの更新は実装していない.
Result
flag: TMCTF{Whose_garden_is_internet?}
無事,ChaCha暗号を復号できたことが分かる.
参考
- NRIセキュア ニュースレター 2016年2月 Vol.79
- "ChaCha, a variant of Salsa20," Daniel J. Bernstein, https://cr.yp.to/chacha/chacha-20080128.pdf
2015/04/04 追記
このエントリが公開された翌日に,ChaCha20-Poly1305についてさらに詳しくまとめられた記事が出ました.
ぜひこちらも併せて読んでみると良いと思います.
d.hatena.ne.jp
BKPCTFのRSA暗号問題を解く
2016年3月4日に開催されたBoston Key Party CTFにチームm1z0r3として少しだけ参加した.大会からかなり日が経ってしまって今更感があるが,今回はbob's hatというRSA暗号の問題のWrite-upを書こうと思う.
http://bostonkey.party/
bob's hat - Crypto 4
問題概要
Task:
bob’s hat — 4 : crypto : Alice and Bob are close together, likely because they have a lot of things in common.This is why Alice asked him a small *q*uestion, about something cooler than a wiener.
File: bkpctf_crypto_bobhat_task.zip
zipファイルを解凍すると,以下のファイルが得られる.
- almost_almost_almost_almost_there.pub - 公開鍵
- almost_almost_almost_almost_there.encrypted - 公開鍵で暗号化された暗号文
- almost_almost_almost_almost_there.zip - パスワード付きzipファイル
暗号文を解読し,得られた平文がzipファイルのパスワードとなる.このzipファイルを解凍するとまた同じような構成のファイルが抽出され,暗号を繰り返し解いていくと最後にフラグが得られる.いつぞやに見かけたエクストリームCTFと似たような形式である.
Stage 1
まず,公開鍵の情報を確認する.
# openssl rsa -text -modulus -pubin < almost_almost_almost_almost_there.pub Public-Key: (1024 bit) Modulus: 00:86:e9:96:01:3e:77:c4:16:99:00:0e:09:41:d4: 80:c0:46:b2:f7:1a:4f:95:b3:50:ac:1a:4d:42:63: 72:92:3d:8a:45:61:d9:6f:bf:b0:24:05:95:90:72: 01:ad:32:25:cf:6e:de:d7:de:02:d9:1c:38:6f:fa: c2:80:b7:2d:0f:95:ca:e7:1f:42:eb:e0:d3:ed:ae: ac:e7:ce:a3:19:5f:a3:2c:1c:60:80:d9:0e:f8:53: d0:6d:d4:57:2c:92:b9:f8:31:0b:bc:0c:63:5a:5e: 26:95:25:11:75:10:30:a6:59:08:16:55:4e:76:30: 31:bc:bb:31:e3:f1:19:c6:5f Exponent: 65537 (0x10001) Modulus=86E996013E77C41699000E0941D480C046B2F71A4F95B350AC1A4D426372923D8A4561D96FBFB0240595907201AD3225CF6EDED7DE02D91C386FFAC280B72D0F95CAE71F42EBE0D3EDAEACE7CEA3195FA32C1C6080D90EF853D06DD4572C92B9F8310BBC0C635A5E26952511751030A6590816554E763031BCBB31E3F119C65F writing RSA key -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCG6ZYBPnfEFpkADglB1IDARrL3 Gk+Vs1CsGk1CY3KSPYpFYdlvv7AkBZWQcgGtMiXPbt7X3gLZHDhv+sKAty0Plcrn H0Lr4NPtrqznzqMZX6MsHGCA2Q74U9Bt1Fcskrn4MQu8DGNaXiaVJRF1EDCmWQgW VU52MDG8uzHj8RnGXwIDAQAB -----END PUBLIC KEY-----
鍵長は十分な長さがあり,ModulusやExponentの値も特に怪しい点はない.このようなケースの場合,各種素因数分解アルゴリズムを用いてModulusの素因数分解を試みると良い.
今回はFermat法によって素因数分解に成功した.
参考
Fermat法は素数同士の積からなる合成数において、二つの素数の差が小さい場合に有効なアルゴリズムである。 具体的には、合成数nの平方根周辺のある値をxとしたとき、(x-k)*(x+k) == nとなるkを小さいものから求める。
Solver
実行結果
p= 9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769 q= 9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987245583 pass: XtCgoEKksjKFWlqOSxqsEhK/+tsr1k5c
pとqの値が非常に近いことが分かる.
なお,今回rsatool.pyを使って秘密鍵を生成して復号する手法を取ろうとしたが,なぜかopensslコマンドでうまく復号できず,結局復号の処理をpythonで実装した.他にも同様の現象に陥った人もいたようなので,この原因について暗号のプロに教えを請いたいところ.
Stage 2
公開鍵の情報を確認.
# openssl rsa -text -modulus -pubin < almost_almost_almost_there.pub Public-Key: (1024 bit) Modulus: 00:ab:e6:33:ce:c2:e7:ec:10:a8:51:92:79:05:a6: 57:df:4e:10:41:60:23:c0:c3:4f:c6:4d:64:bd:8b: 82:57:b7:bf:20:7a:dd:04:7b:0a:df:21:c5:25:b0: 52:06:8c:70:29:5c:74:6c:3b:1b:e1:43:6f:39:ed: 8b:f7:a8:13:e4:b8:45:ce:0c:a8:9c:a8:28:b4:57: 63:d4:6b:18:98:c7:a2:fa:5f:8f:e7:84:28:ca:b6: cd:f7:0e:f8:71:db:97:1b:32:32:84:1a:1c:e2:45: 9c:e6:50:a1:54:36:2f:80:cf:b6:41:63:c3:ca:63: ad:72:bc:fb:db:f0:15:4f:f7 Exponent: 65537 (0x10001) Modulus=ABE633CEC2E7EC10A851927905A657DF4E10416023C0C34FC64D64BD8B8257B7BF207ADD047B0ADF21C525B052068C70295C746C3B1BE1436F39ED8BF7A813E4B845CE0CA89CA828B45763D46B1898C7A2FA5F8FE78428CAB6CDF70EF871DB971B3232841A1CE2459CE650A154362F80CFB64163C3CA63AD72BCFBDBF0154FF7 writing RSA key -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCr5jPOwufsEKhRknkFplffThBB YCPAw0/GTWS9i4JXt78get0EewrfIcUlsFIGjHApXHRsOxvhQ2857Yv3qBPkuEXO DKicqCi0V2PUaxiYx6L6X4/nhCjKts33Dvhx25cbMjKEGhziRZzmUKFUNi+Az7ZB Y8PKY61yvPvb8BVP9wIDAQAB
こちらも鍵長,Modulus・Exponentの値ともに怪しいところはない.
ところが,今回のModulusとStage 1のModulusのGCDをとったところ,1ではない公約数が存在することが判明した.要は,鍵生成において「素数の使い回し」をしていたことになる.これによりModulusの素因数分解が可能となる.
Solver
実行結果
p= 9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769 q= 12401828372292379853813876769631673931562555174641979554254424458038243058638417065284301266881242433017828663818811606556559256084249679274024474025282343 pass: rlSpJ6HbP+cZXaOuSPOe4pgfevGnXtLt
Stage 3
公開鍵の情報を確認する.
# openssl rsa -text -modulus -pubin < almost_almost_there.pub Public-Key: (1040 bit) Modulus: 00:ba:d2:0c:f9:7e:d5:04:2d:f6:96:ce:4d:f3:e5: a6:78:cf:4f:b3:69:3d:3d:f1:2d:fe:9f:d3:fd:8c: c8:aa:b8:b9:55:33:e4:14:e3:fc:0c:37:7f:4e:e5: 48:27:11:8b:1d:30:56:1a:3c:74:1b:ea:7c:76:89: 97:89:b5:17:43:e0:76:09:2d:f9:eb:05:dc:97:eb: 15:05:ce:9e:b1:2b:5a:b9:e1:0a:bf:56:f9:20:a5: 8e:7e:00:ec:f0:59:77:e8:72:83:4d:d8:58:4c:f4: ac:87:cb:7d:c5:01:59:bd:96:2c:75:cb:ef:b6:c6: ac:3a:31:a7:4e:7d:8f:1e:4c:10:d5 Exponent: 65537 (0x10001) Modulus=BAD20CF97ED5042DF696CE4DF3E5A678CF4FB3693D3DF12DFE9FD3FD8CC8AAB8B95533E414E3FC0C377F4EE54827118B1D30561A3C741BEA7C76899789B51743E076092DF9EB05DC97EB1505CE9EB12B5AB9E10ABF56F920A58E7E00ECF05977E872834DD8584CF4AC87CB7DC50159BD962C75CBEFB6C6AC3A31A74E7D8F1E4C10D5 writing RSA key -----BEGIN PUBLIC KEY----- MIGhMA0GCSqGSIb3DQEBAQUAA4GPADCBiwKBgwC60gz5ftUELfaWzk3z5aZ4z0+z aT098S3+n9P9jMiquLlVM+QU4/wMN39O5UgnEYsdMFYaPHQb6nx2iZeJtRdD4HYJ LfnrBdyX6xUFzp6xK1q54Qq/VvkgpY5+AOzwWXfocoNN2FhM9KyHy33FAVm9lix1 y++2xqw6MadOfY8eTBDVAgMBAAE= -----END PUBLIC KEY-----
こちらも鍵長,Modulus・Exponentの値ともに怪しいところはない.
各種素因数分解アルゴリズムを適用したところ,片方の素数の値が非常に小さいことが判明し,Modulusを小さい素数から順に除算していくことにより素因数分解に成功した.
Solver
実行結果
p= 54311 q= 158304142767773473275973624083670689370769915077762416888835511454118432478825486829242855992134819928313346652550326171670356302948444602468194484069516892927291240140200374848857608566129161693687407393820501709299228594296583862100570595789385365606706350802643746830710894411204232176703046334374939501731 pass: hQdK+dKleMJqth/dofWyFaiWp3PW7jil
Stage 4
これが最後の問題.公開鍵の情報を確認する.
# openssl rsa -text -modulus -pubin < almost_there.pub Public-Key: (1024 bit) Modulus: 00:9c:2f:65:05:89:91:20:90:6e:5a:fb:d7:55:c9: 2f:ec:42:9f:ba:19:44:66:f0:6a:ae:48:4f:a3:3c: ab:a7:20:20:5e:94:ce:9b:f5:aa:52:72:24:91:6d: 18:52:ae:07:91:5f:bc:6a:3a:52:04:58:57:e0:a1: 22:4c:72:a3:60:c0:1c:0c:ef:38:8f:16:93:a7:46: d5:af:bf:31:8c:0a:bf:02:76:61:ac:ab:54:e0:29: 0d:fa:21:c3:61:6a:49:82:10:e2:57:81:21:d7:c2: 38:77:42:93:31:d4:28:d7:56:b9:57:eb:41:ec:ab: 1e:aa:d8:70:18:c6:ea:34:45 Exponent: 46:6a:16:9e:8c:14:ac:89:f3:9b:5b:03:57:ef:fc: 3e:21:39:f9:b1:9e:28:c1:e2:99:f1:8b:54:95:2a: 07:a9:32:ba:5c:a9:f4:b9:3b:3e:aa:5a:12:c4:85: 69:81:ee:1a:31:a5:b4:7a:00:68:ff:08:1f:a3:c8: c2:c5:46:fe:aa:36:19:fd:6e:c7:dd:71:c9:a2:e7: 5f:13:01:ec:93:5f:7a:5b:74:4a:73:df:34:d2:1c: 47:59:2e:14:90:74:a3:cc:ef:74:9e:ce:47:5e:3b: 6b:0c:8e:ec:ac:7c:55:29:0f:f1:48:e9:a2:9d:b8: 48:0c:fe:2a:57:80:12:75 Modulus=9C2F6505899120906E5AFBD755C92FEC429FBA194466F06AAE484FA33CABA720205E94CE9BF5AA527224916D1852AE07915FBC6A3A52045857E0A1224C72A360C01C0CEF388F1693A746D5AFBF318C0ABF027661ACAB54E0290DFA21C3616A498210E2578121D7C23877429331D428D756B957EB41ECAB1EAAD87018C6EA3445 writing RSA key -----BEGIN PUBLIC KEY----- MIIBHzANBgkqhkiG9w0BAQEFAAOCAQwAMIIBBwKBgQCcL2UFiZEgkG5a+9dVyS/s Qp+6GURm8GquSE+jPKunICBelM6b9apSciSRbRhSrgeRX7xqOlIEWFfgoSJMcqNg wBwM7ziPFpOnRtWvvzGMCr8CdmGsq1TgKQ36IcNhakmCEOJXgSHXwjh3QpMx1CjX VrlX60Hsqx6q2HAYxuo0RQKBgEZqFp6MFKyJ85tbA1fv/D4hOfmxnijB4pnxi1SV KgepMrpcqfS5Oz6qWhLEhWmB7hoxpbR6AGj/CB+jyMLFRv6qNhn9bsfdccmi518T AeyTX3pbdEpz3zTSHEdZLhSQdKPM73SezkdeO2sMjuysfFUpD/FI6aKduEgM/ipX gBJ1 -----END PUBLIC KEY-----
これは明らかにExponentの値があやしい.通常は65537
などの値が多用されるが,今回は値が大きすぎる.このような場合,Wiener's Attackが成立する可能性が高い.Exponentの値が大きいと,相対的に秘密鍵dが小さくなり,公開鍵からdを復元することができる.素数をp, q,秘密鍵をd,Modulusの値をNとしたとき,Wiener's Attackの成立条件は以下のとおりである.
Wiener's Attackの詳細なアルゴリズムは以下の記事によくまとまっている.
Exponentの値を見た瞬間の弊チームslackの様子(途中バイナリアンが混じっている).
Solver
実行結果
p= 10843221374140991753173625949764386011485161421520044246309105053489500519257941272796681417497061734054081478280518835582353321569961722963922828311576983 q= 10114792273660656874618568712406420344176220457790563178092222929337786916374923318745284718351487926620784106195715878875311958793629905453919697155685507 pass: /3aAP5dF2zmrPh9K6A4AqMLsIiYDk2C2
最後のzipファイルを解凍すると,FLAG
という名前のファイルが抽出される.
# cat FLAG BKPCTF{Its_not_you,_its_rsa_(that_is_broken)}
というわけで,フラグはBKPCTF{Its_not_you,_its_rsa_(that_is_broken)}
問題の考察
改めて,問題文を振り返ってみる.
Alice and Bob are close together, likely because they have a lot of things in common.This is why Alice asked him a small *q*uestion, about something cooler than a wiener.
これを分解・分析してみると.
- "Alice and Bob are close together,": pとqの値が近い(Stage 1)
- "likely because they have a lot of things in common.": 素数の使い回し(Stage 2)
- "This is why Alice asked him a small *q*uestion,": 小さな素数q(Stage 3)
- "about something cooler than a wiener": Wiener's Attack(Stage 4)
このように,問題文が解答の大きなヒントになっていたということが分かる.
Let's Encryptの証明書の更新を自動化する
前回Let's EncryptでSSL証明書を取得し、HTTPSサーバを建てた記事を書いた.
Let's Encryptの証明書の有効期間は90日しかなく,こまめに更新しなければならない.
この更新をなんとか自動化できないかなぁと思っていたら,公式のマニュアルに「cron使うとええよ」と書いてありなるほどねとなった.
/etc/crontab
に以下の行を追加する.
00 00 01 * * root /usr/local/letsencrypt/letsencrypt-auto certonly --webroot -d sonickun.xyz --webroot-path /var/www/html/ --renew-by-default --debug && service nginx reload
これにより,上記のコマンドが「毎月1日の0時0分」に実行されることになる.
コマンドの前半部分では,証明書の取得を行っている.ポイントは--renew-by-default
というオプションで,これをつけることで例の青いUIの画面をスキップすることができる.コマンドの後半部分ではNginxをリロードしている.
これにてLet's Encryptの証明書が毎月1日に自動更新されることとなり,幸せになることができた.
cronについて参考
Let's EncryptでHTTPSサーバを建てたついでにSSL LabsでA+評価をめざす
最近 Let's Encrypt が Public Beta になったということで,自分のサイト(https://sonickun.xyz)もSSL化してみた.また,どうせならSSL LabsのテストでA+を取りたいと思いあれこれ試行錯誤したので備忘録として残しておく.
Let's Encrypt
letsencrypt.org
Let's Encrypt は,SSL/TLSサーバ証明書の取得・管理を簡略化できる無料のサービスであり,TLSやHTTPSを普及させることを目的としている.Let's Encryptで取得可能なSSL/TLSサーバ証明書は「ドメイン認証 (DV) SSL/TLS証明書」であり,独自ドメインの所有者であれば誰でも取得可能である.企業認証(OV)SSL/TLS証明書やEV SSL証明書は取得できないが,個人が運営するサイト程度ならDV証明書で十分といえる.
Let's Encryptの詳細は有志による日本語サイトによくまとまっている.
Qualys SSL Labs
Qualys SSL Labs社が提供するSSL Server Testは,SSLサーバ証明書の設定状況の確認や,信頼性の診断などができるサービスである.グレード表記や項目別スコア表示があり,さらに信頼性に乏しいと思われる設定項目を指摘してくれる.
今回はQualys SSL Labsのテストの最高グレードである A+ の取得を目指す.
Let's EncrptでWebサイトをSSL化
今回SSL化するWebサイトは自分のプロフィールサイト(sonickun.xyz)で,WebサーバはNginx,OSはCentOS 6.5である.
詳しくは過去記事
sonickun.hatenablog.com
Lets's Encrypt Clientのインストール
事前に以下のパッケージをインストールしておく.
# yum -y install httpd openssl mod_ssl # yum -y install git
次に以下のコマンドでLets's Encrypt Clientをインストールする.--debug
オプションは必須ではないが,これがないとエラーが起きる場合があるので付けておいたほうが良い.
# git clone https://github.com/letsencrypt/letsencrypt # cd letsencrypt # ./letsencrypt-auto --help --debug
自分の環境の場合,letsencrypt-autoの実行時にvirtualenvというPythonモジュールがないとがないと怒られてしまったのでeasy_installで入れておく.
# easy_install virtualenv
SSL証明書の取得
以下のコマンドを実行する.${DOMAIN}
の部分は取得したいドメイン(自分の場合は"sonickun.xyz")を,${WEBROOT}
の部分はNginxで設定しているルートディレクトリ(自分の場合は"/var/www/html/")を指定する.
# ./letsencrypt-auto certonly --webroot -d ${DOMAIN} --webroot-path ${WEBROOT} --debug
すると,青い画面でメールアドレスの入力を求められるので,メールアドレスを入力して,<了解>を選択する.つぎに,利用規約に同意するか聞かれるので,<Agree>を選択する.
証明書の取得が完了すると以下の様な表示になる.
IMPORTANT NOTES: - If you lose your account credentials, you can recover through e-mails sent to xsonickun@gmail.com. - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/sonickun.xyz/fullchain.pem. Your cert will expire on 2016-03-13. To obtain a new version of the certificate in the future, simply run Let's Encrypt again. - Your account credentials have been saved in your Let's Encrypt configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Let's Encrypt so making regular backups of this folder is ideal. - If like Let's Encrypt, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
/etc/letsencrypt/live/sonickun.xyz/
に以下のファイルが置かれる.
# cd /etc/letsencrypt/live/sonickun.xyz/ # ls cert.pem #サーバ証明書 chain.pem #中間証明書 fullchain.pem #サーバ証明書+中間証明書 privkey.pem #サーバ秘密鍵
Nginxの設定
今回はSSL化に加えて,HTTP/2にも対応させることにする.HTTP/2はHTTP/1.1と比べ様々な最適化がされており,例えば,バイナリーフレームの採用,ストリームによる多重化,サーバープッシュなどの特徴がある.
Nginxでは,mainlineで提供されているバージョン1.9.5以降でHTTP/2に対応しているため,これよりも古いバージョンの場合はアップデートする必要がある.
まず,"nginx: Linux packages"を参考にしつつリポジトリを以下のように変更する(baseurlに注意).
# vi /etc/yum.repos.d/nginx.repo [nginx] name=nginx repo baseurl=http://nginx.org/packages/mainline/OS/OSRELEASE/$basearch/ gpgcheck=0 enabled=1
yumでアップデート,バージョンの確認をする.
# yum -y install nginx # nginx -v nginx version: nginx/1.9.9
これにてNginxのバージョンアップが完了した.
次に,Nginxの設定ファイルetc/nginx/conf.d/server.conf
を以下のように設定する(なおこれが最終版ではない).
server { listen 80; listen [::]:80; listen 443 ssl http2; listen [::]:443 ssl http2; server_name sonickun.xyz; ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA; ssl_dhparam /usr/local/nginx/conf/dhparam.pem; location / { root /var/www/html; index index.html index.htm; } }
設定変更後のNginxの再起動も忘れずに.
# service nginx reload # service nginx restart
Webサイトの確認
実際にアクセスしてみるとHTTPSで通信していることが分かる.また,Googleの拡張であるHTTP/2 and SPDY indicatorを使ってHTTP/2が使われていることが確認できる.
chrome://net-internals/#events&q=type:HTTP2_SESSION%20is:active
SSL Labs A+ を目指して
ひとまず先程のSSL Labsのテストを行ってみる.
はい.
診断結果によれば,DH鍵が弱く,Perfect Forward Secrecy (PFS) に対応していないとのこと.
DH鍵の変更
DHE鍵交換にはLogjam攻撃という攻撃が存在し,512bit長の標準素数であれば数十秒で解ける.また1024bit長でも国家予算並みのお金をかければ解読できると言われており,現在は2048bitの利用が推奨されている.
さて,現状のDH鍵の鍵長は1024bitとなっているため,2048bitのものに変更する.
まず/usr/local/nginx/conf/
下でopensslを用いてDH鍵を生成する.
# openssl dhparam -out dhparam.pem 2048 # openssl dhparam -text -in dhparam.pem -noout
次にserver.conf
に以下の行を追加する.
ssl_dhparam /usr/local/nginx/conf/dhparam.pem;
これにてDH鍵の変更は完了.
Perfect Forward Secrecy (PFS) ヘの対応
Perfect Forward Secrecyとは,サーバの秘密鍵が暴露された場合でも,ユーザーの個人的な情報記録を過去に遡って解読することを妨ぐ暗号化技術であり,HeartBleedのような致命的なバグに対して効果を発揮する.
PFSをサポートしているのはDHE鍵交換とECDHE鍵交換である.
したがって,server.conf
のssl_ciphersの部分を以下のように変更する.
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";
Chromeのアドレスバーの鍵マークをクリックすると,ECDHE鍵交換が行われているのが確認できる.
HTTP Strict Transport Security (HSTS) ヘの対応
HSTSでは,サーバから"Strict-Transport-Security"というHTTPヘッダを送ることで,クライアントに常にHTTPSで通信するように強制させることができる.この指定はmax-ageに指定した期間クライアントにキャッシュされる.これにより,中間者攻撃より知らず知らずのうちに攻撃者に平文通信を盗聴されることを防ぐことができる.
SSL LabsのテストではHSTSが有効になっているとスコアが上がるとの事だったのでこれを有効にすることにする.Nginxではserver.conf
に以下の行を追加することでHSTSが有効になる.
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";
実際にHTTPSで接続した後,URLの"https"を"http"に変更してアクセスすると,すぐさまHTTPSのサイトにリダイレクトする.レスポンスヘッダを見ると"Strict-Transport-Security"ヘッダが付加されていることが確認できる.
再テスト
再びSSL Labsで診断した結果,めでたくA+を取得できた:)
最終的な設定ファイルserver.conf
は以下のとおり.
server { #listen 80; #listen [::]:80; listen 443 ssl http2; listen [::]:443 ssl http2; server_name sonickun.xyz; ssl_session_timeout 10m; ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"; ssl_prefer_server_ciphers on; ssl_dhparam /usr/local/nginx/conf/dhparam.pem; add_header Strict-Transport-Security "max-age=15768000; includeSubdomains"; location / { root /var/www/html; index index.html index.htm; } }
おわりに
Let's EncrptによるSSL証明書の取得は思ったよりも簡単に行うことができた.また,HTTPSにしたからといって安全と高をくくってはいけないと学んだ.
MozillaやGoogleをはじめとする多くの団体や企業があらゆるWebコンテンツをHTTPSに移行するように呼びかけており,SSL化されたWebサイトはどんどん増えている.GoogleではHTTPS ページが優先的にインデックスに登録されるようになる.
一方で,未だにHTTPSに対応していない大手のWebサービスも多々あり,それらのHTTPのコンテンツがHTTPSのWebサイト上で動作しないといった問題が起きている.
もちろんセキュリティ上の問題もあるが,この度のLet's Encryptのリリースによってこれらの問題が改善されていくことが期待される.
参考
- TLS, HTTP/2演習
- Let's EncryptとnginxでHTTP/2サーバを立てる - pixiv inside
- 無料SSL証明書の Let’s Encrypt が公開されたので実際に試してみた | Webセキュリティの小部屋
- Enable Perfect Forward Secrecy for nginx | AxiaCore
- Heartbleed以後の対策の1つで希望の光「Forward Secrecy」をnginxに設定する巻 - Qiita
- nginx - httpsだからというだけで安全?調べたら怖くなってきたSSLの話!? - Qiita
- HTTPS 化する Web をどう考えるか - Block Rockin’ Codes
- nginx - 我々はどのようにして安全なHTTPS通信を提供すれば良いか - Qiita
TDUCTF 2015 「Portscan」のWrite-upを丁寧に
TDUCTF 2015 に参加した.connpass.com
今回はPortscan(500pt)というNetwork問題で最も点数の高かった問題のWrite-upを書く.解けた人が4人と少なく,tsharkの使い方を知らない人が多い感じがしたので,試行の過程・ツールの使い方も含めて丁寧に書いていく.他の問題は大したものを解いてない or CTF終了後にWrite-up発表があったので,今回は省略する.
最後にCTFの結果と所感を述べる.
tsharkの使い方
問題を解く前提知識として,tsharkの使い方をまとめておく.tsharkとはWiresharkのCUI版であり,Wiresharkで出来るようなことがCUI上で行える.pcapデータを整形したり統計をとったりしたいときなどはこのtsharkが大活躍することがある.スクリプトを書く必要もなく,ワンライナーのコマンドですべてが解決する.Wiresharkがインストールされている環境であればtsharkも一緒に入っているはず.
オプション一覧も日本語でまとめられている.
CTFでよく使うオプションまとめ
-r [infile]
: [infile]で指定したpcapファイルを読み込む.-Y "[display filter]"
: [display filer]で指定したパケットに絞り込む.フィルタはWiresharkのDisplay filterで使えるものと同じ.-T fields
,-e [field]
: パケットのフィールドを指定して表示させる.フィールドの名前も同様にWiresharkのDisplay filterで使えるものと同じ.複数選択するときに区切り文字を指定したいときは-E separator=[char]
を使ってお好みで.-c [packet count]
: 表示するパケットの数を指定する.
すべてのDisplay filterは以下のページで参照できる.
Wiresharkでパケットを開いて,「このパケットのこのフィールドのDisplay filterを知りたい!」という時は,そのフィールド上で右クリック→Apply as Filter→SelectedでWiresharkのFilter欄に表示される.
コマンド例
「secretmsg.pcapを読み込み,送信先IPアドレスが192.168.0.238のパケットに絞り込み,送信先ポート,UDPチェックサム,ペイロードを並べて10パケット表示させたい」とき,コマンドは以下のようになる.
# tshark -r secretmsg.pcap -Y "ip.dst==192.168.0.238" -T fields -e udp.dstport -e udp.checksum -e data.data -c 10
出力は以下のとおり.
➜ tshark -r secretmsg.pcap -Y "ip.dst==192.168.0.238" -T fields -e udp.dstport -e udp.checksum -e data.data -c 10 1 0x0000f25f 23 1 0x00000000 2 0x0000f25d 23 2 0x00000000 3 0x0000f25b 23 3 0x00000000 4 0x0000f259 23 4 0x00000000 5 0x0000f257 23 5 0x00000000
パケットに指定したフィールドが存在しない場合は空白になる.また,ICMP Destination Unreachableなどの"パケットの中にパケットが存在する"ようなパケットの場合には,指定したフィールドがカンマで区切って複数個表示される場合がある.
Portscan (Network 500pt) Write-up
問題文:
ポートスキャンが仕掛けられた!ん?何かメッセージがついてきているぞ?
問題ファイル:
secretmsg.pcap
pcapファイルが渡されるので開いてみると,UDPスキャンを行っている通信が見れる.
ポートスキャンといえばSYNスキャンが主流だが,UDPのサービスを調査するときにはUDPパケットを送ってスキャンを行うことがある.ちなみにNmapでは-sU
オプションを付けてUDPスキャンを行うことが出来る.
今回のpcapでは,192.168.100.60から192.168.0.238(1~1288ポート)に向かってUDPパケットを送信し,すべてのポートからICMP Destination Unreachableが返ってきている.
問題文より,192.168.100.60が送信するUDPパケットにメッセージが隠されていることが推測できる.パケットにメッセージが隠されているとすれば,当然そのペイロードに注目する.ペイロードを見てみると,それぞれ1バイトのデータがあり,ぱっと見で0x20,0x23,0x0aの3種類しかないことがわかる(たまにペイロードがないものもある).
そこで,先ほどのtsharkを用いてUDPパケットのペイロードのみを抽出してみる.
➜ tshark -r secretmsg.pcap -Y "udp and !(icmp)" -T fields -e data.data 23 23 23 23 23 23 20 20 23 23 23 ....
Display filter を"!(icmp)"としたのは,前述のようにICMPパケットの中にUDPパケットが含まれており,"udp"というFilterだけではICMPパケットも同時に表示されるためである.これをリダイレクト(>)を用いてテキストファイル(out.txt)に出力しておく.
ここで,0x20,0x23,0x0aをそれぞれASCII文字で表すと,0x20はスペース,0x23は#(シャープ),0x0aは制御文字で改行を意味する.
先ほど生成したテキストファイルに対して,スクリプトを用いて文字の置換を行う.またペイロードがなかったものに関しては"@"とでもしておく.
solve.py
f = open("out.txt","r") str = "" for l in f: l = l.strip() if(l==""): l ="@" if(l=="0a"): l="\n" if(l=="20"): l=" " if(l=="23"): l="#" str += l print str f.close()
実行結果
ということで,フラグの文字がASCIIアートで表示された.
ヒントとしては,0x0aの出現回数が少なく,等間隔に存在するため,ここで改行したら長方形型になるかな?と推測できる.
フラグはTDU{PORTSCAN_PAYLOAD}
結果と所感
結果は69人中9位というつらい成績だった.上の人を見るとア。という感じ.
悔やまれる点
- Networkのメールの問題.zipファイルを見た瞬間に既知平文攻撃ができるとわかり,すぐにpkcrackを使ったがなぜか解凍できなかった.ファイルが壊れていたのか,既知平文のデータ量が少なかったのか,VMのメモリが足りなかったのか未だに謎.
- Webのアップロードのやつ.同じような問題が前回のTDUCTFでも出ており,しかも時間内に解いていたにも関わらず今回はやらなかった.やればよかった.
- 全体的にどうでもいいところでつまづくところがあった(pcap破損してて開けねえ!とか).競技中は音楽など聞かずに運営の方の話をちゃんと聞こうと思った.
問題数の割に時間が足りなくて全てに取り組むことができなくて残念だったが,取り組んだ問題に関しては楽しく解くことができた.
運営のみなさま,ありがとうございました!
セキュリティ・キャンプ 2015 講義資料まとめ
セキュキャンに行けなかった人がセキュキャンの資料のまとめをつくりました.
公開してくださって本当にありがとうございます!
※ここにない資料があれば@y_hagまで教えてくださるととても嬉しいです.
#ssmjp で発表した―「進化するWebトラッキングの話」
2015/07/31に開催された#ssmjpにて,「進化するWebトラッキングの話」というタイトルで発表しました.
会場は品川のBIGLOBEのオフィスでした.
2015年07月の#ssmjpまとめ | ssmjp Infomation
発表スライド
オフレコのスライドは省いてあります.ごめんなさい><
結構身近な話題だったので,shlideshareのほうがそこそこbuzzってました.やはりみんな関心があるんだなあと.
この勉強会では過去に一度発表させていただいたことがあったので,今回は比較的落ち着いて発表出来ました.少し残念だったのは,時間があまりなかったこともあり,発表を聞いた人から質問,意見,マサカリをあまりいただけなかったこと.まだまだWeb初心者なので,色々とフィードバックをいただけたらと思います.
twitter上の評価を見ていると,「今後はCookieに取って代わって,Browser Fingerprintがトラッキングの主流になるということなのか」というコメントがありましたが,けしてそうではないと思います.スライドにもあるように,RTBといった広告システム等で今後もCookieは使われ続けると思いますし,Browser Fingerprintはあくまでユーザーを識別するための"補助的な"情報という立ち位置になると思います.一方で,今後それ単体だけでユーザーを特定できるような最強のFingerprintが見つかってしまうかもしれません(ないとおもいますが).
Webトラッキングは新しい話題がどんどんでてきますし,対策もまだまだ十分と言えないので,研究してみるには面白い題材だと思います.