Content Security Policy
This commit is contained in:
parent
c8bdec242e
commit
5b20919fb3
12 changed files with 133 additions and 29 deletions
|
@ -1,7 +1,8 @@
|
||||||
RAILS_ENV=production
|
RAILS_ENV=
|
||||||
IMAP_SERVER=
|
IMAP_SERVER=
|
||||||
DEFAULT_FROM=
|
DEFAULT_FROM=
|
||||||
SKEL_SUTTY=https://0xacab.org/sutty/skel.sutty.nl
|
SKEL_SUTTY=https://0xacab.org/sutty/skel.sutty.nl
|
||||||
SUTTY=sutty.nl
|
SUTTY=sutty.local
|
||||||
|
SUTTY_WITH_PORT=sutty.local:3000
|
||||||
REDIS_SERVER=
|
REDIS_SERVER=
|
||||||
REDIS_CLIENT=
|
REDIS_CLIENT=
|
||||||
|
|
|
@ -4,9 +4,6 @@ module Api
|
||||||
module V1
|
module V1
|
||||||
# API
|
# API
|
||||||
class BaseController < ActionController::Base
|
class BaseController < ActionController::Base
|
||||||
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
|
|
||||||
password: ENV['HTTP_BASIC_PASSWORD']
|
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
protect_from_forgery with: :null_session
|
||||||
respond_to :json
|
respond_to :json
|
||||||
end
|
end
|
||||||
|
|
42
app/controllers/api/v1/csp_reports_controller.rb
Normal file
42
app/controllers/api/v1/csp_reports_controller.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Api
|
||||||
|
module V1
|
||||||
|
# Recibe los reportes de Content Security Policy
|
||||||
|
class CspReportsController < BaseController
|
||||||
|
# Crea un reporte de CSP intercambiando los guiones medios por
|
||||||
|
# bajos
|
||||||
|
#
|
||||||
|
# TODO: Aplicar rate_limit
|
||||||
|
def create
|
||||||
|
csp = CspReport.new(csp_report_params.to_h.map do |k, v|
|
||||||
|
{ k.tr('-', '_') => v }
|
||||||
|
end.inject(&:merge))
|
||||||
|
|
||||||
|
csp.id = SecureRandom.uuid
|
||||||
|
csp.save
|
||||||
|
|
||||||
|
render json: {}, status: :created
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only#Violation_report_syntax
|
||||||
|
def csp_report_params
|
||||||
|
params.require(:'csp-report')
|
||||||
|
.permit(:disposition,
|
||||||
|
:referrer,
|
||||||
|
:'blocked-uri',
|
||||||
|
:'document-uri',
|
||||||
|
:'effective-directive',
|
||||||
|
:'original-policy',
|
||||||
|
:'script-sample',
|
||||||
|
:'status-code',
|
||||||
|
:'violated-directive',
|
||||||
|
:'line-number',
|
||||||
|
:'column-number',
|
||||||
|
:'source-file')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,9 @@ module Api
|
||||||
module V1
|
module V1
|
||||||
# API para sitios
|
# API para sitios
|
||||||
class SitesController < BaseController
|
class SitesController < BaseController
|
||||||
|
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
|
||||||
|
password: ENV['HTTP_BASIC_PASSWORD']
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: Site.all.order(:name).pluck(:name)
|
render json: Site.all.order(:name).pluck(:name)
|
||||||
end
|
end
|
||||||
|
|
4
app/models/csp_report.rb
Normal file
4
app/models/csp_report.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Almacena un reporte de CSP
|
||||||
|
class CspReport < ApplicationRecord; end
|
|
@ -6,31 +6,34 @@
|
||||||
# For further information see the following documentation
|
# For further information see the following documentation
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||||
|
|
||||||
# Rails.application.config.content_security_policy do |policy|
|
Rails.application.config.content_security_policy do |policy|
|
||||||
# policy.default_src :self, :https
|
policy.default_src :self
|
||||||
# policy.font_src :self, :https, :data
|
# XXX: Varios scripts generan estilos en línea
|
||||||
# policy.img_src :self, :https, :data
|
policy.style_src :self, :unsafe_inline
|
||||||
# policy.object_src :none
|
# Repetimos la default para poder saber cuál es la política en falta
|
||||||
# policy.script_src :self, :https
|
policy.script_src :self
|
||||||
# policy.style_src :self, :https
|
policy.font_src :self
|
||||||
# # If you are using webpack-dev-server then specify
|
# TODO: Permitimos cargar imágenes remotas?
|
||||||
# # webpack-dev-server host
|
policy.img_src :self
|
||||||
# policy.connect_src :self, :https, "http://localhost:3035",
|
# Ya no usamos applets!
|
||||||
# "ws://localhost:3035" if Rails.env.development?
|
policy.object_src :none
|
||||||
|
if Rails.env.development?
|
||||||
|
policy.connect_src :self,
|
||||||
|
'http://localhost:3035',
|
||||||
|
'ws://localhost:3035'
|
||||||
|
end
|
||||||
|
|
||||||
# # Specify URI for violation reports
|
# Specify URI for violation reports
|
||||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
policy.report_uri "https://api.#{ENV.fetch('SUTTY_WITH_PORT', 'sutty.nl')}/v1/csp_reports.json"
|
||||||
# end
|
end
|
||||||
|
|
||||||
# If you are using UJS then enable automatic nonce generation
|
# If you are using UJS then enable automatic nonce generation
|
||||||
# Rails.application.config.content_security_policy_nonce_generator =
|
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||||
# -> request { SecureRandom.base64(16) }
|
|
||||||
|
|
||||||
# Set the nonce only to specific directives
|
# Set the nonce only to specific directives
|
||||||
# Rails.application.config.content_security_policy_nonce_directives =
|
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||||
# %w(script-src)
|
|
||||||
|
|
||||||
# Report CSP violations to a specified URI
|
# Report CSP violations to a specified URI
|
||||||
# For further information see the following documentation:
|
# For further information see the following documentation:
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||||
# Rails.application.config.content_security_policy_report_only = true
|
Rails.application.config.content_security_policy_report_only = false
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
Mime::Type.register 'application/csp-report', :json
|
||||||
|
|
||||||
# Add new mime types for use in respond_to blocks:
|
|
||||||
# Mime::Type.register "text/richtext", :rtf
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ Rails.application.routes.draw do
|
||||||
constraints subdomain: 'api' do
|
constraints subdomain: 'api' do
|
||||||
scope module: 'api' do
|
scope module: 'api' do
|
||||||
namespace :v1 do
|
namespace :v1 do
|
||||||
|
resources :csp_reports, only: %i[create]
|
||||||
get 'sites/allowed', to: 'sites#allowed'
|
get 'sites/allowed', to: 'sites#allowed'
|
||||||
resources :sites, only: %i[index]
|
resources :sites, only: %i[index]
|
||||||
end
|
end
|
||||||
|
|
17
db/migrate/20200205173039_create_csp_reports.rb
Normal file
17
db/migrate/20200205173039_create_csp_reports.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
class CreateCspReports < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
create_table :csp_reports, id: :uuid do |t|
|
||||||
|
t.timestamps
|
||||||
|
|
||||||
|
t.string :disposition
|
||||||
|
t.string :referrer
|
||||||
|
t.string :blocked_uri
|
||||||
|
t.string :document_uri
|
||||||
|
t.string :effective_directive
|
||||||
|
t.string :original_policy
|
||||||
|
t.string :script_sample
|
||||||
|
t.string :status_code
|
||||||
|
t.string :violated_directive
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
db/migrate/20200206151057_add_source_to_csp_reports.rb
Normal file
7
db/migrate/20200206151057_add_source_to_csp_reports.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class AddSourceToCspReports < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :csp_reports, :column_number, :integer
|
||||||
|
add_column :csp_reports, :line_number, :integer
|
||||||
|
add_column :csp_reports, :source_file, :string
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,10 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
ActiveRecord::Schema.define(version: 20_200_206_151_057) do
|
||||||
|
# Could not dump table "access_logs" because of following StandardError
|
||||||
|
# Unknown type '' for column 'id'
|
||||||
|
|
||||||
create_table 'action_text_rich_texts', force: :cascade do |t|
|
create_table 'action_text_rich_texts', force: :cascade do |t|
|
||||||
t.string 'name', null: false
|
t.string 'name', null: false
|
||||||
t.text 'body'
|
t.text 'body'
|
||||||
|
@ -55,6 +58,9 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
||||||
t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id'
|
t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Could not dump table "csp_reports" because of following StandardError
|
||||||
|
# Unknown type 'uuid' for column 'id'
|
||||||
|
|
||||||
create_table 'deploys', force: :cascade do |t|
|
create_table 'deploys', force: :cascade do |t|
|
||||||
t.datetime 'created_at', null: false
|
t.datetime 'created_at', null: false
|
||||||
t.datetime 'updated_at', null: false
|
t.datetime 'updated_at', null: false
|
||||||
|
@ -132,6 +138,7 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
||||||
t.string 'status', default: 'waiting'
|
t.string 'status', default: 'waiting'
|
||||||
t.text 'description'
|
t.text 'description'
|
||||||
t.string 'title'
|
t.string 'title'
|
||||||
|
t.boolean 'colaboracion_anonima', default: false
|
||||||
t.index ['design_id'], name: 'index_sites_on_design_id'
|
t.index ['design_id'], name: 'index_sites_on_design_id'
|
||||||
t.index ['licencia_id'], name: 'index_sites_on_licencia_id'
|
t.index ['licencia_id'], name: 'index_sites_on_licencia_id'
|
||||||
t.index ['name'], name: 'index_sites_on_name', unique: true
|
t.index ['name'], name: 'index_sites_on_name', unique: true
|
||||||
|
|
25
test/controllers/api/v1/csp_reports_controller_test.rb
Normal file
25
test/controllers/api/v1/csp_reports_controller_test.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
module Api
|
||||||
|
module V1
|
||||||
|
class CSPReportsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
test 'se puede enviar un reporte' do
|
||||||
|
post v1_csp_reports_url,
|
||||||
|
params: {
|
||||||
|
'csp-report': {
|
||||||
|
'document-uri': 'http://example.com/signup.html',
|
||||||
|
'referrer': '',
|
||||||
|
'blocked-uri': 'http://example.com/css/style.css',
|
||||||
|
'violated-directive': 'style-src cdn.example.com',
|
||||||
|
'original-policy': "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal 201, response.status
|
||||||
|
assert_equal 1, CspReport.all.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue