Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
iOSDCを支えるDrupal8 / Drupal 8 - backend of iOSDC
Search
HASEGAWA Tomoki
July 25, 2016
Technology
100
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
iOSDCを支えるDrupal8 / Drupal 8 - backend of iOSDC
Drupal Meetup #1のスライドです。
HASEGAWA Tomoki
July 25, 2016
More Decks by HASEGAWA Tomoki
See All by HASEGAWA Tomoki
3Dプリンタでコレはこう作る! - マルチディスプレイ用モニタアームマウンタの作り方 / Made with a 3D Printer: DIY Multi-Display Monitor Arm Mount
tomzoh
0
94
デシリアライゼーションを理解する / Inside Deserialization
tomzoh
0
970
PHPer Book Revue: CPUの創りかた / How to Build a CPU
tomzoh
0
92
超入門3Dプリンタ: 生活を便利にするモノを作ろう / Getting Started with 3D Printing: Making small things that quietly improve your daily life.
tomzoh
0
93
最新ハードウェアの中の8ビットCPU / The Hidden Power of 8-Bit CPUs in Modern Hardware
tomzoh
0
170
PHPからはじめるコンピュータアーキテクチャ / From Scripts to Silicon: A Journey Through the Layers of Computing Hiroshima 2025 Edition
tomzoh
0
430
PHPからはじめるコンピュータアーキテクチャ / From Scripts to Silicon: A Journey Through the Layers of Computing
tomzoh
5
900
低レイヤを知りたいPHPerのためのCコンパイラ作成入門 完全版 / Building a C Compiler for PHPers Who Want to Dive into Low-Level Programming - Expanded
tomzoh
6
4.8k
カンファレンスのつくりかた / The Conference Code: What Makes It All Work
tomzoh
11
2.4k
Other Decks in Technology
See All in Technology
FinOps × AIエージェントで実現する コストインシデントの自動調査
oasis1994liveforever
0
150
日本 Fintech 未来予測レポート 2027〜2028年(手動編集版)
8maki
0
2.4k
スキルと MCP ツール、責務をどう分けるか? AI が迷わないインターフェース設計の戦略
cdataj
1
1.1k
連合学習と機密コンピューティング
lycorptech_jp
PRO
0
120
Kubernetesにおける学習基盤とLLMOpsの概要
ry
1
310
エラーバジェットのアラートのタイミングを考える.pdf
kairim0
0
150
ルールやカスタム機能、どう活かす?ハンズオンで体感するIBM Bobの出力コントロール
muehara
1
170
機械学習を「社会実装」するということ 2026年夏版 / Social Implementation of Machine Learning June 2026 Version
moepy_stats
6
2.4k
2026 TECHFRESH 畢業分享會 - 開發日常大解密!從領域驅動到企業級上線
line_developers_tw
PRO
0
1.1k
フィジカル版Github Onshapeの紹介
shiba_8ro
0
270
Claude Codeとのおしゃべりでセマンティックモデルの定義からダッシュボード作成まで完成させる
nic_sugiyama
0
120
LayerXにおけるセキュリティ管理の現在地と次の一手
tosho
0
220
Featured
See All Featured
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
940
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Statistics for Hackers
jakevdp
799
230k
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
180
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
590
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
310
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
Building the Perfect Custom Keyboard
takai
2
800
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Transcript
iOSDCを支えるDrupal8 Drupal 8 - backend of iOSDC 長谷川智希 HASEGAWA Tomoki
この資料は後ほど公開します This slide will be published later.
ライフワーク: 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
None
None
WE ARE HIRING!! Web Development Mobile App Development ( )
(iOS, Android) http://www.dgcircus.com Omotesando, Tokyo
今日のテーマ Drupal 8 - backend of iOSDC iOSDCを支えるDrupal8 Today’s theme
iOSDC https://iosdc.jp
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 •
開催情報は公式サイトにて告知。
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 •
開催情報は公式サイトにて告知。
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 •
開催情報は公式サイトにて告知。
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 •
開催情報は公式サイトにて告知。
技術カンファレンス • スピーカーを募集して技術トークをする。 • チケットを販売。 • スポンサーを募集して快適度アップ。 • 運営は公募したボランティアスタッフ。 •
開催情報は公式サイトにて告知。
募集
数多くのフォーム
トーク応募フォーム
スポンサー申込フォーム
当日スタッフ募集フォーム
スピーカーディナー参加フォーム
フォームが増えるたびに いちいちプログラムを作るのか…
否
ではどうするか
None
None
iOSDCを支える技術
iOSDCを支える技術
今日のテーマ Drupal 8 - backend of iOSDC iOSDCを支えるDrupal8 Today’s theme
Googleフォーム & スプレッドシート
Google フォーム • フォームを作るサービス。 • いくつかの入力形式。 • テキスト入力 • チェックボックス
• ラジオボタン • 複数ページ対応なども可能。 • アンケート的な集計も可。
Google スプレッドシート • Excel • 複数人での同時編集可。 • Googleフォームの入力内容を 流し込める •
APIからアクセスできる。
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー
• デザインができる • Webサイトができる カンファレンス × CMS
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー
• デザインができる • Webサイトができる カンファレンス × CMS
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー
• デザインができる • Webサイトができる カンファレンス × CMS オンラインコラボレーション
• 開催が決まる • 各種募集を行う • コアスタッフ • トーク • スポンサー
• デザインができる • Webサイトができる カンファレンス × CMS オンラインコラボレーション 情報公開
None
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
Case 1: Googleフォーム → Drupal
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン Google
API + CRON
Googleスプレッドシートアクセス • 2通りの方法: • Google Developers Consoleで認証情報を設定して、OSSの PHP用モジュールをインストール • データ取得用のURLからデータ取得
• iOSDCでは後者を使用。 • データが読めれば良かった。(書き込みは不要) • 後者は認証が不要でラク。 • どちらにしても値のアクセスはやや面倒。
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; }
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でデータ取得
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データに 入っているのですべてループし て取り出す。
None
列: 1, 行: 1, データ形式: テキ スト, データ: “タイムスタンプ”
列: 2, 行: 1, データ形式: テキ スト, データ: “トーク時間”
列: 3, 行: 1, データ形式: テキ スト, データ: “トークタイトル”
列: 3, 行: 1, データ形式: テキ スト, データ: “トークタイトル” TSURAI
Drupalのcronタスク <?php function cfp_importer_cron() { // Calculate latest hour. $the_hour
= intval(REQUEST_TIME / 3600) * 3600; $last_run = Drupal::state()->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); }
Case 2: PassMarket (CSV) → Drupal
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン +
モジュール
None
None
None
PassMarket(CSV) → Drupal • 参加者データ(CSV)ダウンロードにパスワード を要求される。 • TSURAI • Guzzleとかcurlとかで頑張ってやれば出来なくは
ない。 • 時限でしか使わないモノで「頑張る」のも TSURAI。
頑張らない
こうした • 手動でCSVダウンロード • 諸般の事情で複数のイベントがあるので6回ほど同じ手順を繰 り返してダウンロード • Dropboxで本番サーバに同期 • Dropbox
for Linuxを使う。 • ローカルPCに置いたデータが本番サーバに同期される • 自作モジュールでインポート • Drupalユーザを作成する • 特定のディレクトリのCSVを全部開いてインポート
こうした • 手動でCSVダウンロード • 諸般の事情で複数のイベントがあるので6回ほど同じ手順を繰 り返してダウンロード • Dropboxで本番サーバに同期 • Dropbox
for Linuxを使う。 • ローカルPCに置いたデータが本番サーバに同期される • 自作モジュールでインポート • Drupalユーザを作成する • 特定のディレクトリのCSVを全部開いてインポート 職人の手による暖かみのある手作業
: 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. : } } :
: 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. : } } : ファイルを開いて 行でループ
: 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. : } } : 繰り返し実行するので 存在チェックが必要。 ファイルを開いて 行でループ
Case 3: Drupal → Local PC
iOSDCサイト •応募されたトーク •採用されたトーク •チケットを購入したユーザ •写真登録 トーク応募 購入者データ 参加者 アイコン API
+ CRON + モジュール
Drupal → Local PC • ユーザにフィールドタイプ「画像」のフィールドを 追加。 • 画像ファイルのアップロード先からDropbox対象の ディレクトリにシンボリックリンク。
• アップロードされた画像が何もしなくてもローカルPCに! • 何も考えずに作るとファイル名=アップロードされ たファイル名。これでは誰だかわからない
アップロードファイル名を変更する • ユーザが画像ファイルをアップロードした時に ユーザにユニークなファイル名にリネームする。 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); }
アップロードファイル名を変更する • ユーザが画像ファイルをアップロードした時に ユーザにユニークなファイル名にリネームする。 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); } フック
まとめ • iOSDCはDrupalが支えている • サイトが無い状態でデータが発生する場合: • Googleフォーム & スプレッドシートを使うと、 後が(比較的)ラク。
• モジュールを少し作るのがお勧め。難易度低め。 • Drupal 8はもう使えるか。 • ふつうに使える。 • Drupal 7の知識は半分使える感じでは。
Thanks WE ARE HIRING @tomzoh
…
おや…?
Drupalの様子が…?
!!
None
SA-CONTRIB-2016-039 • バリデーションが不十分でPHPコードが実行され る可能性がある。 • モジュールが有効化されていなくても影響を受け る。
SA-CONTRIB-2016-039 • バリデーションが不十分でPHPコードが実行され る可能性がある。 • モジュールが有効化されていなくても影響を受け る。
どういう修正が当たっているか • coder_upgrade/scripts/coder_upgrade.run.php にこの対策としてパッチが当たっている。 • 実行環境がCLI(Command Line Interface) でなければ終了する。 •
つまりApacheから実行されると都合が悪い。
パッチが当たっていないとどうなるか
パッチが当たっていないとどうなるか ここに http://example.com/poison.txt とかを突っ込める
どこだ!? $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"; }
どこだ!? $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"; } 既存のクラスのデストラクタが動く
どこだ!? $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"; } 既存のクラスのデストラクタが動く うまく文字列を作ればローカルファイルを実行できる
デモ IUUQESVQBMTJUFTBMMNPEVMFTDPEFSDPEFS@VQHSBEF TDSJQUTDPEFS@VQHSBEFSVOQIQ pMFIUUQ"' 'ESVQBM'QPJTPOUYU IUUQESVQBMTJUFTBMMNPEVMFTDPEFSDPEFS@VQHSBEF TDSJQUTDPEFS@VQHSBEFSVOQIQ
もう1つありましたね。
None
まくらばなし • 太古の昔から、UNIXでは環境変数にシステムの設 定を入れるという習慣があった。 • システムのプロキシ設定は環境変数 HTTP_PROXY に入れることになっていた。 • UNIXにCGIの仕組みが出来たときに、HTTPリクエ
ストの各種パラメタを環境変数としてプログラム に受け渡すことになった。
httpoxyは なぜヤバい? • 外部から、HTTPリクエストヘッダに Proxy ヘッダ を付けてリクエストすると、Apacheは環境変数 HTTP_PROXY にその中身を展開してプログラムを 実行する。
• 攻撃者が指定したプロキシを使わせることができ る。
攻撃対象 決済サービス
攻撃対象 攻撃者 決済サービス
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス
攻撃対象 攻撃者 Proxy: x.x.x.x 決済サービス 攻撃者の設置した プロキシ x.x.x.x
Thanks WE ARE HIRING @tomzoh