Force headers on all Rails 404/500 responses - ruby-on-rails

I am trying to set the 'X-Frame-Options' header on all responses returned by my Rails application. It seems like this header is not set on 404 or 500 type responses. How can I configure Rails to always include this header?
It appears I somehow need to hook into Rails to ensure these headers are always set.

I am having success using the below middleware as my 'exceptions_app'.
class XSecurityHandler
def initialize(app)
#app = app
end
def call(env)
_status, headers, response = #app.call(env)
headers['X-Frame-Options'] = "SAMEORIGIN"
headers['X-Content-Type-Options'] = "nosniff"
[status(env), headers, response]
end
private
def status(env)
path = env["ORIGINAL_FULLPATH"]
if path == "/404"
404
elsif path == "/422"
422
else
500
end
end
end

Set default headers in config/application.rb:
config.action_dispatch.default_headers['X-Frame-Options'] = 'SAMEORIGIN'
but it wont work, if you have configured reverse proxy (like nginx) to serve static assets which does not exists (404), but i think you know about this :)

Related

Rack custom middleware giving error "ActionDispatch::Http::Parameters::ParseError"

I am writing a custom middleware for my rails app, to decrypt/encrypt parameters in a post request. I have configured the middleware in 'config/environments/staging.rb'
config.middleware.use CustomMiddleware
In order to read the request params i am reading the env obj like this
rack_input = env['rack.input'].read
I am able to access the parameter and manipulate as needed. But when i pass over on the env obj to the app object which is the receiver. I am getting
"ActionDispatch::Http::Parameters::ParseError"
Full error
Error occurred while parsing request parameters.
Contents:
F, [2017-11-15T02:13:50.230088 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc]
F, [2017-11-15T02:13:50.230340 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc] ActionDispatch::Http::Parameters::ParseError (no implicit conversion of nil into String):
F, [2017-11-15T02:13:50.230563 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc]
F, [2017-11-15T02:13:50.230782 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc] app/middleware/custom_middleware.rb:34:in `call'
My code is somewhat like this
class CustomMiddleware
include Encryption
def initialize(app)
#app = app
end
def set_payload_params(env)
rack_input = env['rack.input'].read
#args = JSON.parse(rack_input) rescue {}
end
def call(env)
request = Rack::Request.new(env)
set_payload_params(env)
payload = #args['payload']
decrypt_params
status, headers, response = #app.call(env)
encrypt_params
end
If i don't read the env object and hard code the parameters, there is no such parsing error. And my controller is responding with data w.r.t to hardcode parameters.
My Rails version is 5.1.4
My ruby version is 2.4.2
I have done a similar middleware in another rails app(4.2.8) which is working fine.
Any insight on this will be of much help. Thanks in advance.
This error occurs cause after params are empty after read in middleware (this is stream).
rack_input = env['rack.input'].read
After execute this code rack params are blank. Now you need to make
env['rack.input'].rewind
or
request.body.rewind
Now your request has params again and Rails can simply continue job.
Added a hack to set content-type as "" in custom middleware, which avoided parsing on the input before forwarding the input to next component in the stack.
class CustomMiddleware
include Encryption
def initialize(app)
#app = app end
def set_payload_params(env)
rack_input = env['rack.input'].read
#args = JSON.parse(rack_input) rescue {} end
def call(env)
env['CONTENT_TYPE'] = ''
request = Rack::Request.new(env)
set_payload_params(env)
payload = #args['payload']
decrypt_params
status, headers, response = #app.call(env)
encrypt_params
end

WEBrick alter HTTP response headers on a per-file extension basis

Is it possible to modify the WEBrick HTTP response headers globally for a specific file extension, for example to serve all files with svgz extension to include the HTTP Header "Content-Encoding: gzip" in the HTTP response? I can't seem to figure out how to do this.
lib/dps/compression.rb
module Dps
class Compression
def initialize(app)
#app = app
end
def call(env)
status, headers, response = #app.call(env)
if File.extname(env['REQUEST_URI']) == ".svgz" && status == 200
headers["Content-Encoding"] = "gzip"
else
nil
end
[status, headers, response]
end
end
end
config/application.rb
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.middleware.insert_before("ActionDispatch::Static", "Dps::Compression")

How to intercept and rewrite damaged JSON calls with Middleware

I'm trying to find the best place to do this. My middleware looks like this :
class Fixer
def initialize app
#app = app
end
def call env
if env["HTTP_ORIGIN"] == "https://where_i_expect_it_to_come_from.com/"
env['rack.input'] = StringIO.new('{"yo":"momma"}') # <-- But this info is not actually written before the call is passed!
end
env['rack.input'].rewind
status, headers, response = #app.call env
return [status, headers, response]
end
end
Rails.application.config.middleware.insert_before ActionDispatch::ParamsParser, Fixer
It seems that even when I rewrite the call here, the info is not actually rewritten properly. Any ideas how I can have my content written before its bubbled up?
i think this problem is the same as here: https://stackoverflow.com/questions/22712663/getting-a-routing-error-when-my-routes-are-clearly-marked and here I have a third party service that I believe is sending me malformed JSON
from what i understand you want to fix some incoming request-data.
as a simplified example:
curl -X PUT -d "user[name]=something" http://localhost:3000/users/123
i change something to phoet in the fixer
class Fixer
def initialize(app)
#app = app
end
def call(env)
env['rack.input'] = StringIO.new("user[name]=phoet")
#app.call(env)
end
end
and add it to application.rb
config.middleware.insert_before "ActionDispatch::ParamsParser", "Fixer"
when i request my app now, i see the logs
[localhost] [fdff4eb0-8387-41] Started PUT "/users/123" for 127.0.0.1 at 2014-03-30 13:16:02 -0400
[localhost] [fdff4eb0-8387-41] Processing by UsersController#update as */*
[localhost] [fdff4eb0-8387-41] Parameters: {"user"=>{"name"=>"phoet"}, "id"=>"123"}

How can I return a 404 for a specific URL with Rack middleware

A site I manage is getting constant requests for a javascript file that no longer exists, from an older version of the site. These requests take up a lot of resources because they get routed through Rails every time to return a 404. I am thinking it would be much better to have Rack handle that specific URL and return 404 itself. Is that correct? If so, how would I set that up?
I have been checking out this blog post which I think is kinda the way to move forward (ie, inheritance from some existing Rack module):
http://icelab.com.au/articles/wrapping-rack-middleware-to-exclude-certain-urls-for-rails-streaming-responses/
So I ended up writing my own little bit of middleware:
module Rack
class NotFoundUrls
def initialize(app, exclude)
#app = app
#exclude = exclude
end
def call(env)
status, headers, response = #app.call(env)
req = Rack::Request.new(env)
return [status, headers, response] if !#exclude.include?(URI.unescape(req.fullpath))
content = 'Not Found'
[404, {'Content-Type' => 'text/html', 'Content-Length' => content.size.to_s}, [content]]
end
end
end
and then adding this to the config.ru file:
use Rack::NotFoundUrls, ['/javascripts/some.old.file.js']
It's the first time I've done this so let me know if there's any glaring mistakes...
The rack-contrib gem includes a Rack::NotFound middleware component (among many other useful elements) which should do the job:
https://github.com/rack/rack-contrib/

Mongrel::DirHandler equivalent for Passenger

I'm using Mongrel::DirHandler to control response headers for static files - this works great on my dev machine. My production machine uses Passenger so my headers aren't getting set. How do I control headers for static files when using Passenger?
snippet from my environment.rb:
if defined? Mongrel::DirHandler
module Mongrel
class DirHandler
def send_file_with_expires(req_path, request, response, header_only=false)
if req_path =~ /((\/images)|javascripts|stylesheets)/
response.header['Cache-Control'] = 'max-age=315360000'
response.header['Expires'] = (Time.now + 10.years).rfc2822
else
response.header["Last-Modified"] = Time.now.httpdate
response.header["Expires"] = 0
# HTTP 1.0
response.header["Pragma"] = 'no-cache'
# HTTP 1.1 ‘pre-check=0, post-check=0′ (IE specific)
response.header["Cache-Control"] = 'no-store, no-cache, must-revalidate, max-age=0, pre-check=0, post-check=0'
end
send_file_without_expires(req_path, request, response, header_only)
end
alias_method :send_file_without_expires, :send_file
alias_method :send_file, :send_file_with_expires
end
end
end
Since you're using Passenger, I assume you're under apache, so your request isn't going through Mongrel anymore. If so, you can establish rules on the .htaccess file inside the public directory of your application.
Here's an explination on how to do it.

Resources