When we do a security audit of a client's Ruby on Rails application there are a couple areas where we commonly see too much information being revealed. The first area is the web server response headers. By default Nginx and Apache will return a server token in the response headers that shows server information including version. This information can be used by bad actors to look for known exploits for that server version. Both web servers can be configured to disable that header, but we're going to specifically look at Nginx.
To check if your site is sharing this information the following curl command can be used:
curl -I yourdomain.com
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0 (Ubuntu)
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
For Nginx, to hide the version, the http block in the configuration file could be updated with the setting:
server_tokens off;
To completely remove the server header, the Headers-More module can be used. Once that module is installed and enabled the more_clear_headers directive can be used:
more_clear_headers Server;
This setting will almost entirely hide the web server information, but there are still potentially ways to discover which web server is being used when dealing with error responses. For instance the invalid request below:
curl -X POST https://yourdomain.com/robots.txt
<html>
<head><title>405 Not Allowed</title></head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx</center>
</body>
</html>
As you can see the built-in Nginx error response includes the server in the response HTML. To avoid this, the easiest option is to configure custom error pages. We typically just re-use the error pages for the Rails app. To do this the server block in the nginx.conf can be updated like below:
error_page 404 405 /404.html;
location = /404.html {
root /path/to/app/public;
internal;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /path/to/app/public;
internal;
}
Besides the web server, another area where sensitive information can inadvertantly be revealed is when doing authorization. If routes and resources that require authorization return a 302 or 403 status, it reveals routes and IDs that can potentially be compromised. It would be better when an unauthorized request is made to a restricted resource that a 404 status is always returned. This issue often comes up because of apps that use CanCanCan have implemented the documented controller code to rescue an access denied exception:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { head :forbidden }
format.html { redirect_to root_path, alert: exception.message }
end
end
end
However, the CanCanCan documentation has added a section for handling access denied in a more secure way:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { render nothing: true, status: :not_found }
format.html { redirect_to main_app.root_url, notice: exception.message, status: :not_found }
format.js { render nothing: true, status: :not_found }
end
end
end
Of course, this isn't just an issue for CanCanCan, any application doing authorization could potentially have these issues, so it is worthwhile to periodically do a security review. At Neomind we make it our business to care for our clients apps and proactively monitor them for any needed updates.
Interested in getting more tips on securing your Rails app? Learn more about our code audit service here. The team at Neomind is always happy to chat!