Using HTTPs with Ruby on Rails
Author: Simone Carletti , 原文在此
注:本文中本分原文保留,以斜体呈现。
简介
HTTPS是HTTP协议的安全加密版本。要想您的 Ruby on Rails应用使用HTTPS协议,您需要按照以下步骤来做:
- 获取证书 Obtain an SSL certificate
- 配置web服务使用证书 Configure the web server to use the SSL certificate
- 配置RoR应用使用HTTPS协议 Configure the Ruby on Rails application for HTTPS
本文中,我们将会依次简要说明以上步骤。由于有多种服务配置方法,来服务于使用HTTPS协议的 Ruby on Rails 应用,这里我们聚焦于最普遍的选择和配置。
如果你是迁移已存在的Ruby on Rails应用到 HTTPS,这将会有一些小的改变来简化迁移过程,并保持兼容于两种协议。我将会在本文最后提及一些。(there are a number of small changes and best practices that you should follow to simplify the transition and guarantee compatibility with both protocols. I\'ll mention a few of them at the end of this guide.)
获取SSL证书(Obtaining an SSL Certificate)
There are several different types of SSL certificates. 按照验证级别分为 (domain validated, organization validated, extended validation), 按照覆盖范围 (single-name, wildcard, multi-domains, etc.), and authenticity (self-signed vs. publicly-trusted certificate authorities).
通常,最多选择的证书类型是: single-name or wildcard certificates 。 They allow you to secure a single hostname (such as www.example.com
) or an entire subdomain level (such as *.example.com
). Unless you need some extra level of validation, domain validated certificates are the cheapest and most common solution. The second-most-popular alternative is the extended validation certificate, which is generally recognized by the green bar displayed by the browsers in the address bar.
生产环境下,你需要购买由工信证书机构签发的SSL证书 (例如: Digicert, Comodo, Let\'s Encrypt) or a reseller (e.g. DNSimple). To purchase a trusted SSL certificate, follow the instructions provided by the certificate provider.
For non-production applications, you can avoid the costs associated with the SSL certificate by using a self-signed SSL certificate. If you use a self-signed certificate the connection will still be encrypted, however, your browser will likely display a security warning because the certificate is not issued by a trusted certification authority. You can follow these instructions to generate a self-signed certificate.
无论申请的何种证书,签发之后你都应获得以下这些文件:
- 公钥证书 The public SSL certificate
- 私钥 The private key
- 可选的一系列中间证书或者合并了中间证书的合并证书 Optionally, a list of intermediate SSL certificates or an intermediate SSL certificate bundle
这些文件将在下面步骤中,用于配置web服务来支持HTTPS协议。
配置Web服务启用HTTPS (Configuring the Web Server to Support HTTPS)
本段中,我们将学习如何为大多数的RoR应用启用HTTPS协议。我们将会用到上面步骤中申请到的证书、私钥和中间证书( and the intermediate SSL chain)。
为了文中演示,我将使用到下面的文件名:
certificate.crt
- 公钥证书 the public SSL certificate
private.key
- 私钥 the private key
可能有些web server,需要你提供 SSL intermediate chain + public SSL certificate 合并为一个单独文件,也可能单独提供即可:
chain.pem
- 中间链证书 the intermediate SSL certificate bundle
certificate_and_chain.pem
- 证书及中间链这书合一证书 the SSL certificate and intermediate SSL certificate bundle
制作合一证书文件 Intermediate and SSL Certificate Bundle
如何制作合一证书可能是最具迷惑性的一步,因此,这步需要特别说明一下:(The creation of the intermediate SSL certificate bundle is generally one of the most confusing steps, therefore it deserves a special mention.)
合一证书只是一个包含所有中间链证书的简单文本文件。当然,一般是按照固定的顺序的,从最小范围证书直到最大范围证书(/根证书)。
(The bundle is just a simple text file that contains the concatenation of all the intermediate SSL certificates. The order is generally in reverse order, from the most specific intermediate SSL certificate to the most generic (and/or the root certificate). )
一般来说根证书是不包括在内的,因为它应该由浏览器或操作系统自带。如果合一证书需要包含服务器的证书,那么服务器证书一定是列表中的第一个位置(因为它是最小范围的) ( If the bundle has to contain the server SSL certificate, then this must appear as the first certificate in the list (as this is the most specific). )
SERVER CERTIFICATE INTERMEDIATE CERTIFICATE 1 INTERMEDIATE CERTIFICATE 2 INTERMEDIATE CERTIFICATE N ROOT CERTIFICATE
你可以使用文本编辑器以把这些文件连接为一个文件,或者使用命令 cat
来完成.
cat certificate.crt interm1.crt intermN.crt root.csr > certificate_and_chain.pem cat interm1.crt intermN.crt root.csr > chain.pem
Nginx配置HTTPS
Nginx通常作为前端代理配合其他的Rack web servers,比如:Unicorn、 Passenger、Puma 或者 Thin。(Nginx is generally used as a front-end proxy for other Rack web servers, such as Unicorn, Passenger, Puma or Thin.)
开启Nginx的 HTTPS,首先必须设置listen的ssl参数,然后指定证书的位置:
server { listen 443 ssl; server_name www.example.com; ssl_certificate /path/to/certificate_and_chain.pem; ssl_certificate_key /path/to/private.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; ... }
参考 Configuring HTTPS Servers for Nginx. 你可能想要 refer to cipherli.st for the recommended cipher and protocol configurations.
尽管你可以配置一个server block即监听443端口又监听80端口,但是一般作为最佳实践,推荐您配置两个不同的server block来分别服务于HTTP 和 HTTPS。
请不要忘记重新启动web server,才能使用您的新配置。
Apache配置HTTPS
Apache 通常是和Passenger联合使用。
要启用Apache使用HTTPS协议,VirtualHost必须监听端口
443,必须将
SSLEngine设置为on,然后你你必须指定
SSL certificate bundle和私钥的位置。
<VirtualHost *:443> ServerName www.example.com SSLEngine on SSLCertificateFile "/path/to/certificate_and_chain.pem" SSLCertificateKeyFile "/path/to/private.key" </VirtualHost>
如果你使用的是Apache 2.4.8之前的版本,你需要分别提供单独的公钥证书和SSL chain证书。If you are using a version of Apache before 2.4.8, please note that you need to use two separate files for the public SSL certificate and the SSL intermediate chain.
<VirtualHost *:443> ServerName www.example.com SSLEngine on SSLCertificateFile "/path/to/certificate.crt" SSLCertificateChainFile "/path/to/chain.pem" SSLCertificateKeyFile "/path/to/private.key" </VirtualHost>
See also SSL/TLS Strong Encryption for Apache. You may want to refer to cipherli.st for the recommended cipher and protocol configurations.
请不要忘记重新启动web server以使您的配置生效。
Heroku
Heroku is a popular platform as a service (PaaS) also used to deploy Rack applications. Heroku is a managed environment, therefore you don\'t get access to the low-level web server configuration.
In order to configure the SSL certificate on Heroku, you have to use the Heroku CLI to provision the SSL endpoint
1
$ heroku addons:create ssl:endpoint
and install the SSL certificate using the private key and the full SSL certificate bundle composed by the certificate and the chain (if any).
1
$ heroku certs:add certificate_and_chain.pem private.key
Amazon AWS
How you configure AWS to serve HTTPS depends on the specific AWS service you use. If you use a single EC2 instance, then you\'ll have to install a web server to host your Ruby on Rails application and the configuration depends on the specific server you will use. If you use Nginx or Apache along with a specific Rack web server, you can follow the instructions published in the previous sections.
配置RoR应用使用HTTPS协议 Configuring the Ruby on Rails Application for HTTPS
默认情况下,你只要正确配置好你的web server使用 HTTPS,就应该可以在HTTPS下装载你的RoR应用而不需要更多的配置了。
然而,为了有效地使用 HTTPS,推荐设置安全的 cookie,响应正确的 HTTPs 安全头,将HTTP请求重定向到HTTPS.
配置force_ssl
最近的Ruby on Rails 3.1以后的版本中,提供了一项配置项force_ssl,凭此可以强制应用使用
HTTPS。
你可以在特定的 environment配置中打开force_ssl配置。
# config/environments/production.rb Rails.application.configure do # ... # force HTTPS on production config.force_ssl = true end
请记住,您必须重新启动web服务才能应用此配置。
你也可以在全局配置application.rb
中配置force_ssl,这会影响所有运行环境。比如,这会同样影响测试和开发环境。
module MyApp class Application < Rails::Application # ... # force HTTPS on all environments config.force_ssl = true end
启用force_ssl之后,框架会自动执行如下的动作:
- 所有cookie标记为安全cookie (All cookies set by the application will be flagged as secure)
- 应用的响应中会包含HSTS安全头部,这将导致浏览器对于接下来的请求只通过HTTPS协议(The response will contain the HTTP Strict Transport Security (HSTS) security header that will instruct the browser to perform subsequent requests via HTTPS only )
- 所有HTTP请求会被重定向为HTTPS请求(All HTTP requests will be redirected to HTTPS)
It\'s important to note that this configuration applies to the entire environment: it affects all requests sent to the application and it\'s not possible to enable/disable it on specific controllers or actions. Furthermore, it\'s not possible to run the application under HTTP and HTTPS in parallel because of the point.
使用rack-ssl包 ( The rack-ssl
Gem)
如果你的 Rails 版本低于3.1或者你想要更多一些的控制,你可以使用 rack-ssl
。这个 gem 是一个 Rack
middleware ,提供了和force_ssl几乎一样的功能。实际上,rails以前就依赖于它。
添加到 Gemfile
gem \'rack-ssl\', require: \'rack/ssl\'
运行Bundler来安装
$ bundle
然后在配置中添加这个中间件:
# config/application.rb config.middleware.insert_before ActionDispatch::Cookies, "Rack::SSL"
推荐至少要添加到 ActionDispatch::Cookies
和
ActionDispatch::Static
(如果有的话) 之前,因为这个中间件需要基于HTTPS来优化这两个中间件的响应。
Rack::SSL
is a little bit more flexible than the force_ssl
configuration. You can enable it on a per-request basis by passing the :exclude
option.
The following example disables the middleware if the request comes from HTTP and it allows to run the application in parallel to HTTP and HTTPS.
config.middleware.insert_before ActionDispatch::Cookies, "Rack::SSL", exclude: ->(env) { !Rack::Request.new(env).ssl? }
This approach is particularly useful to silently rollout HTTPS in parallel to the HTTP version of the site, to test how your application behaves and fix bugs before forcing the entire site to HTTPS.
You can also configure the HSTS header by setting, for example, a different expiration and to include subdomains.
config.middleware.insert_before ActionDispatch::Cookies, "Rack::SSL", hsts: { expires: 2.years, subdomains: true }
Or you can disable the HSTS header completely. This is useful if the header is already set at another level, for instance, by a front-end proxy such as HAProxy or Nginx.
config.middleware.insert_before ActionDispatch::Cookies, "Rack::SSL", hsts: false
Of course, you can combine these options together:
config.middleware.insert_before ActionDispatch::Cookies, "Rack::SSL", exclude: ->(env) { !Rack::Request.new(env).ssl? }, hsts: false
You can also inject the middleware in a specific environment to enable it only for that specific environment. Alternatively, you can use the exclude
configuration in combination with an environment check.
Although the rack-ssl
gem is a little bit more customizable, it still works at the Rack middleware level, therefore, it\'s a little bit tricky to selectively enable or disable it on a per-controller or per-action basis. You could, in theory, use the exclude
option to filter by request path, but this approach may quickly become cumbersome if you have a large number of routes.
在控制器和动作级别配置HTTPS(HTTPS Configuration per Controller or per Action)
只对特定的controller和action启用HTTPS?这是可以实现的。通常的方法是使用 before_action来强制使用
HTTPS ,然而,我个人是反对这么做的,因为这样会造成你的应用的安全性降低,增加controller的复杂性,并且限制了使用增强安全手段的能力,比如安全头部( and it limits the ability to use extra security measures such as the security headers)。请一定优先考虑整个应用使用HTTPS协议而不仅仅是一部分。
In certain cases, a Ruby on Rails application is a container for more than one application or different components. Let\'s think for a second about Rails applications that consist of a front-end and an API, or a front-end, where the assets are served from different domains.
There may be cases where you need to enable HTTPS only on certain actions or controllers that represent one or more specific components. In these cases, it is recommended that you map these components to separate hostnames.
In other words, if the APIs are served via HTTPS and the website via HTTP, but they are both powered by the same application,
你应该使用这样的单独域名
https://api.example.com/ http://example.com
替换下面的主机路径
https://example.com/api/ http://example.com
as security policies such as the HSTS
and the HPKP
security headers applies to an entire hostname.
It\'s also quite easy to enable the rack-ssl
middleware only for a list of hostnames, rather than having to deal with controllers and actions.
提示和技巧 Tips and Tricks
The following is a list of suggestions and best practices you should follow to reduce the effort of migrating an existing Ruby on Rails applications from HTTP to HTTPS. Some of these best practices apply to Ruby on Rails or web development in general, and you can already follow them today regardless of your plans to move the application under HTTPS.
使用应用的router 来动态生成 URLs 和 links
一个需要长期遵循的原则就是,对于RoR应用来说,(不仅仅是HTTPS), 从来都不要在代码硬编码应用的路由路径。你应该总是使用router的helper方法来生成链接地址和URLs.
在邮件模板中看到像这样的硬代码链接并不罕见:
If you have any questions, feel free to <a href="http://example.com/contact">contact us</a>.
另一个很常见的习惯是在 JavaScript 文件或静态资产中硬编码应用程序 URL。
无论何时你需要生成路径,最重要的是一定要依靠 Ruby on Rails routing helpers 。最基本的 routing helper 是 url_for
,非常强大的方法。
对于命名的路由,Ruby on Rails提供了xxx_path
和 xxx
_url
helper方法。例如:你需要向映射路径为“/posts”的“posts”路由提交,你需要使用下面的方法:
posts_path
生成/posts
posts_url
生成http://example.com/posts
xxx
_url
helper 是非常强大的,因为它能够使用当前的请求的协议和主机名,自动的构建完整的绝对地址。因此,如果你使用这些帮助方法来生成绝对地址,当当前请求是基于HTTPS时,Ruby on Rails将会自动的生成使用HTTPS的链接。
自然对于由server运行时通过模板生成的视图来讲,这肯定可以工作的很好。但是对于静态的文件呢?比如avaScript文件中需要向应用发送请求的时候?就像我之前提到过,在 JavaScript 文件或 CSS 中硬编码路由并不少见。
对于JavaScript,你可以通过HTML 的data 属性来传递信息。例如,你有一个脚本需要在用户点击按钮时发送一个POST请求。
// somefile.js $.post("https://example.com/loader", function(payload) { $("#result").html(payload); }); <!-- file.html --> <div id="result"></div>
请抛弃这里面的硬编码地址,作为替代的,在模板中,通过 xxx_url() 来生成URL:
<!-- file.html --> <div id="result" data-url="<%= loader_url %>"></div>
然后,修改
JavaScript ,从DOM中来获取目标地址URL。
// somefile.js $.post($("#result").data("url"), function(payload) { $("#result").html(payload); });
<!-- file.html --> <div id="result"></div>
如果可能的话,尽量使用相对路径(Use Relative Paths Whenever Possible)
如果你使用相对路径,浏览器会为链接自动添加当前的主机名和协议。
例如,将下面的一行
<link rel="shortcut icon" type="image/x-icon" href="http://example.com/images/favicon.png" />
替换为
<link rel="shortcut icon" type="image/x-icon" href="/images/favicon.png" />
当然,如果你是用我前面提到的RoR的路由helper方法,这个技巧就不需要了。
Load Third-party Assets via HTTPS Whenever Possible
The most common error you will experience while transitioning from HTTP to HTTPS is the mixed content warning. This happens when your site is using HTTPS but you are loading an external resource (such as an image or a JavaScript file) via HTTP.
To prevent this issue, always link to the HTTPS version of a third party resource whenever you can. That will save you some headaches when you switch your website to HTTPS. In fact, in general there is absolutely no downside of embedding an HTTPS resource in a site loaded via HTTP, whereas the vice-versa is not true.
Most content providers are now distributing their resources both via HTTP and HTTPS. For example, let\'s suppose you want to embed a font via Google Font or jQuery from the code.jquery.com
CDN: in both cases, you can link immediately to the HTTPS version of the resource, there is no reason to use the HTTP link.
In the past, it was also quite common to use the protocol-relative link to delegate to the browser the decision to use HTTP or HTTPS depending on the request.
<link rel="stylesheet" media="screen" href="//netdna.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css" />
However, now that SSL is encouraged for everyone and doesn\'t have performance concerns, this technique is considered an anti-pattern. If the asset you need is available on SSL, then always use the https://
link, as I mentioned before.
配置浏览器安全头 Configure the Browser Security Headers
无论 force_ssl
或者
rack-ssl
选项都是设计未来快速的应用一组默认设置及安全配置,以期你的应用马上可以开始使用 HTTPS。
然而,当你的应用已经运行于 HTTPS,你可能想要更好的发挥HTTPS 作用,正确优化应用返回的security headers,请参考这篇Troy Hunt的文章: Introduction to Browser Security Headers。This course will help you to better understand what the security headers related to HTTPS are and how to properly use them.
————————————————————
另外,如果想在开发环境总时使用HTTPs协议,参考这篇:
请发表评论