Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
iOSDCを支えるDrupal8 Drupal 8 - backend of iOSDC 長谷川智希 HASEGAWA Tomoki
Slide 2
Slide 2 text
この資料は後ほど公開します This slide will be published later.
Slide 3
Slide 3 text
ライフワーク: Web / iOSアプリ開発, ビール, 電子工作, サッカー観戦, レンタルカートレース, … 長谷川 智希 Web / iOS App Development, Beer, IoT, Watch soccer match, Rental Kart Racing, … デジタルサーカス株式会社 副団長CTO Digital Circus, Inc. Vice-master CTO Tokyo, Japan Hobby: @tomzoh
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
WE ARE HIRING!! Web Development Mobile App Development ( ) (iOS, Android) http://www.dgcircus.com Omotesando, Tokyo
Slide 7
Slide 7 text
今日のテーマ Drupal 8 - backend of iOSDC iOSDCを支えるDrupal8 Today’s theme
Slide 8
Slide 8 text
iOSDC https://iosdc.jp
Slide 9
Slide 9 text
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 • 開催情報は公式サイトにて告知。
Slide 10
Slide 10 text
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 • 開催情報は公式サイトにて告知。
Slide 11
Slide 11 text
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 • 開催情報は公式サイトにて告知。
Slide 12
Slide 12 text
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 • 開催情報は公式サイトにて告知。
Slide 13
Slide 13 text
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 • 開催情報は公式サイトにて告知。
Slide 14
Slide 14 text
募集
Slide 15
Slide 15 text
数多くのフォーム
Slide 16
Slide 16 text
トーク応募フォーム
Slide 17
Slide 17 text
スポンサー申込フォーム
Slide 18
Slide 18 text
当日スタッフ募集フォーム
Slide 19
Slide 19 text
スピーカーディナー参加フォーム
Slide 20
Slide 20 text
フォームが増えるたびに いちいちプログラムを作るのか…
Slide 21
Slide 21 text
否
Slide 22
Slide 22 text
ではどうするか
Slide 23
Slide 23 text
No content
Slide 24
Slide 24 text
No content
Slide 25
Slide 25 text
iOSDCを支える技術
Slide 26
Slide 26 text
iOSDCを支える技術
Slide 27
Slide 27 text
今日のテーマ Drupal 8 - backend of iOSDC iOSDCを支えるDrupal8 Today’s theme
Slide 28
Slide 28 text
Googleフォーム & スプレッドシート
Slide 29
Slide 29 text
Google フォーム • フォームを作るサービス。 • いくつかの入力形式。 • テキスト入力 • チェックボックス • ラジオボタン • 複数ページ対応なども可能。 • アンケート的な集計も可。
Slide 30
Slide 30 text
Google スプレッドシート • Excel • 複数人での同時編集可。 • Googleフォームの入力内容を 流し込める • APIからアクセスできる。
Slide 31
Slide 31 text
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー • デザインができる • Webサイトができる カンファレンス × CMS
Slide 32
Slide 32 text
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー • デザインができる • Webサイトができる カンファレンス × CMS
Slide 33
Slide 33 text
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー • デザインができる • Webサイトができる カンファレンス × CMS オンラインコラボレーション
Slide 34
Slide 34 text
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー • デザインができる • Webサイトができる カンファレンス × CMS オンラインコラボレーション 情報公開
Slide 35
Slide 35 text
No content
Slide 36
Slide 36 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録
Slide 37
Slide 37 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募
Slide 38
Slide 38 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ
Slide 39
Slide 39 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
Slide 40
Slide 40 text
Case 1: Googleフォーム → Drupal
Slide 41
Slide 41 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
Slide 42
Slide 42 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン Google API + CRON
Slide 43
Slide 43 text
Googleスプレッドシートアクセス • 2通りの方法: • Google Developers Consoleで認証情報を設定して、OSSの PHP用モジュールをインストール • データ取得用のURLからデータ取得 • iOSDCでは後者を使用。 • データが読めれば良かった。(書き込みは不要) • 後者は認証が不要でラク。 • どちらにしても値のアクセスはやや面倒。
Slide 44
Slide 44 text
function get_cfps(){ $url = 'https://spreadsheets.google.com/feeds/cells/xxx/yyy/public/values?alt=json'; $json = file_get_contents($url); $data = json_decode($json, true); // CfP parser used as generator function sheet_parser($data){ $row_cursor = 1; $cols = []; foreach ($data['feed']['entry'] as $cell){ $c = $cell['gs$cell']; $row = intval($c['row']); if ($row !== $row_cursor){ yield $cols; $cols = []; $row_cursor = $row; } $cols[] = trim($c['$t']); } } $rows = sheet_parser($data); $cfps = []; foreach ($rows as $row){ if (! trim(implode('', $row))){ break; } $cfps[] = $row; } array_shift($cfps); return $cfps; }
Slide 45
Slide 45 text
function get_cfps(){ $url = 'https://spreadsheets.google.com/feeds/cells/xxx/yyy/public/values?alt=json'; $json = file_get_contents($url); $data = json_decode($json, true); // CfP parser used as generator function sheet_parser($data){ $row_cursor = 1; $cols = []; foreach ($data['feed']['entry'] as $cell){ $c = $cell['gs$cell']; $row = intval($c['row']); if ($row !== $row_cursor){ yield $cols; $cols = []; $row_cursor = $row; } $cols[] = trim($c['$t']); } } $rows = sheet_parser($data); $cfps = []; foreach ($rows as $row){ if (! trim(implode('', $row))){ break; } $cfps[] = $row; } array_shift($cfps); return $cfps; } JSONでデータ取得
Slide 46
Slide 46 text
function get_cfps(){ $url = 'https://spreadsheets.google.com/feeds/cells/xxx/yyy/public/values?alt=json'; $json = file_get_contents($url); $data = json_decode($json, true); // CfP parser used as generator function sheet_parser($data){ $row_cursor = 1; $cols = []; foreach ($data['feed']['entry'] as $cell){ $c = $cell['gs$cell']; $row = intval($c['row']); if ($row !== $row_cursor){ yield $cols; $cols = []; $row_cursor = $row; } $cols[] = trim($c['$t']); } } $rows = sheet_parser($data); $cfps = []; foreach ($rows as $row){ if (! trim(implode('', $row))){ break; } $cfps[] = $row; } array_shift($cfps); return $cfps; } JSONでデータ取得 セルが1つずつJSONデータに 入っているのですべてループし て取り出す。
Slide 47
Slide 47 text
No content
Slide 48
Slide 48 text
列: 1, 行: 1, データ形式: テキ スト, データ: “タイムスタンプ”
Slide 49
Slide 49 text
列: 2, 行: 1, データ形式: テキ スト, データ: “トーク時間”
Slide 50
Slide 50 text
列: 3, 行: 1, データ形式: テキ スト, データ: “トークタイトル”
Slide 51
Slide 51 text
列: 3, 行: 1, データ形式: テキ スト, データ: “トークタイトル” TSURAI
Slide 52
Slide 52 text
Drupalのcronタスク get('cfp_importer.cron_last_run'); Drupal::logger('cfp_importer')->debug( 'Now: '.date('Y/m/d H:i:s', REQUEST_TIME)."\n". 'Last run : '.date('Y/m/d H:i:s', $last_run)."\n". 'Latest hour: '.date('Y/m/d H:i:s', $the_hour)); // Skip this cron if cron run after latest hour. if (($last_run) and ($last_run > $the_hour)){ //Drupal::logger('cfp_importer')->debug('Skip cron'); //return; } (略) // Update cron_last_run to next time. Drupal::state()->set('cfp_importer.cron_last_run', REQUEST_TIME); }
Slide 53
Slide 53 text
Case 2: PassMarket (CSV) → Drupal
Slide 54
Slide 54 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
Slide 55
Slide 55 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン + モジュール
Slide 56
Slide 56 text
No content
Slide 57
Slide 57 text
No content
Slide 58
Slide 58 text
No content
Slide 59
Slide 59 text
PassMarket(CSV) → Drupal • 参加者データ(CSV)ダウンロードにパスワード を要求される。 • TSURAI • Guzzleとかcurlとかで頑張ってやれば出来なくは ない。 • 時限でしか使わないモノで「頑張る」のも TSURAI。
Slide 60
Slide 60 text
頑張らない
Slide 61
Slide 61 text
こうした • 手動でCSVダウンロード • 諸般の事情で複数のイベントがあるので6回ほど同じ手順を繰 り返してダウンロード • Dropboxで本番サーバに同期 • Dropbox for Linuxを使う。 • ローカルPCに置いたデータが本番サーバに同期される • 自作モジュールでインポート • Drupalユーザを作成する • 特定のディレクトリのCSVを全部開いてインポート
Slide 62
Slide 62 text
こうした • 手動でCSVダウンロード • 諸般の事情で複数のイベントがあるので6回ほど同じ手順を繰 り返してダウンロード • Dropboxで本番サーバに同期 • Dropbox for Linuxを使う。 • ローカルPCに置いたデータが本番サーバに同期される • 自作モジュールでインポート • Drupalユーザを作成する • 特定のディレクトリのCSVを全部開いてインポート 職人の手による暖かみのある手作業
Slide 63
Slide 63 text
: foreach ($lines as $line){ if (! $line){ continue; } $cols = explode(",", $line); $email = substr($cols[6], 1, -1); $order_no = substr($cols[0], 1, -1); $nids_order_no = \Drupal::entityQuery('user') ->condition('field_ticket_order_no', $order_no) ->execute(); $nids_email = \Drupal::entityQuery('user') ->condition('mail', $email) ->execute(); if ((count($nids_order_no) > 0) or (count($nids_email) > 0)){ // Existing user : } else { // New user. : } } :
Slide 64
Slide 64 text
: foreach ($lines as $line){ if (! $line){ continue; } $cols = explode(",", $line); $email = substr($cols[6], 1, -1); $order_no = substr($cols[0], 1, -1); $nids_order_no = \Drupal::entityQuery('user') ->condition('field_ticket_order_no', $order_no) ->execute(); $nids_email = \Drupal::entityQuery('user') ->condition('mail', $email) ->execute(); if ((count($nids_order_no) > 0) or (count($nids_email) > 0)){ // Existing user : } else { // New user. : } } : ファイルを開いて 行でループ
Slide 65
Slide 65 text
: foreach ($lines as $line){ if (! $line){ continue; } $cols = explode(",", $line); $email = substr($cols[6], 1, -1); $order_no = substr($cols[0], 1, -1); $nids_order_no = \Drupal::entityQuery('user') ->condition('field_ticket_order_no', $order_no) ->execute(); $nids_email = \Drupal::entityQuery('user') ->condition('mail', $email) ->execute(); if ((count($nids_order_no) > 0) or (count($nids_email) > 0)){ // Existing user : } else { // New user. : } } : 繰り返し実行するので 存在チェックが必要。 ファイルを開いて 行でループ
Slide 66
Slide 66 text
Case 3: Drupal → Local PC
Slide 67
Slide 67 text
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン API + CRON + モジュール
Slide 68
Slide 68 text
Drupal → Local PC • ユーザにフィールドタイプ「画像」のフィールドを 追加。 • 画像ファイルのアップロード先からDropbox対象の ディレクトリにシンボリックリンク。 • アップロードされた画像が何もしなくてもローカルPCに! • 何も考えずに作るとファイル名=アップロードされ たファイル名。これでは誰だかわからない
Slide 69
Slide 69 text
アップロードファイル名を変更する • ユーザが画像ファイルをアップロードした時に ユーザにユニークなファイル名にリネームする。 function user_image_rename_file_insert($file){ $parts = pathinfo($file->getFilename()); $order_no = get_order_no_by_user_id($file->getOwnerId()); $filename = $order_no.".".$parts['extension']; $uri = 'public://avatars/' . $filename; $file = file_move($file, $uri); }
Slide 70
Slide 70 text
アップロードファイル名を変更する • ユーザが画像ファイルをアップロードした時に ユーザにユニークなファイル名にリネームする。 function user_image_rename_file_insert($file){ $parts = pathinfo($file->getFilename()); $order_no = get_order_no_by_user_id($file->getOwnerId()); $filename = $order_no.".".$parts['extension']; $uri = 'public://avatars/' . $filename; $file = file_move($file, $uri); } フック
Slide 71
Slide 71 text
まとめ • iOSDCはDrupalが支えている • サイトが無い状態でデータが発生する場合: • Googleフォーム & スプレッドシートを使うと、 後が(比較的)ラク。 • モジュールを少し作るのがお勧め。難易度低め。 • Drupal 8はもう使えるか。 • ふつうに使える。 • Drupal 7の知識は半分使える感じでは。
Slide 72
Slide 72 text
Thanks WE ARE HIRING @tomzoh
Slide 73
Slide 73 text
…
Slide 74
Slide 74 text
おや…?
Slide 75
Slide 75 text
Drupalの様子が…?
Slide 76
Slide 76 text
!!
Slide 77
Slide 77 text
No content
Slide 78
Slide 78 text
SA-CONTRIB-2016-039 • バリデーションが不十分でPHPコードが実行され る可能性がある。 • モジュールが有効化されていなくても影響を受け る。
Slide 79
Slide 79 text
SA-CONTRIB-2016-039 • バリデーションが不十分でPHPコードが実行され る可能性がある。 • モジュールが有効化されていなくても影響を受け る。
Slide 80
Slide 80 text
どういう修正が当たっているか • coder_upgrade/scripts/coder_upgrade.run.php にこの対策としてパッチが当たっている。 • 実行環境がCLI(Command Line Interface) でなければ終了する。 • つまりApacheから実行されると都合が悪い。
Slide 81
Slide 81 text
パッチが当たっていないとどうなるか
Slide 82
Slide 82 text
パッチが当たっていないとどうなるか ここに http://example.com/poison.txt とかを突っ込める
Slide 83
Slide 83 text
どこだ!? $parameters = unserialize(file_get_contents($path)); : // Extract individual array items by key. foreach ($parameters as $key => $variable) { $$key = $variable; } : // Load coder_upgrade bootstrap code. $path = $_coder_upgrade_modules_base . '/coder/coder_upgrade'; $files = array( 'coder_upgrade.inc', 'includes/main.inc', 'includes/utility.inc', ); : foreach ($files as $file) { require_once DRUPAL_ROOT . '/' . $path . "/$file"; }
Slide 84
Slide 84 text
どこだ!? $parameters = unserialize(file_get_contents($path)); : // Extract individual array items by key. foreach ($parameters as $key => $variable) { $$key = $variable; } : // Load coder_upgrade bootstrap code. $path = $_coder_upgrade_modules_base . '/coder/coder_upgrade'; $files = array( 'coder_upgrade.inc', 'includes/main.inc', 'includes/utility.inc', ); : foreach ($files as $file) { require_once DRUPAL_ROOT . '/' . $path . "/$file"; } 既存のクラスのデストラクタが動く
Slide 85
Slide 85 text
どこだ!? $parameters = unserialize(file_get_contents($path)); : // Extract individual array items by key. foreach ($parameters as $key => $variable) { $$key = $variable; } : // Load coder_upgrade bootstrap code. $path = $_coder_upgrade_modules_base . '/coder/coder_upgrade'; $files = array( 'coder_upgrade.inc', 'includes/main.inc', 'includes/utility.inc', ); : foreach ($files as $file) { require_once DRUPAL_ROOT . '/' . $path . "/$file"; } 既存のクラスのデストラクタが動く うまく文字列を作ればローカルファイルを実行できる
Slide 86
Slide 86 text
デモ IUUQESVQBMTJUFTBMMNPEVMFTDPEFSDPEFS@VQHSBEF TDSJQUTDPEFS@VQHSBEFSVOQIQ pMFIUUQ"' 'ESVQBM'QPJTPOUYU IUUQESVQBMTJUFTBMMNPEVMFTDPEFSDPEFS@VQHSBEF TDSJQUTDPEFS@VQHSBEFSVOQIQ
Slide 87
Slide 87 text
もう1つありましたね。
Slide 88
Slide 88 text
No content
Slide 89
Slide 89 text
まくらばなし • 太古の昔から、UNIXでは環境変数にシステムの設 定を入れるという習慣があった。 • システムのプロキシ設定は環境変数 HTTP_PROXY に入れることになっていた。 • UNIXにCGIの仕組みが出来たときに、HTTPリクエ ストの各種パラメタを環境変数としてプログラム に受け渡すことになった。
Slide 90
Slide 90 text
httpoxyは なぜヤバい? • 外部から、HTTPリクエストヘッダに Proxy ヘッダ を付けてリクエストすると、Apacheは環境変数 HTTP_PROXY にその中身を展開してプログラムを 実行する。 • 攻撃者が指定したプロキシを使わせることができ る。
Slide 91
Slide 91 text
攻撃対象 決済サービス
Slide 92
Slide 92 text
攻撃対象 攻撃者 決済サービス
Slide 93
Slide 93 text
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス
Slide 94
Slide 94 text
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス
Slide 95
Slide 95 text
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス 攻撃者の設置した プロキシ x.x.x.x
Slide 96
Slide 96 text
Thanks WE ARE HIRING @tomzoh