Rails 6.0.3
ruby 2.6.5p114
ActionCableでチャットアプリを作っています。
とある脆弱性が見つかってしまいました..
↓ ActionCableでテストさんがメッセージを発信して、ActionJobで読み込んだ時。
コード一覧
room_channel.rb
rb
1class RoomChannel < ApplicationCable::Channel 2 3 def subscribed 4 5 stream_from "room_channel_#{params['room'].to_s}" 6 end 7 8 def unsubscribed 9 # Any cleanup needed when channel is unsubscribed 10 end 11 12 def speak(data) 13 ActionCable.server.broadcast 'room_channel', room_id: params['room'].to_s 14 # NGワード集 15 ng_word_params = [...省略] 16 ip = self.connection.ip_addr 17 # puts "==========================" + current_user.name 18 # user_signed_in? = self.connection.signed_in 19 now = Time.now 20 secondsAgo = now - 10 21 if current_user.present? 22 if Usermanager.where(user_id: current_user.id, room_ban: true, room_id: params['room'], login: true).exists? 23 return false 24 end 25 else 26 if Usermanager.where(ip_id: ip, room_ban: true, room_id: params['room'], login: false).exists? 27 return false 28 end 29 end 30 ng_word_params.each do |ng| 31 unless current_user.present? 32 if Usermanager.where(ip_id: ip,user_id: nil, room_ban: false, room_id: params['room'].to_s, login: false, ng_word: true).exists? 33 unless data['message'].nil? 34 if data['message'].include?(ng) 35 return false 36 end 37 end 38 end 39 else 40 if Usermanager.where(user_id: current_user.id, room_ban: false, room_id: params['room'].to_s, login: true, ng_word: true).exists? 41 unless data['message'].nil? 42 if data['message'].include?(ng) 43 return false 44 end 45 end 46 end 47 end 48 end 49 50 51 52 if data['message'].include?("https://www.youtube.com/watch?v=") 53 urll = data['message'].gsub(/http.+v=/, "") 54 url = urll.gsub(/&.+./, "") 55 elsif data['message'].include?("https://youtu.be/") 56 url = data['message'].gsub(/http.+be./, "") 57 end 58 messagesCount = Message.where(ip_id: ip).where('created_at > ?', secondsAgo).count 59 60 unless current_user.present? 61 p "===========ログインしてない=======" 62 if messagesCount <= 8 63 unless data['message'].nil? 64 if Usermanager.where(ip_id: ip, room_ban: false, message_limit: false, room_id: params['room'].to_s, login: false).exists? 65 if data['message'].length <= 1000 66 Message.create! content: data['message'], room_id: params['room'].to_s,username: "名無し",ip_id: ip, login: false, youtube_id: url, user_id: nil 67 end 68 end 69 end 70 end 71 else 72 p "===========ログインしてる=======" 73 74 if messagesCount <= 8 75 unless data['message'].nil? 76 if Usermanager.where(user_id: current_user.id, room_ban: false, room_id: params['room'].to_s, message_limit: false, login: true).exists? 77 if data['message'].length <= 1000 78 Message.create! content: data['message'], user_id: current_user.id, room_id: params['room'].to_s,username: current_user.name, ip_id: ip, login: true, youtube_id: url 79 end 80 end 81 end 82 end 83 end 84 end 85end
message_broadcast_job.rb
rb
1class MessageBroadcastJob < ApplicationJob 2 queue_as :default 3 4 def perform(message) 5 p "------ROOMCHANNELROADCAST-------------" 6 ActionCable.server.broadcast "room_channel_#{message.room_id}", message: render_message(message) 7 end 8 private 9 10 def render_message(message) 11 # ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message }) 12 ApplicationController.render_with_signed_in_user(message.user, partial: 'messages/message', locals: { message: message }) 13 14 end 15 16 17end
message.rb
rb
1class Message < ApplicationRecord 2 belongs_to :user, optional: true 3 belongs_to :room 4 belongs_to :usermanager, optional: true 5 after_update_commit { EditBroadcastJob.perform_later self } 6 after_create_commit { MessageBroadcastJob.perform_later self } 7 8 # after_update_commit { EditBroadcastJob.perform_later self } 9 # has_rich_text :content 10 validates :content, length: {maximum: 1000 } 11 mount_uploader :image, ImageUploader 12 13 validate :image_size 14 private 15 16 def image_size 17 if image.size > 5.megabytes 18 errors.add(:image, "容量が大きすぎます。5MB未満のファイルにしてください。") 19 end 20 end 21end 22
_message.html.erb
erb
1<div class="message-support" id="message-support-<%= message.id %>"> 2 <div class="message" id="message-<%= message.id %>"> 3 <div class="usernameAndTimeBox"> 4 <% unless message.user_id.nil? %> 5 <%= link_to message.username, user_path(message.user_id),class: "message_username" %> 6 <% else %> 7 <p class="message_username"><%= message.username %></p> 8 <% end %> 9 <div class="usettimeAndDeleteBox"> 10 <p class="agomessage"><%= time_ago_in_words(message.created_at) + "前" %></p> 11 <% if user_signed_in? %> 12 messageUserId: <%= message.user_id %> 13 CurrentUserId: <%= current_user.id %> 14 <% if message.user_id == current_user.id %> 15 <button id="<%= message.id %>" class="delete_btn"> 16 <%= image_tag "ゴミ箱のアイコン素材.png", class: "deleteBtnImage", id: "#{message.id}" %> 17 </button> 18 <button id="edit_click-<%= message.id %>" class="edit_click"> 19 <%= image_tag "鉛筆のアイコン素材.png"%> 20 </button> 21 <% end %> 22 <% end %> 23 </div> 24 </div> 25 <div id="message-<%= message.id %>-box"> 26 <%= raw Rinku.auto_link simple_format h(message.content), class: "message_content", id: "message-link-#{message.id}" %> 27 </div> 28 <% unless message.youtube_id.nil? %> 29 <% iframe = content_tag( 30 :iframe, 31 '', 32 width: 560, 33 height: 315, 34 src: "https://www.YouTube.com/embed/#{message.youtube_id}", 35 frameborder: 0, 36 allowfullscreen: true, 37 class: "youtube-container" 38 ) %> 39 <%= content_tag(:div, iframe, class: "youtube-container2") %> 40 <% end %> 41 <% if message.image? %> 42 <%= image_tag message.image.url %> 43 <% end %> 44 <!-- <div class="d" --> 45 <!-- <div class="editBox"> 46 <div class="formchat"> 47 <%#= form_with model: message, url: message_path(message) do |f| %> 48 <%#= f.rich_text_area :content %> 49 <%#= f.submit %> 50 <%# end %> 51 </div> 52 </div> --> 53 <% if user_signed_in? %> 54 <% if current_user.id == message.user_id %> 55 <div id="edit_form_box_<%= message.id %>" class="editform"> 56 <%= text_area_tag :content,nil,data: {behavior: "edit_speaker"},id: "message-#{message.id} form-message-#{message.id}", class: "edit_form" %> 57 <%= submit_tag "編集", class: "edit_submit" %> 58 </div> 59 <% end %> 60 <% end %> 61 </div> 62</div> 63 64<script> 65 // var message_value = document.getElementById("message-link-<%= message.id %>"); 66 // console.log(message_value) 67 // $('.edit_form').val(message_value.value) 68 var click = true 69 70 $("#edit_click-<%= message.id %>").click(function(){ 71 72 if(click == true){ 73 74 $('#edit_form_box_<%= message.id %>').fadeIn('slow'); 75 click = false 76 }else{ 77 console.log("hi") 78 $('#edit_form_box_<%= message.id %>').fadeOut('slow'); 79 click = true 80 } 81 }); 82</script>
application.controller.rb
ruby
1class ApplicationController < ActionController::Base 2 protect_from_forgery 3 # ApplicationController.render 'messages/message' 4 before_action :configure_permitted_parameters, if: :devise_controller? 5 before_action :search 6 def configure_permitted_parameters 7 devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) 8 end 9 10 def self.render_with_signed_in_user(user, *args) 11 ActionController::Renderer::RACK_KEY_TRANSLATION['warden'] ||= 'warden' 12 proxy = Warden::Proxy.new({}, Warden::Manager.new({})).tap{|i| i.set_user(user, scope: :user) } 13 renderer = self.renderer.new('warden' => proxy) 14 renderer.render(*args) 15 end 16 17 def search 18 @q = Room.ransack(params[:q]) 19 @rooms = @q.result(distinct: true).where(public: true) 20 end 21end 22
あなたの回答
tips
プレビュー