Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

SQLQL とは!?

3,189 views

Published on

銀座 Rails #8 の LT で発表した際のスライドです

Published in: Technology
  • Be the first to comment

SQLQL とは!?

  1. 1. SQLQL とは!?SQLQL とは!? 2019/04/24 銀座Rails#8 @yancya Powered by Rabbit 2.2.2 and COZMIXNG
  2. 2. @yancya の自己紹介 Rubyist, SQList 情報処理安全確保支援士
  3. 3. 基本的なこと Ruby on Rails でアプリケーショ ンを開発していて脆弱性になる パターン Rails に対応していない設計 Rails のルールに従っていない設計
  4. 4. SQLQL とは https://somehost/sqlql みたいな URL に SQL を送信すると、SQL 実行結果の JSON が返ってくると いう概念
  5. 5. SQL の実行結果が JSON と は SELECT id, name FROM user ってやったら [{"id":10,"name":"yancya"}, {"id":11,"name":"testuser"}] みたいなのが返ってくる
  6. 6. 送信すると、とは 文字通り、リクエストにそのまま SQL を含める curl -X GET -H 'Authorization: Token 6f916ae6-8472-463a-9808-6af19e459541' -F "haute_couture[query]=select id, name from users" https://sqlql-sample-yancya.herokuapp.com/haute_couture
  7. 7. 認証している? している。そして、認証されたユー ザーに参照が許可されているレコー ドのみが返ってくる ↓ は secretman というユーザーの トークンを使ったリクエスト curl -X GET -H 'Authorization: Token 4f3326a4-b900-4624-af58-87e8f363dee6' -F "haute_couture[query]=select id, name from users" https://sqlql-sample-yancya.herokuapp.com/haute_couture
  8. 8. 許可されているレコードのみ が リクエストしたユーザーの認可に応 じたレスポンスとなる secretman のリクエストには secretman のレコードが含まれる [{"id":10,"name":"yancya"}, {"id":11,"name":"testuser"}, {"id":14,"name":"secretman"}]
  9. 9. ネストした行は返せなく無 い? SQL に JSON を組む関数がある SELECT u.id, u.name, json_agg(c.content) as comments FROM users AS u LEFT OUTER JOIN comments AS c ON u.id = user_id GROUP BY 1, 2
  10. 10. Array 入りの JSON の例 curl -X GET -H 'Authorization: Token 4f3326a4-b900-4624-af58-87e8f363dee6' -F "haute_couture[query]= SELECT u.id, u.name, json_agg(c.content) as comments FROM users AS u LEFT OUTER JOIN comments AS c ON u.id = user_id GROUP BY 1, 2" https://sqlql-sample-yancya.herokuapp.com/haute_couture
  11. 11. Array 入りの JSON の例 [{"id":11,"name":"testuser","comments":["it is not secret"]}, {"id":14,"name":"secretman","comments":[null]}, {"id":10,"name":"yancya","comments":["hoge", "fuga", "piyo"]}]
  12. 12. どうやって実現しているのか リクエストのクエリを CTE(WITH) の中に埋め込んでいる module HauteCouture def self.find_by_sql(query:, user:) ActiveRecord::Base.connection.execute(<<~SQL).first['result'] || '[]' WITH users AS (#{user.for_haute_couture_sql}) , comments AS (#{Comment.for_haute_couture(user).to_sql}) , t AS (#{query}) SELECT JSON_AGG(t) AS result FROM t SQL end end
  13. 13. どうやって実現しているのか # User#for_haute_couture_sql def for_haute_couture_sql base = User.select(:id, :name, :created_at, :updated_at) base.where(id: id).or(base.where(privacy: false)).to_sql end
  14. 14. 最終的に組まれる SQL WITH users AS ( SELECT "users"."id", "users"."name", "users"."created_at", "users"."updated_at" FROM "users" WHERE ("users"."id" = 10 OR "users"."privacy" = 'f')) , t AS (SELECT id, name FROM users) -- <- ここに入ってるのかリクエストされた SQL SELECT JSON_AGG(t) AS result FROM t
  15. 15. SQL インジェクションで死 ぬのでは SELECT 1), killer AS (DELETE FROM likes CASCADE RETURNING * みたいな、悪意ある問い合わせをさ れたらどうするのか
  16. 16. SQL インジェクションで死 ぬのでは 確かに、likes が全消しされる SQL になってしまう WITH users AS ( SELECT "users"."id", "users"."name", "users"."created_at", "users"."updated_at" FROM "users" WHERE ("users"."id" = 10 OR "users"."privacy" = 'f')) , t AS (SELECT 1), killer AS (DELETE FROM likes CASCADE RETURNING *) SELECT JSON_AGG(t) AS result FROM t
  17. 17. SQL インジェクションで死 ぬのでは 流石に、一回 SQL パーサーに喰わ せる必要がある PgQuery.parse("SELECT 1), killer AS (DELETE FROM likes CASCADE RETURNING *") #=> PgQuery::ParseError: syntax error at or near ")" (scan.l:1121)
  18. 18. SQL インジェクションで死 ぬのでは でもまぁ、いくら SQL として valid でも、普通に DELETE FROM likes とか送られてくるかもしれな い 構文木を見て、副作用のある文が含 まれていないかどうかをチェックし て検知する必要がある sql_ast = PgQuery.parse('DELETE FROM users') mutate_stmts = %w[TruncateStmt DeleteStmt UpdateStmt InsertStmt] sql_ast.tree.first.dig("RawStmt","stmt").keys & mutate_stmts #=> ["DeleteStmt"]
  19. 19. pg_query pg_query 便利 p PgQuery.parse("select id, name from users") #=> #<PgQuery:0x00007f98a61374a0 # @aliases=nil, # @cte_names=nil, # @query="select id, name from users", # @tables=nil, # @tree= # [{"RawStmt"=> # {"stmt"=> # {"SelectStmt"=> # {"targetList"=> # [{"ResTarget"=> # {"val"=>{"ColumnRef"=>{"fields"=>[{"String"=>{"str"=>"id"}}], "location"=>7}}, "location"=>7}}, # {"ResTarget"=> # {"val"=>{"ColumnRef"=>{"fields"=>[{"String"=>{"str"=>"name"}}], "location"=>11}}, "location"=>11}}], # "fromClause"=>[{"RangeVar"=>{"relname"=>"users", "inh"=>true, "relpersistence"=>"p", "location"=>21}}], # "op"=>0}}}}], # @warnings=[]>
  20. 20. 複数 DB Rails 6.0 から、replica 属性のサ ブのコネクションの設定が簡単 に書けるようになった development: primary: <<: *default database: sqlql_development readonly: <<: *default database: sqlql_development replica: true
  21. 21. replica 属性のコネクション SQLQL の処理をするときだけ replica 属性のコネクションを使 えば、Mutations っぽい SQL は Rails が弾いてくれて便利っぽい ActiveRecord::Base.connected_to(database: :readonly) do User.first.update(name: 'hoge') end #=> ActiveRecord::ReadOnlyError #=> (Write query attempted while in readonly mode...
  22. 22. WITH は危ないらしい CTE の WITH 句は Rails 的には ホワイトリストに入ってないっ ぽい…… なぜ WITH がホワイトでないかにつ いては長くなるので割愛します(気 になる人は訊いて下さい ActiveRecord::Base.connected_to(database: :readonly) do ActiveRecord::Base.connection.execute( "WITH t AS (SELECT 1 AS n) SELECT * FROM t" ) end #=> ActiveRecord::ReadOnlyError #=> (Write query attempted while in readonly mode...
  23. 23. DB ユーザーの権限 せっかく複数 DB 機能があるん だから、本当に READONLY な ユーザーを作って使えばよい create user readonlyuser with password 'readonlyuser' NOCREATEDB NOCREATEROLE; GRANT SELECT ON ALL TABLES IN SCHEMA public TO "readonlyuser";
  24. 24. DB ユーザーの権限 Rails 6 便利 development: primary: <<: *default database: sqlql_development readonly: <<: *default database: sqlql_development replica: true username: readonlyuser password: readonlyuser
  25. 25. SQLQL の脆弱性 Generated Record Bomb SELECT generate_series(1, 100000000) AS death Recurring Nightmare WITH RECURSIVE r AS ( SELECT 1 AS n UNION ALL SELECT n + 1 AS n FROM r) SELECT * FROM r
  26. 26. SQLQL の脆弱性 Public Schema SELECT * FROM public.users Information Table SELECT * FROM pg_user
  27. 27. SQLQL の脆弱性 まだまだ沢山脆弱性があるはずな ので、何か気づいたら教えてくだ さい 試せるサンプルアプリケーション があります Powered by Rabbit 2.2.2 and COZMIXNG

×