We recently launch subdomain
& custom domain
features in our product: Logdown, a Markdown Blogging Platform.
People keep asking me how to implement, here is how :
(1) Constraint Routing
And in Subdomain
class:
If it doesn't match logdown.com
& www.logdown.com
, then it goes straight to the constraint routing.
(2) Find Current Blog
In PostsController
, we also build a method find_blog
to find @current_blog
.
The sequences will be : subdomain
=> custom domain
=> Not Found
(3) Nginx Setting
The code is very simple, but it tooks me lots of time to figure out...
Yesterday, we changed Logdown's assets host from local to Cloundfront. One day later, many firefox users complained about the icons are broken.
We found out the reason is: Firefox doesn’t allow cross-domain fonts
Catalin Rosu nicely explains the situation and the reason behind it here:
http://www.red-team-design.com/firefox-doesnt-allow-cross-domain-fonts-by-default
And there were also some discussions on font-awesome-sass-rails issue tracker.
- https://github.com/littlebtc/font-awesome-sass-rails/issues/30
- https://github.com/bokmann/font-awesome-rails/pull/27
The fix:
Modify Nginx Config
Add these three lines to /opt/nginx/conf/nginx.conf
Go to Cloudfront panel to purge old content
You can find out assets path from ~/project_name/current/public/assets
, by typing ls -al fontawesome-webfont-*.*
Go to CloudFront Panel, create a invalidation
paste
Invalidate!!
Wait about 5 minutes or longer...
It takes time to invalid CDN cache, just be patient..
By typing curl -I "https://d1vzm1nztjpyu4.cloudfront.net/assets/fontawesome-webfont-009f6d1f667cc42c25e712ab3429cbc7.eot" -s
, you can checkout the status.
Once the response shows Access-Control-Allow-Origin: *
, it is done :)
長話短說:想學好 Ruby on Rails,Tealeaf Academy 是你最佳的選擇
承諾我的朋友 @knwang 要寫這一篇很久了,但是一直被諸多事情拖到,現在來補寫...。相信認識我很久的人,都會知道我寫這篇文章相當不尋常。我一般不幫人廣告,我又是 Rails 這個領域的專家,很少會為其他人的實力背書,而且我從來不推薦我沒用過或者用過覺得不好的服務。
TeaLeaf Academy 是 我的朋友 @knwang 的創業公司,專精 Ruby on Rails 教學。創業至今,也在世界各地上教出了接近上百位 Ruby on Rails developer。Kevin 同時也是大陸知名 Ruby Podcast Teahour 主持人之一。
我自己教過很多人 Ruby on Rails,曾經出了一兩本有關於 Rails 的書籍,平常也會在社群裡面解答各式各樣的疑難雜症。很多人練完 Rails 101 之後,都會問我:「xdite,接下來我該學些什麼?」或者是「哪裡有課繼續上?」
在以往,我的建議都會是去找個工作開始實戰,而不是繼續上課。不過今年開始,我開始推薦想要立志練好 Ruby on Rails 的開發者,可以去報名 TeaLeaf Academy。
為什麼呢?我們平日在指點 Ruby on Rails 的開發者,實際上都很清楚一件事情:坊間所有的書籍和教材是很難在短時間「教」出一個 Full Stack 的 Rails Developer 的。要成為一個 Solid 的 Ruby on Rails developer,唯有不斷的實戰,在實戰中不斷的累積經驗,再從線上開發者社群吸收社群最好的實務經驗不停地自我修正,才能達到這個目標。
市面上的入門書籍還有什麼七天的速成課程,是很難幫到你達到這個目標的。原因在 (1) 它們的內容,多半是簡單的 CRUD 加上一些現成的套件,讓你很快就能拼湊出一個煞有其事的 demo application。 (2) 多半是單方面的演示學習,也不會逼迫學生動手實作與課後練習。這樣的強度,多半只能讓學生在結業時達到所謂「略為熟悉 Ruby on Rails 的開發環境,可以開始基礎的修改擴增」的程度。不過,這樣也不能說這些課程沒有營養。因為實際上,光這樣的基礎內容,初學者要靠自己摸索,可能就要掙扎奮鬥接近一兩個月才能搞定了。
從很多的線上學習課程走向,如知名的 Codeschool 以及 Thoughbot 推出的 Learn Prime,你可以觀察到一個正確觀念以及趨勢。要所謂的「吸收成長」,課程的安排走向必須要「親自動手作」才行。但即使是這兩個公司,推出的課程也都只是「片段」。
而要快速的提升實力,則唯有加入一個實戰且有技術累積的團隊操練,有一個線上的 Application 待維護,有一個已企劃好的骨架可供實現。但是,這樣的工作機會不是到處都有。而這樣的工作機會,通常也少 offer 給「新手」。
而我會推薦 TeaLeaf 的課程關鍵原因,是因為 TeaLeaf 的內容提供了這樣的一個 option,他的內容不僅是「實際動手作」,更是一個完整的 full stack 的 project。在紮實的八個禮拜開發課程,你必須藉由完成每週的 assignment,最後親手搭建出一個 high quality 的 EC 網站,徹底學到開發一個實際的商務網站,你該準備練習的方向有哪些。而更值得一提的是 Kevin 的背景,Kevin 在創辦 TeaLeaf 之前,是美國知名 Rails Consultancy :Hashrocket 的工程師,在這個課程中你也可以實際上學到不少 Hashrocket 內部本身的開發精隨。
我會這麼清楚,是因為我五月時,真的實際的(自掏腰包)報名了 TeaLeaf 的課程,從當中學到了不少連我自己以前自學都難以精熟的開發技巧(這個課程裡面也有相當全面的 TDD 以及高階的 Service Design 主題)。平常我在網路上報名過不少高階課程,還是第一次報到這麼紮實的。
這裡有一份 Kevin 當初給我的課程清單 https://gist.github.com/xdite/4044f3a037de029bc35c
Anyway, 在上過這個課程之後, 要是有人想要請我教他 Rails, 或者是想要找 Rails 課程報名, 我都直接推薦他直接去上 Kevin 所開設的課程了。而且 Kevin 拆解課程觀念以及教學的技巧,真的都非常的棒。
目前 TeaLeaf Academy 的課程有三種:
- Introduction to Ruby and Web Development: $475 for 4 weeks (如果你連 Web Development 都不熟的話,報這個)
- Rapid Prototyping with Ruby on Rails: $610 for 4 weeks (如果你是跨領域來學 Rails,但懂 Web Development,報這個)
- Build Production Quality Applications: $1485 for 8 weeks (如果你寫 Rails 有一年經驗以上了,來報這個)
不過對於他們的課程,我只有一件小事情想要備註而已。因為 Kevin 的課是採取 Remote 方式授課,以交作業與每週 Live Session 的方式進行。第三階段的課程分量是非常非常重的,Kevin 曾經跟我說他有學生上 Rails 課上到必須辭職才能專心上完這門課。我本來以為 Kevin 是開玩笑,哪那麼誇張。後來上了以後,真的覺得所言不假。連我自己的程度平均每一週都要花上 8 小時來寫作業。同學花到 3-40 小時的比比皆是。若說有人要非得辭職才能上完,我看也不是沒有可能。(請斟酌你的時間分配再行報名。不過要是中途 Kevin 發現你跟不上進度,他是同意讓你到下個班補上的…)
但是若你能堅持到最後,功力是真的會有非常非常的大躍升,這點是我也可以幫忙保證的。與其花一堆錢在 CRUD 上,我誠摯的建議不如把錢省下來在這個 program 上面。甚至我現在收人的標準,也是變成希望收從 Kevin 班上畢業的 Developer 了。
祝各位能夠早日能達成自己的目標 :)
P.S. 課程是以英文進行。但是 Kevin 是華裔,所以你私下寫 mail 問他時是可以寫中文的。
這是我六月初在新加坡 Reddot Ruby Conf 給的演講。原稿投影片放在 Github 上。這篇文章是我自己整理的 Note。
其他的演講多半是重談 OWASP 那些標準需要注意的地方。這個演講當時在設計時,刻意跟其他演講不太同調。原始設計的出發點著重於:如果是一個熟 Rails 架構的 Hacker
(1) 會如何進攻你的 Application
(2) 初學者會在架構設計上犯哪些錯?
(3) 如果你是 Application 設計者,如何一開始就設計出相對安全的架構
在談 Security 之前,我們要來談談 Rails 是不是個「安全」的框架?
平心而論,Rails 的預設是比許多框架要「安全」許多的,它的設計預設就防了很多攻擊手段、屏蔽了開發者設計上的盲點,或者是預設就內置許多最佳實務。
- Helper / View 預設提供了 HTML Escape : 防止 XSS
- ORM 預設提供了 SQL Escape : 防止 SQL Injection
- 表單提供 Authenticity Token :防止 CSRF
- 在 Production mode 錯誤時直接扔 500 頁面:防止 PHP 忘記關 debug mode 結果吐程式碼這種慘劇...
- 熱門的使用者驗證 Gem 如 Devise 直接內置密碼 Bcrypt 加密 :有些開發者懶得自己寫加密 function 乾脆明碼 password 直上
- Sensitive data filtered from log:Rails 的 Log 自動會濾掉一些關鍵字如 [password],以免幹到 Log 就超精彩..
- 熱門的上傳 Gem 如 Paperclip 內建 Filename Sanitization 的實作:防止 path attack
很多傳統打 PHP 網站的方式,是很難拿來直接打 Rails 的,因為一些常見的路都預設被堵死了....
但。這就表示 Rails 不好打嗎?
這也未必。Rails 是個「Framework」。所以其實也有「Pattern」可以依循。一般要打下一個站,去找 Framework 0day 效率通常太低了。找常見的「人為錯誤」其實是比較快的方式...
1. Massive Assignment
Rails 在表單設計上提供了強大的 Helper,表單的 attribute 直接對應到資料庫的欄位。
會生成
看出規律了嗎?所以如果是這樣,從 Chrome DOM Inspect 塞...
通常就可以達到目的...
因為大部分的 Rails Developer 都會寫出這樣的程式碼:
注意 @topic.update_attributes(params[:topic])
那一段。
也有風險的地方:role_ids
除了單一 attribute 會被攻擊外,另外還有一個虛擬的 attribute 設計會有風險。
在這個 user model 裡,使用者擁有很多角色。
Rails 提供一個十分方便的設計。user 可以會自動擁有一個虛擬的 attribute role_id
可以用。role_id
是 Getter 也是 Setter。
如果你在系統的 rails console 下這樣的指令
Rails 會自動幫這個 user 設上 Role id = 1, 2, 3 的三個角色。之所以會有這個「貼心」設計,是因為「大家」都用 checkbox 作多選角色,所以內建了這個設計方便一次設定上多個角色。範例 code 如下:
但這也就表示,其實你的 application 有可能被這樣的假 DOM 玩到的風險....
特別是大家都曉得 role_id = 1
通常就是 admin
。
需要被檢查的地方:
-
has_many
,has_many :through
involve OWNERSHIP, Permission -
user_roles
,group_users
, …. -
UPDATE
action
幾個可能有效的解法:
Rails 3 可以用的解法
( 這個方法在 Rails4 裡被移除了)
- 使用 whitelist attribute
- 打開
config.active_record.whitelist_attributes = true
- 在 model 裡設定
attr_protected :roles
推薦的作法,同時也是 Rails 4 解法
- 使用 Strong parameters。( Rails 4 如果沒過 permit 會直接丟 exception,算直接根除)
params.require(:topic).permit(:title, :body)
進階做法
使用 Reform。出發點是 validation 的責任不應該在 Model 身上,也不該丟給 Controller 去解決,這件事應該要在 Form 層面被解決掉。Reform 的作者同時也是 Cells 的作者 apotonick。
2. Admin
第二部分要談的是 Admin 的設計。這部分也是很讓人無言的。根據非官方統計,高達...99% 的開發者會把 admin 後台放在 ... /admin
...XD
這樣的設計有幾個 concern :
- 容易被猜到放哪裡
- 容易被 XSS 打到
通常建議的解法
把 admin 拆到其他地方去,如 subdomin,或者是不同 domain。
一些其他的基本解法
- 拆到 Intranet 上。(Ineternet 上基本找不到)
-
WiteList.contains?(request.remote_ip)
只准白名單 - 拆到另外一個 Admin App 去管
也有風險的地方:admin?
這也是另外一個有風險的部份。關於 admin?
的實作。多數的開發者,是這樣做的:
然後,就被針對 massive assignment 的攻擊打下來了...
一些解法
-
Setting.admin_emails.include?(email)
// 起碼沒有那麼直觀 - 使用第三方驗證 gem : 如 Github 的 team warden-github-rails 作驗證。想法是:Github 比你家難打太多了....XD 而且其實會是 Admin 的人通常也是 Github 這個 repo 的參加者,用這種方式去驗證比較方便...
下篇: Secure Your Application : The Basic (2)
上篇: Secure Your Application : The Basic (1)
3. bypass RESTful
RESTful 是初上手 Rails 的開發者,相當討厭的一個課題。因為沒有很好懂,加上被認為不自由。總之,不遵守 RESTful 設計原則原因有很多
- 老子就是討厭 RESTful
- 我...不懂 RESTful 要怎麼寫
- 真的有必要把任何 action 都走 RESTful 嗎?
但是,很多開發者不知道的是:雖然 Rails 提供了 CSRF protection,攻擊者發了錯的 request,就會被導錯誤 422。但是,這樣的預設防禦只在遵守 Rails RESTful 設計規範的情形下運作。
在 Rails 3 的 routes.rb
最下面有一段被註解掉的 code
很多開發者在不知道怎麼設 routing 的情況下就把 match ':controller(/:action(/:id(.:format)))'
這行打開了。
因為打開,程式就可以動了 .....(汗)。
可以繼續快樂的寫 http://example.com/usses/eat/1234 這種直觀的 routing,而不用去管 Rails RESTful 的原則...
這等於把自家大門敞開任人打 XD。
因為 Rails 防禦 CSRF 攻擊的原則上是若對 routing 送了錯誤的 http verb 或者是送了錯的 Authenticity Token,Rails 就視為非法 request 導掉。但是 match
everything 的結果是,所有的 action 都變成了 GET
,一些需要保護的 POST
、PUT
action 就門戶洞開了。
可能的解法:
- 從
routes.rb
幹掉match ':controller(/:action(/:id(.:format)))'
這行 - 設立 coding policy,嚴禁任何人再把他加回去。
Rails4 起也已把預設的這行 example 拿掉了...
4. match in routing
這要從 routing 開始講起。
在 Rails 3 時,因為...我也不知道的原因,match
等於 matches all HTTP verb
。而所有的 custom routing 的教學都推薦用 match
去實作。所以很多 application 裡的 route 很歡樂的到處都是 match
。
但是,到處都是 match 就會造成下面的這種類似的 code 會有中招的風險。(allow using GET to massive delete articles)
解法
- 檢查
routes.rb
幹掉所有的 `match,改用正確的 HTTP verb - Rails 提供正確的 verb 用法可以取代掉
match
:get
,post
,put
,delete
所以上面的 example 可以改成
- 或者是改用
via
下篇: Secure Your Application : The Basic (3)
上篇: Secure Your Application : The Basic (2)
5. bypass HTML Escape
Rails 預設對所有 helper 出來的字串,先都過了一層 html escape。所以是相當安全的。
但是,在開發者設計一些複雜的元件時,通常會不小心打破這個原則。如美術設計師對該元素下了複雜的 HTML,程式設計師為了貪方便可能就會這樣串接,最後再下一個 raw
/ html_safe
...
原來那層天然防護罩就不見了。
網站上哪一些設計容易有這種問題:
- 列表 list
- 麵包屑 breadcrumb
- user name with glyphicons
解法:
回歸最初,該是 HTML 的就不要用 helper,用 partial
也可能有問題的地方:TinyMCE on UGC
有些網站被迫要讓使用者使用 TinyMCE 這種所見即所得的工具。但是這種工具危險之處,在於裡面塞的 HTML 可以非常的有創意...如:
- img
- table
- tbody
- div
- span
都是高危險地帶。
可能的解法:
Rails 提供消毒 Helper。可以設白名單黑名單..
6.bypass SQL escape
Rails 在 ORM 裡面也預設提供 SQL escpe,所以這種 query 是安全的:
但是有些時候,開發者為了實作某些功能,就會不小心用了 find_by_sql
:
或者是開發者根本就不知道 where
的正確用法,在網路上 google 到一個 sample 會動就丟進去了:
高危險地帶:
- Search Functions
- actions with complex options, ex.
:date
,:order
,:field
- actions with complex joins
-
find_by_sql
,count_by_sql
解法
如果只是搜尋的話,可以改用 ransack 這種 gem,可以用相當漂亮的 DSL 設計出複雜的 SQL search,都沒有 SQL Injection 問題..
設計 Application 時,優先使用 ORM 解,非不得已為了效能才手 drop query
延伸資料
- http://rails-sqli.org/ Rails SQL Injection 大全。
下篇: Secure Your Application : The Basic (4)
上篇: Secure Your Application : The Basic (3)
7. Same secret token
Rails project 裡的 config/initializers/secret_token.rb
這個檔案。裡面的 secret_token
是用來 verify signed cookie 的。在產生 Rails project 時,每個 project 也都會生一把不同的 key...
狀況 1 : fork opensource project
不過如果你的 project 不是你產生的,而是 Github 上 clone 的呢?那麼有 99% 的機率,你這個 application 已經暴露在高度風險之上。
- clone 的人忘記跑
rake secrect
產生一把全新的。 - opensource 的人忘記把
secret_token.rb
設進去.gitignore
-
google://secret_token.rb site:github.com
超精彩...
狀況 2 : opensource project owner
- opensource 的人忘記把
secret_token.rb
設進去.gitignore
- 而且 production 用的就是這一把...
該檢查的地方以及怎麼處理
- 如果你的 application 是從 github 上 fork 下來的。抓下來第一件事就是砍
secret_token.rb
,重跑rake secrect
- 如果你是 opensource project 維護者,發布之前,請砍掉
secret_token.rb
,設進 .gitignore,然後換 key。 - Redmine 的作法是預設移除,然後設進 .gitignore
- Discourse 的作法是保留,但用 condition 設定 production 和 development 跑不同的 token,production 跑的是環境變數:
ENV[‘SECRET_TOKEN’]
。
8. scopes
很多 Application 是這種常見的設計,利用一個 check_permission
去應付多種狀況。
但是 check_permission
的狀況很容易不小心寫漏了,就能夠進到該頁面。
問題與解法
- by case 設計在 controller 不是好解法,因為 helper / view 有時候也需要類似邏輯
- 邏輯經過幾次擴充之後就變成漿糊了。擴充了 Controller 忘記改 View ...etc.
- 改用 Cancan 這種 Rule Engine 式的設計方法,全上黑名單,再一個一個設白名單解開,而且寫一次可以套用在所有地方。
9. Upgrades
Rails 在 3.2.11 前的版本,都有一個致命漏洞:可以被 Remote code execution。Remote code execution 表示攻擊者可以對你的 application 你的 server 作 任何事
原理詳見:http://blog.codeclimate.com/blog/2013/01/10/rails-remote-code-execution-vulnerability-explained/
基本上只要這個洞還留著。其他地方再怎麼防都沒有用
解法
- 升級到 3.2.11+
Recap
Security 是一件讓人很頭痛的事。Rails 的 core member : Aaron Patterson 就曾經抱怨,其實他最討厭處理 securtiy
事件。
- 因為,attacker 不會主動回報,就算它跟你講了,你也不知道這個洞已經多久了
- 即便他好心跟你講(寫信到 Rails security mailing list),你也只有「很短」的時間修。很短可能是幾天,也可能是幾小時,但總之...火會燒得超級快。每次遇到資安事件,security team 的人就不用睡了...
- patch 得馬上得 Release。不同於其他 feature release,feature release 都會有時間公測。唯有 security patch 沒有這個時間。而這種 patch 最危險的是不保證會不會炸了其他沒問題的地方。(某次 patch 炸了 Github ...)
- 你努力了拯救了整個世界,但沒人會感謝你。但如果你寫的 patch 炸爛了大家的 production,肯定一堆人角你...
洞是真的防不慎防的。
有時候就算框架本身是沒問題的。但是還是會因為人為疏失被摸進去。
不過如果是人為疏失,其實還是有幾招可以降低發生的機率:
- 注意使用者塞給你的東西 Pay attention of user geneate content
- 不要繞過 Rails 的預設機制 Avoid bypass default design
- 盡量套用 Rails 4 的新機制 Apply new features introduce by Rails 4
- 不要忘記更新 Don’t forgot to upgrade