今某サイトの下記の脆弱性が話題です
最近弊社ではRuby on Railsなので自社での状況を確認しました
1. 認証CookieにSecure属性をつけていない
どのような問題が?
HTTP通信でCookieが送られてしまうため、暗号化されていないため、信用できないWiFiを利用した場合などに、登録している情報ややり取りの履歴が盗まれる可能性がある。
参考情報
Secure 属性がついた Cookie は HTTPS プロトコル上の暗号化されたリクエストでのみサーバーに送信され、安全でない HTTP では決して送信されないため、中間者攻撃者が簡単にアクセスすることはできません。(URL に http: を含む) 安全でないサイトは、 Secure 属性を使用して Cookie を設定することができません。
https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies
弊社での対策
1. Secure属性を付与する
config.session_store(secure: true)
<追記>
ところが Secure 属性が付加されません
def []=(name, options)
# name: _server_rails_session
# options: {:value=>"...=", :same_site=>nil, :path=>"/", :domain=>nil, :expire_after=>nil,
# :secure=>false, :httponly=>true,
# :defer=>false, :renew=>false, :id=>"..."}
def set_cookie(request, session_id, cookie)
cookie_jar(request)[@key] = cookie
end
def context(env, app = @app)
req = make_request env
prepare_session(req)
status, headers, body = app.call(req.env)
res = Rack::Response::Raw.new status, headers
commit_session(req, res)
[status, headers, body]
end
:
def commit_session(req, res)
session = req.get_header RACK_SESSION
options = session.options
# options: {
# :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :defer=>false, :renew=>false, :id=>"..."
# }
:
set_cookie(req, res, cookie.merge!(options))
module SessionObject # :nodoc:
def commit_session(req, res)
req.commit_csrf_token
super(req, res)
end
:
def prepare_session(req)
# @default_options: {
# :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :defer=>false, :renew=>false, :id=>"..."
# }
Request::Session.create(self, req, @default_options)
end
結局 @default_options
がsecure: false
ままでした。
以下を書き換えると修正できました
- config.session_store secure: true
+ config.session_store :cookie_store, secure: true
2. CloudFrontのビヘイビアでHTTPS通信だけに限定する
2. 他人の情報を書き換え可能
ユーザAでログインしてユーザAの認証Cookieを使い、ユーザBの情報を入手したり書き換えることができる。
なぜ起きるのか?以下のように書き換えるユーザのIDをリクエストパラメータで参照している。
user = User.find(params[:user_id])
user.update params[:user_name]
本来であれば以下のようにユーザIDはセッションから取得すること。またデータベースに渡すパラメーターは制限する必要がある。
user = User.find(current_user.id)
user.update params.require[:user].permit[:name]
またユーザが投稿した記事のような場合は単純にfind
せずに事前にwhere
句で対象となる記事を絞り込んでください
article = Article.where(author: current_user).find(params[:id])
さらに
RUNTEQ校長「ひさじゅ」さんより教えていただきました
BrakemanというGemを使うとよくある脆弱性を検知してくれるとのことです