phpcon2018に参加してきた

phpcon2018 に参加してきました。

PHP Conference 2018 - #phpcon2018

以下4セッションを拝聴させていただきました。

ランサーズのCakePHP1.3→Cake2.8移行

スライド: CakePHP1.3 → CakePHP 2.8バージョンアップ - Speaker Deck

PHPCakePHPのバージョンアップに、2,3名体制で、おおよそ2年かかったとのこと。

PHPよりも、フレームワークのバージョンアップのほうが辛そうな印象。

CakePHP1.3 => CakePHP2.8 を同居する(コントローラをindex.phpでスイッチする)作戦は、リソースが取れなくガッとバージョンアップできないかつ平行で開発が進んでいる場合でも、少しずつリリースできるので、よさそうと思った。

ユニットテストが入れられないレガシーなソースでCIが回せるようになった

スライド:

ユニットテストが入れられないレガシーなソースでCIが回せるようになった - Speaker Deck

codeception, Seleniumを使って、受け入れテスト(End-to-Endのテスト)をCIで回すようにしたお話。

Codeception はPHPのテストフレームワークとのことですが、知らなかったので今度調べてみよう。 https://codeception.com/

PHPを検査するPHPを書く

スライド:

PHPを検査するPHPを書く / Write PHP inspection by PHP - Speaker Deck

Linterが内部でどのようにソースコード解析を行っているのか、仕組みの発表だった。 GitHubのコードレビューを自動化できるサービスを作られている Siderさんのお話。

phpは触り始めたのが、最近なのですが、 こういう観点からも学んでいけると良さそうと思った。

PHPバージョンアップと決済テストを支えたユニットテスト

スライド:

PHPバージョンアップと決済リプレイスを支えたユニットテスト #phpcon - Speaker Deck

レガシーコードの改善に積極的に取り組まれているBASEさん レガシーコードの課題としてよくあるのが、 ユニットテストを導入したいがそもそも、そういう構造になっていない。 ということ。 それをどう乗り越えてきたかがわかる発表だった。 ユニットテストがないロールにガッと導入するようなタイミングがあれば、このスライドを改めて見直すようにしよう。

まとめ

PHPカンファレンスは実はSIer時代に一度来ていて、今回2回目なのですが、前回よりも現場の苦労やつらみ(しかし楽しいみたいな)が気持ちがわかる分、色々なことが身に沁みた一日だった。

開発環境のMySQLをdockerで動かすようにしたときのメモ

開発環境では、ローカル(PC)にMySQL使っていたのですが、dockerで動かすようにしました。 そのときのメモを残しておきます。

  • OS: Ubuntu 18.04 LTS
  • 事前準備: docker, docker-compose をインストールしておく

ローカルにインストールしているmysqlを削除する

参考: https://askubuntu.com/questions/172514/how-do-i-uninstall-mysql

sudo apt-get remove --purge 'mysql-server*' 'mysql-common'
sudo rm -r /etc/mysql
sudo rm -r /var/lib/mysql
sudo apt-get autoremove --purge

docker-compose を設定・起動

以下に合わせて、ディレクトリの構成をしておく、できたら、docker-compose up する

version: '3'
services:
  mysql:
    image: mysql:5.7
    volumes:
      - ./etc/mysql/conf.d:/etc/mysql/conf.d
      - ./var/lib/mysql:/var/lib/mysql
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
    ports:
      - 3306:3306
  • 今回は、root(パスワードなし)を前提に構築しているが、ユーザ・パスワードを指定したい場合は environment を設定する必要がある。environment の値はこちらを参考にする https://hub.docker.com/_/mysql?tab=description

  • MySQLの設定 /etc/mysql/conf.dmysql.cnf を置くようにし、その中で設定する

MySQLのクライアントをインストール

sudo apt install mysql-client libmysqlclient-dev

mysql clientは localhost を指定するとunix socketで接続しようとするので、

mysql -u root
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
mysql -u root -h localhost
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

vim ~/.my.cnf に以下を設定し、localhostを指定したときはtcpで接続するようにする。 (接続時、明示的に127.0.0.1を設定すれば問題ないので任意)

[client]
protocol=TCP

以上〜

SonarQubeを試してみる

こちらの続きです。

takapi86.hatenablog.com

セキュリティの診断ができる静的解析ツールを探していましたが、前回絞り込みを行った結果以下、3つのツールが残りました。

2018-12-09 現在、それぞれ、以下のような状況でした。

Parse: A PHP Security Scanner

https://github.com/psecio/parse

  • Star: 229
  • 最終更新: 2018-08-02
  • contributors: 7
  • その他

Pixy

https://github.com/oliverklee/pixy

  • Star: 85
  • 最終更新: 2018-01-24
  • contributors: 1
  • その他
    • JUnit が必要らしい

SonarQube

https://github.com/SonarSource/sonarqube

  • Star: 3164
  • 最終更新: 2018-12-04
  • contributors: 75
  • その他
    • phpの他に20種類以上の言語に対応しているらしい
    • 管理サーバを立てる必要があるらしい

SonarQube を試す

パッと見で、3つの内だと SonarQube が良さそうなので試してみる。

以下の方法で試せる

1. webサービスから

https://sonarcloud.io/about/sq

2. HomeBrew から

brew install sonarqube

でいけるらしい。

参考: https://qiita.com/thankkingdom/items/8abb0b329dc575f5b281

3. docker から

Docker Hub に公式のリポジトリがあるので、それを使う

https://hub.docker.com/_/sonarqube/

4. サーバをちゃんと構築

サーバにちゃんと構築するなら、この方法でやる。 https://docs.sonarqube.org/latest/setup/overview/

参考: https://dev.classmethod.jp/ci/sonarqube-source-analytics-1/

dockerで立ち上げる

一番手軽そうな docker を選びました。

今回は docker-compose を使いました。

docker-compose.yml

version: '3'
services:
  sonarqube:
    image: sonarqube:lts
    ports:
      - 9000:9000

これだけ書いて、

docker-compose up

で、ピッとサービスが上がるはずです。

その他、設定などはこちらに書いてあります。 https://hub.docker.com/_/sonarqube/

初期設定

  • ログイン

http://localhost:9000/ へアクセス

id, password は、DockerHubのページに書いてあります。

tokenの生成 と 診断したいプロジェクトの言語を選択するウィザードがでるのでよしなに入力します。

f:id:takapi86:20181210005855p:plain

次の画面のリンク先から SonarQube Scanner というソースコードの解析を行い、サーバに結果を通知するツールをダウンロードします。そして、パスの通ったディレクトリから呼び出せるように設定します。

次に、Execute the SonarQube Scanner from your computer の欄にあるコマンドを、静的解析したいコードのあるディレクトリで実行します。(なければ、ウィザード画面上部にある Skip Tutorial を押します。)

f:id:takapi86:20181210005950p:plain

Administration -> Marketplace から、phpの解析プラグインSonarPHP)を更新します。

f:id:takapi86:20181210010059p:plain

Update to XXX の部分が Update Pending になったら、画面上部に出てくる Restart ボタンを押し再起動します。

静的解析してみる

手元に静的解析したい良い感じのPHPのコードが無かったので、GETのパラメータの値を直接 shell_exec コマンドに入れてみたり、SQL文に入れてみたり(PreparedStatementなし)ないかにもヤバそうなコードを書いてテストしてみました。

先程チュートリアルで実行したものと同じコマンドを実行しました。

結果

1件

Define a constant instead of duplicating this literal "hoge_id" 3 times.

コードの内部で $_GET['hoge_id'] を3回つかっていたのですが、そこだけ怒られました。 もう少し怒ってくれることを期待していたのですが。

rulesを確認する

セキュリティに関する rules は以下、合わせて28でした。

https://rules.sonarsource.com/php/type/Vulnerability https://rules.sonarsource.com/php/type/Security%20Hotspot

これは一般的にみて、多いのか少ないのか比較していないのでわかりませんが、とりあえず今日はここまで

PHPのソースコードから脆弱性をチェックするツールの洗い出しを行ってみた

なぜか唐突にPHPソースコードから良い感じに脆弱性を検出できるようなツールがないか調べたくなったので、軽く調査しました。 ひとまず、どんなものがあるか洗い出しまで。

やりたいこと

  • タイトルの通り、PHPソースコードから脆弱性を検出できるようにしたい。
  • 最終的には、CIなどで継続的に検出しコード修正時に把握できるようにしたい。
  • ついでに、品質チェックもできれば、なおgood。
  • 有償のツールが多いが、ひとまず継続的に脆弱性を検出できるようにしたいと思っているので、最初は無償/OSSのツールを導入してみる。

選定するツールのイメージ

ずばりこれのPHP版のようなもののイメージ

https://blog.ohgaki.net/security-check-tool-for-rails-brakeman

インターネットを使って洗い出してみる

ちょっと古そうなものもあるが、このあたりを参考にしてみる

参考:

https://www.ipa.go.jp/security/technicalwatch/20140306.html http://algo13.net/php/tips/security-check.html http://kiban.nict.go.jp/annual_report/report/report20/20-20-03.pdf https://www.sonarqube.org/ https://blog.ohgaki.net/php-script-analyzers

・・・いっぱいある。

このうち、以下は除外

RIPS

  • OSS版(RIPS 0.55)は開発終了
  • また、OSS版はPHP 3, 4しかサポートしていない

PHP-Sat

  • 安定版なし、更新されていない

phpvulhunter

  • 更新が4年前から止まっている
  • ドキュメントが中国語で読めない

VisualCodeGrepper, Eir

  • 使用言語が、VB, C#ということで、Windows環境でないとダメそう

PHP-Reaper, Side Channel Analyzer, XSS code sniffer

  • 一部の脆弱性に特化したツールなので、今回の選定基準とは異なる

TaintPHP, RATS(Rought Auditing Tool for Security

  • あまり活発ではない様子

今日はここまで、何かおすすめのツールがあれば教えてください。

eval族のスコープについてまとめた

ややこしかったので、まとめました。

eval(Bindingなし)

実行中のコンテキストに出現する変数に対しての操作が可能

foo = "foo"
eval('p foo') #=> foo

eval(Bindingあり)

以下のように、Bindingオブジェクトを使うことで、以下のようにコンテキストを指定できます。

class C
  def instance_binding
    foo = "foooo"
    binding
  end
end

foo = "foo"
binding_object =  C.new.instance_binding
eval('p foo', binding_object) #=> foooo

# このようにも書ける

binding_object.eval('p foo') #=> foooo

module_eval, class_eval

module_evalclass_eval の別名です。 文字列を渡した場合とブロックを渡した場合で、どのスコープで評価されるかが変わります。

文字列を引数とした場合は、レシーバーのスコープで評価されます。

class C; end

C.class_eval(<<-EOF)
Foo = "bar"
def hoge
  p Foo
end
EOF

Foo = "foo"

ブロックを引数とした場合は、そのコンテキストのスコープで評価されます。 つまり、以下ではトップレベルで定義したことになります。

class C; end

C.class_eval do
  Foo = "bar"
  def hoge
    p Foo
  end
end

Foo = "foo"

C.new.hoge
# => warning: already initialized constant Foo
# warning: previous definition of Foo was here
# "foo"

p Object.const_get(:Foo) # => "foo"

instance_eval

module_eval, class_eval と同じく、文字列で渡すときとブロックで渡すときで変わります。

module_exec, class_exec

module_execclass_exec の別名です。

ブロックで渡されたときのスコープは、module_eval と同じです。 文字列での評価はできません。

class C; end

C.class_exec do
  Foo = "bar"
  def hoge
    p Foo
  end
end

Foo = "foo"

C.new.hoge

また、引数をとりブロック引数として、評価する式に値を渡すことができます。

class C; end

C.class_exec(:foo) do |foo|
  define_method(foo) do
    p "foo"
  end
end

C.new.foo #=> "foo"

instance_exec

module_execclass_exec と同じく、引数で値を渡すことができます。

参考

Rubyの定数参照について頭を整理した

最近は、空いた時間にRuby認定試験(Gold)の対策をやっているのですが、 本を読んでいてわかった気になっていた箇所がまぁ、多いこと。。。

練習問題をやってみると、そのへんがはっきりをわかるので良いです。

今回はかなり基本的なことなのですが、定数参照について頭を整理するためにまとめていこうと思います。

Rubyの定数を参照する順番

レキシカルスコープの探索 -> クラス探索 の順番で探索します。

例えば、以下の場合は、CONST_A が表示されます。

class A
  NAME = "CONST_A"

  def name
    NAME
  end
end

class B < A
  NAME = "CONST_B"
end

puts B.new.name #=> CONST_A

BクラスにAのメソッド def name ~ end を追加すると、CONST_B が表示されます。 また、NAME = "CONST_B" を消すと、継承チェーンをたどって、CONST_A が表示されるようになります。

ネストしている場合

もちろんですがネストしている場合、階層が異なるものはそれぞれ別の値が保持されているので、そのスコープに合わせて参照されます。

module M
  class A
    CONST = "M::A"

    def say
      CONST
    end
  end
end

module M
  module B
    class A
      CONST = "M::B::A"

      def say
        CONST
      end
    end
  end
end

ma = M::A.new
puts ma.say # => M::A
mba = M::B::A.new
puts mba.say # => M::B::A

では、この場合はどうなるかというと

module M
  CONST = "Hello"
end

module M
  class C
    def say
      CONST
    end
  end
end

puts M::C.new.say #=> Hello

moduleを再オープンしている場合でも、値は保持されているので Hello が表示されます。

以下のように M::C と記述するとクラスMの探索は行われないようです。

module M
  CONST = "Hello"
end

class M::C
  def say
    CONST
  end
end

puts M::C.new.say #=> uninitialized constant M::C::CONST (NameError)

Cクラスにいる定数は参照可

module M
  class C
    CONST = "Hello"
  end
end

class M::C
  def say
    CONST
  end
end

puts M::C.new.say #=> Hello

includeなどが入ったとき

続いて、includeやprependなどが入ったとき。 こちらも同じく、継承チェーンのどこにクラスが入ってくるかわかれば問題なさそうです。

class B
  CONST = "Hello B"
end

module C
  CONST = "Hello C"
end

module D
  CONST = "Hello D"
end

class A < B
  include C
  include D

  def say
    CONST
  end
end

a = A.new
p a.say # => Hello D

呼び出したところから、一番近い Hello D が参照されます。

A.ancestors
=> [A, D, C, B, Object, Kernel, BasicObject]

まとめ

定数の参照については、

  • 呼び出している箇所はどこか?
  • 定数はどのクラス・モジュールにいるのか?
  • レキシカルスコープはどうなっているか?
  • 継承チェーンはどうなっているか?

を、モジュールのネストも意識しつつ確認していけばよさそうです。

その他

こちらは何が出力されるか?というと、

class A
  CONST = "Hello A"

  class << self
    def name
      const_get(:CONST)
    end
  end
end

class B < A
  CONST = "Hello B"
end

puts B.name #=> Hello B

const_get は、selfに定義された定数を探索するので、Bクラスの持っているCONST Hello B が表示されます。

参考

Rubyの認定試験があったので受けてみた

最近、Rubyを原点に戻って学び直しているのですが、 その学習の一環として、Ruby技術者認定試験を受験することにしました。

試験は、javaoracleと同じような感じでレベル分けがされており、RubyではSilverとGoldの2段階がありました。

詳しくはこちら http://www.ruby.or.jp/ja/certification/examination/

Goldに認定されるには、Sliver,Gold試験両方に受からなければならないということもあり、最初に今回はSilverを受けてきました。

結果は合格で 88/100(75点合格)でした。

90点台はいけるかなと思っていたのですが、どこかのひっかけ問題にまんまと引っかかってしまっていたようです。

Silverはそんなに難易度は高くはありませんでしたが、落ちると受験料16,200円(税込)が吹っ飛ぶので、若干の良いプレッシャーを感じながら学習できました。

試験対策をして効果はあったのか?と聞かれればあった気がします。

私は試験対策がただの覚えゲーにならないよう、最初は試験範囲はあえて意識せず、以下の書籍を復習するようにしました。そのきっかけになったこと。

組み込みクラスのメソッドが試験対策前よりパッとでてくるようになったこと。割と前から知らなかったメソッドも多くありました。

あまり触る機会の無かった、RubyでIOクラス周りの理解が深まったこと。 これに関しては、ファイルをまとめて処理するバッチを作る場面などで役立ちそうな気がしています。

などなど。。。

次はGoldへと行きたいところです。 (でもphprailsもやり直したいな)