Skip to content

入力+検査=型安全

公開日:

Download PDF

スライドテキスト

Page 1

入力+検査=型安全

Make PHP type safe by validating input

pixiv Inc.
USAMI Kenta

2023-09-16

PHPカンファレンス沖縄2023

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • ピクシブ株式会社 pixiv事業本部 Webエンジニアリングチーム PHPer
    • 2012年末から現職、APIとかCIとかいろいろなところを見つめてきました
    • 最近はピクシブ百科事典(dic.pixiv.net)も開発しています
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にちょっとこだわりのある素人 (spcamp2010)

Page 3

Page 4

Page 5

さて

Page 6

型、つけてますか?

Page 7

PHPの進化は 型宣言の進化

Page 8

型がついていない関数(PHP5)

function add($a, $b) {
return $a + $b;
}

Page 9

スカラー型宣言(PHP7)

int + intって
本当にintなの?

function add(int $a, int $b): int {
return $a + $b;
}

Page 10

広い値をとるにはfloatが必要

ひとつの解決策では
あるが… 不必要に(cid:152)oat
を強制するのか

function add(float $a, float $b): float {
return $a + $b;
}

Page 11

PHPDocの型注釈

(アノテーション)

/**
* @param int|float $a

あえて型宣言を省略する

* @param int|float $b
* @return int|float
*/

function add($a, $b) {
return $a + $b;
}

Page 12

ユニオン型宣言 (PHP8.0)

function add(int|float $a, int|float $b): int|float {
return $a + $b;
}

Page 13

いたるところに型が 書けるようになった

Page 14

PHP Static Analysis Tool

Page 15

PHPStanとは

  • 2016年から開発されているPHPの静的解析ツール
    • Ondřej Mirtesさんの個人プロジェクト、2021年からフルタイム開発
  • 開発当初は純粋な静的解析ではなく実行時リフレクションを用いることで
    高速な解析を実現していた
    • 現在は静的解析がデフォルトで、レガシープロジェクトに導入しやすくなった
  • その他のPHP静的解析ツールにはPsalm, Phan, Qodana(PhpStorm)

Page 16

Page 17

201X年、PHPは 型の炎に包まれた!!

Page 18

いまやPHPは
静的型付きと言っても
過言ではない
(本当か…?)

Page 19

だが型なしは
滅びていなかった

Page 20

型なしは
どこからくるの?

Page 21

今回のお題

Page 22

そうです

Page 23

今回のお題

Page 24

みなさんに
覚えておいて
ほしいこと

Page 25

型なしとは何か

Page 26

Validation

Page 27

Validation
Sanitize

Page 28

Validation
Sanitize Escape

Page 29

Validation

これは気に
しなくていい

Sanitize Escape

Page 30

自信をもって
説明できますか?

Page 31

…ここから本題

Page 32

クエリパラメータから
ID値をとりたい

Page 33

/** IDから記事を取得 */

function getById(int $id): Article
{
// 中でデータベースに問い合わせ

}

Page 34

実際あぶない

$id = $_GET['id'];
$article=getById($id);

Page 35

そもそも何者

$id = $_GET['id'];
$article=getById($id);

Page 36

ここでブラウザ開く

https://phpstan.org/try

Page 37

型なしとはどういう状態か

  • ある式の値の型が静的に定まっていない (mixed, TSのunknown/any)
  • 型付けされた関数に渡したときに要求された文脈に従っているか
    (関数呼び出しをしたときにTypeErrorが出ないか安全だと言いきれない)
  • PHPではいろんなところから型なしが吹き出す
    • スーパーグローバル、eval、json_decode()、unserialize ()、
      PDOStatement::fetch()

Page 38

// ?id=1
$_GET['id'] === '1';

Page 39

// ?id=a
$_GET['id'] === 'a';

Page 40

// ?id[]=a
$_GET['id'] === ['a'];

Page 41

// ?id[foo]=bar $_GET['id'] ===
['foo' => 'bar'];

Page 42

キャストすれば
いいのか

Page 43

危険ではないが
よくはない

$id = (int)$_GET['id'];

Page 44

// ?id=32x
$id = (int)$_GET['id'];
$id === 32;

Page 45

if (is_numeric($_GET['id'])) {

throw new BadRequestException();
}

$id = (int)$_GET['id'];

Page 46

is_numeric()

Page 47

整数 123

小数 1.23

指数表記

1.844674407371E+1

前後にスペース

Page 48

だいたいのIDは
正の整数だけ

Page 49

// ?id=1.23
if (is_numeric($_GET['id'])) {

throw new BadRequestException();
}

$id = (int)$_GET['id'];

Page 50

安全に

int|false

$id = filter_var(
$_GET['id'] ?? '',
FILTER_VALIDATE_INT
);

Page 51

安全に

$id = filter_var(

int|false

$_GET['id'] ?? '',
FILTER_VALIDATE_INT,
['options' => [
'min_range' => 1,
]]);

Page 52

ここでブラウザ開く

https://phpstan.org/try
https://php-play.dev/

Page 53

filter_var()

Page 54

うまいこと値の
バリデーションできる
標準関数

Page 55

……話は遡り
2007年

Page 56

伝説のプレゼン

Page 57

ホルモンのPHP (前半)

  • Rasmus LerdorfがPHPを作った歴史的経緯を話している
  • 昔のPHPコードとか開発体制とか
  • 構文解析とか苦手なのでPHPの構文がめちゃくちゃだったという自虐
  • 利己的な動機で始めたプロジェクトだが開発者を惹きつけるように工夫して
    いる様子
  • 自宅サーバに入れたDrupalが遅いのでプロファイリングした

Page 58

ホルモンのPHP (後半)

  • 世の中にセキュリティの問題が多いウェブサイトが多すぎる
  • IEとかFlashとかAdobe ReaderとかUTF-7とか罠がいっぱい
  • 俺たちのウェブは完全に壊れている。特にXSSがヤバイ。JS実行されちゃう。
  • 58

Page 59

ホルモンのPHP (後半)

  • 世の中にセキュリティの問題が多いウェブサイトが多すぎる
  • IEとかFlashとかAdobe ReaderとかUTF-7とか罠がいっぱい
  • 俺たちのウェブは完全に壊れている。特にXSSがヤバイ。JS実行されちゃう。
  • だからFlter関数作ったよ! サニタイズすれば絶対にXSS起こらない!

Page 60

フィルタには
サニタイズ機能もある

Page 61

フィルタには
サニタイズ機能もある
使わないで!!

Page 62

サニタイズとは何か

Page 63

わからん

Page 64

???

Page 65

今年書いた記事

Page 66

フィルタ関数を
使えば解決するのか

Page 67

今年書いた記事

Page 68

なんだよ
外部公開してない
ライブラリの自慢かよ

Page 69

キタカミの里から
帰ってきたら
公開したい!!

Page 70

対応予定

スーパーグローバル

($_XXX)

PSR-7 ServerRequest