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
110
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
ファミコンでPHPを動かす / PHP on the Famicom
tomzoh
2
760
3Dプリンタでコレはこう作る! - マルチディスプレイ用モニタアームマウンタの作り方 / Made with a 3D Printer: DIY Multi-Display Monitor Arm Mount
tomzoh
0
140
デシリアライゼーションを理解する / Inside Deserialization
tomzoh
0
1.2k
PHPer Book Revue: CPUの創りかた / How to Build a CPU
tomzoh
0
120
超入門3Dプリンタ: 生活を便利にするモノを作ろう / Getting Started with 3D Printing: Making small things that quietly improve your daily life.
tomzoh
0
110
最新ハードウェアの中の8ビットCPU / The Hidden Power of 8-Bit CPUs in Modern Hardware
tomzoh
0
330
PHPからはじめるコンピュータアーキテクチャ / From Scripts to Silicon: A Journey Through the Layers of Computing Hiroshima 2025 Edition
tomzoh
0
460
PHPからはじめるコンピュータアーキテクチャ / From Scripts to Silicon: A Journey Through the Layers of Computing
tomzoh
5
960
低レイヤを知りたいPHPerのためのCコンパイラ作成入門 完全版 / Building a C Compiler for PHPers Who Want to Dive into Low-Level Programming - Expanded
tomzoh
6
4.9k
Other Decks in Technology
See All in Technology
【Google Cloud Next Tokyo'26】Gemini Enterprise と Oracle AI Database で実現する、業務データ活用を実現する AI エージェント実装
shisyu_gaku
0
210
モダンフロントエンド 開発研修
recruitengineers
PRO
1
370
AIがAPIを書く時代に、私たちは何を設計すべきか
nagix
0
220
オートロックマンションなのに、各部屋は施錠なし!? 攻撃者が組織内ネットワークで大暴れする理由 / The Front Door Is Locked, but the Rooms Are Wide Open: Why Attackers Move Freely Inside Enterprise Networks
nttcom
0
1.5k
Goでデータパイプラインを作ろう
sansantech
PRO
0
260
AIは実装を速くする。では、私たちは何を今作るべきか?-立場を越えてリリースに向き合ったチーム開発の実践 / 20260801 Hiromi Nakaya and Naoki Takahashi
shift_evolve
PRO
3
390
Contract One Engineering Unit 紹介資料
sansan33
PRO
0
19k
名古屋の市バスGTFS-JPデータ×スガキヤ 最寄りバス停検索をAmazon ElastiCache Serverless for Valkeyで最適化する
usanchuu
1
310
Agent 時代の Kaggle 展望 / kaggle-in-the-agentic-era
upura
0
440
AI駆動開発は個人技からチーム戦へ:組織でAIを使いこなすための実践設計
moongift
PRO
0
450
モバイルアプリ開発概論2026
recruitengineers
PRO
1
360
Service Connect 上のサービスに ECS Service の外側から到達できなかった話
ota1022
1
130
Featured
See All Featured
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
250
The untapped power of vector embeddings
frankvandijk
2
1.8k
Six Lessons from altMBA
skipperchong
29
4.4k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
520
Rails Girls Zürich Keynote
gr2m
96
14k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Code Reviewing Like a Champion
maltzj
528
40k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
160
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Exploring anti-patterns in Rails
aemeredith
3
450
Designing for Timeless Needs
cassininazir
1
430
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