[譯]構建現代Web應用的安全指南

原文:Security for building modern web apps
譯者:傑微刊—張迪 php

 

這篇文章的靈感來自於另外一篇文章,它是關於「在今天,構建Web應用以前要知道的事情」的。並不長,但遺漏了一些關於安全性的建議,因此我就此動筆,分享一些這方面的知識。 html

 

本文重點是寫給那些來自初創公司,而且想要從頭開始開發一個Web應用的開發者,他們並不知道太多信息安全的知識,也不想花太多時間考慮其應用程序的安全性。一些重要的內容就不在這裏討論了,諸如威脅建模(threat modeling),持續交付安全(continuous delivery security)等等。這篇文章的目標不是要取代現有的代碼安全檢查表(例如,OWASP,SANS),而是要從今天的視角補充一下它們。畢竟,安全概念是很老的,(例如,安全設計原則是在70年代被定義的),它會在今天乃至將來都繼續存在,因此安全也須要與時俱進,適應現實。 前端


注:雖然像這類的文章都是有益的,可是安全是一個過程,必須從一開始就與開發過程緊密關連。要始終考慮找一個應用安全專家來幫助你。 git


客戶端 Client github


輸出過濾(Output filtering):著名的跨站點腳本(Cross-Site Scripting),也被稱爲「XSS」或「HTML注入」,在沒有輸出過濾和執行某些代碼時就會出現問題。防護方法依賴於上下文,如: HTML標籤屬性上的動態值(onclick、onload等),或標籤內部(如,$("p:first").innerHTML=dangerousVariable)。只有在把動態變量存儲在HTML標籤的屬性中時,這種危險代碼纔會生效。過濾輸入對安全會有幫助,可是記住,XSS取決於上下文,因此不是全部的過濾都是有效的。這裏有我對XSS的詳細解釋(PT-BR)。 web


使用靜態頁面(Use Static Pages):單頁應用程序(SPA)的優勢除了因爲ajax請求而減小的通訊阻塞外,還有就是它擁有一個靜態前端。這就意味着有更少的攻擊面和更低的成本,所以你能夠在Amazon S3上存儲你的全部內容,並讓Amazon保證其安全,在你沒有一個安全技術團隊或者你的安全技術團隊不如Amazon擅長這個領域的狀況下,讓Amazon提供安全保證是很是棒的。SPA的缺點是缺少自定義HTTPS的證書支持(custom https certificate support)。你須要轉移到Amazon CloudFront(CDN)上,這很容易實現,它將提高你的web應用的可用性。缺點是須要處理文件夾失效(assets invalidations),但不會太多。他們用一些使用文件名的版本管理技術,雖然很糟糕,但有總比沒有好。 ajax


避開第三方網站的JSONP反應指令和多種JS文件(甚至廣告網絡):若是容許第三方網站在你的網站注入JavaScript代碼,而且毫無保留地信任它們,結果就是加大了你的網站的被攻擊可能性。當心那些響應數據的API,不要讓他們輕易被執行。看看Troy Hunt的案例吧。 算法


不要留下HTML註釋:有的安全工具能夠用於搜索HTML註釋,並呈現給攻擊者,以查看是否有任何用處,例如OWASP WebScarab。刪除HTML註釋。若是你須要註釋,就在頁面生成的時候使用動態語言來添加註釋,這些註釋就不會出如今響應中了。 spring


客戶端校驗(服務器端固然也要執行):服務器端校驗不能被替代,有兩個優勢:1)更好的用戶體驗,由於反饋迅速;2)阻止了後臺的無用請求,從而提升有效性。 數據庫


退出(logout)應在每個頁面都是可見的:請不要忘記這一點。最好是在預期的地方,如點擊用戶的頭像以後的右上角。


若是你將數據存儲在客戶端,必定要謹慎:相同的威脅適用於移動端,也適用於其餘客戶存儲的設備,會形成數據丟失和被盜。若是你將數據存儲在客戶端了,就要假設有人會看到它,因此不要存儲重要信息。存儲就要加密,並把key保存在cookie裏(沒有可被JavaScript讀取到的HTTPOnly標記),至少保存到當前會話結束。當用戶註銷的時候要刪除全部信息。根據數據,你可能想要使用例如HMAC的技術來防止完整性違規(integrity violations)。不管如何,記得這樣使用它。固然,服務器中也要保存key。當用於session存儲機制時,Rails的cookie會和服務器的APP SECRET一塊兒使用。爲了增強這個概念,可使用Json Web Tokens(JWT),它是目前作這件事(data + signature + algorithm used + expiration + base64 encoding + json format)的標準。


考慮用Json Web Tokens(JWT)取代session:你可使依賴於JWT的無狀態服務器,而不是session和數據庫。缺點是保密性差,看上一條就知道了。這個方法能夠提升應用的有效性,若是把它們存儲在LocalStorage而不是cookie中,還能夠防止CSRF攻擊。CSRF發生時瀏覽器無反應(dumbness),即便是跨域請求,cookie也有被傳輸到服務器的風險。


切記,LocalStorage會受到XSS影響,HttpOnly標識的Cookies則不會:雖然這有利於存儲session標識符(cookie w/ HttpOnly標識),但仍有CSRF的風險。這是一個權衡,記住這一條便可。


服務器端 Server


選擇一個web框架,至少是MVC:遠離構建web應用程序的腳本。最經常使用的框架已經給了你一些保護(例如,CSRF保護,Security頭),若是你正在寫PHP,直接使用它們就行了。可是,要當心,你可能會在下面一條跌倒:


避免太過異想天開:我認爲這是開發人員中最多見的缺陷。他們對某些有用的功能或框架十分滿意,而且盲目地相信它們。這爲許多安全漏洞和bug的產生留下了空間。最多見的例子是OAuth庫。使用SSO前,必定要了解它的工做細節。不然你會身份驗證失敗。在開發過程當中也沒有免費的午飯。在開發以前,在你的應用程序裏插入一些未知代碼,作一些code review,靜態分析,檢查已知bug(CVE),並在可能的狀況下閱讀一下RFC,可是不要盲目地去作,尤爲是在web應用程序的關鍵部分,如身份驗證、受權、責任和支付處理/儲值卡。


驗證CORS源(CORS Origin):除非你打算向整個世界開放API,你應該只容許單頁應用的源地址被調用,以免其餘網站的瀏覽器內(in-browser)調用。


默認設置Cookie標識HTTPOnly:HTTPOnly標識有更多的Cookies是必須的,這能防止Javascript訪問cookie值(如會話cookie),這樣作能保護Cookies中的信息,即便發生XSS。實際上,恕我直言,HTTPOnly應該是默認屬性纔對,non-httponly只有在異常中才使用。沒有這個標識的cookie僅能用於客戶端訪問,例如一個根據用戶偏好顯示或隱藏菜單的標識符。LocalStorage對它的支持也很好,因此咱們應該再也不使用沒有HTTPOnly的Cookie。


默認爲Cookie設置secure標示:secure標示容許cookie只能經過HTTPS鏈接傳輸,這是偉大的,但你須要有一個HTTPS端口監聽工具。現在,它應該是一個必備設置,不只爲了安全,並且爲了增長你的谷歌搜索查詢排名。據我所知,你不能夠在Amazon S3上使用自定義證書。你須要將你的自定義證書部署到Amazon CloudFront(CDN)上,這對你的密鑰來講是有害的,但對於小團隊來講別無選擇。CloudFlare想到了這一點,開發出了無需key的SSL,但你須要創建一個能處理全部SSL握手的服務器,至少是使用這個鑰匙的一部分標頭,這也意味着須要更多的服務器和更高的成本。


避免業務邏輯Bypass:最多見的缺陷之一就是受權bypass,甚至在facebook上你能夠看到這種事情發生。例如,編輯用戶賬戶的細節時,你能確保若是用戶輸入嵌入了另外一個用戶的user_id時,你的應用可以阻止此次更新麼?你須要在全部的控制器(controller)上仔細確認。這一般是一些開發人員必須本身實現的驗證,因此一般被忽略,或實現得很難看。你本身測試一下,也邀請一個有作安全程序背景的人來測試一下,甚至作一些單元測試來驗證你的controller。質量分配漏洞(Mass Assignment Vulnerability)也值得注意,homakov利用它攻擊過GitHub。你須要將你的模型參數列入白名單,不然攻擊者會經過猜想他們的名字,利用「framework magic」,經過請求參數構建出模型對象。


在你的API中放置CSRF保護: Web框架一般建議你使用CSRF保護,當你構建API時,看到「請求中缺乏CSRF token」的消息時,你通常會禁用它以後繼續編碼。不要那麼作。CSRF真的很危險,提醒你本身,確保添加一個CSRF token,即便是在API被調用時。你能夠經過如下3種方式作到這一點:


① 有狀態session:在每個session上添加CSRF隨機token,檢查每個請求中它們是否匹配。


② 無狀態的雙Cookie提交技術:攻擊者能夠操縱請求體(request body),但不能操縱cookies,由於它們來自另外一個域,在cookie和請求中向服務器發送相同的隨機值,並檢查它們是否匹配;若是你的用戶(或第三方腳本,如廣告)能夠控制任何子域,你也有一些技術能夠bypass。從Blackhat的文章中獲得更多的信息。


③ 無狀態的Json Web Token:存儲在LocalStorage中,並在每一個請求中發送。攻擊者不能訪問跨域的LocalStorage。


不要讓全部操做都得到訪問你AWS賬戶所有資源的權限:你不會浪費太多時間爲你應用的AWS訪問憑證找出正確的許可。不要傻到容許訪問全部東西。若是你將key上傳到一個公共的GitHub庫,你就完蛋了,會被攻擊,設置權限下降風險吧。


不要將證書存儲在源代碼裏:從源代碼部署之外的環境或文件中去讀取證書。剛開始會有些麻煩,但一些函數庫使它很是容易,如ruby的dotenv gem

 

當進行服務端到服務端的通訊時,驗證端點證書(endpoint),考慮pin它或它的公鑰:當你瀏覽一些HTTPS網站,瀏覽器會驗證其信任的CA。但當你進行從服務端到服務端的通訊時,誰來作驗證呢?一般沒人,因此你須要本身設置邏輯去驗證端點證書。驗證經過以前,不要容許別的操做,不然SSL/TLS就沒意義了。除了在傳輸過程當中加密數據,HTTPS的另外一個目標是驗證端點的真僞,從而防止中間人攻擊。能夠考慮使用證書pinning(Certificate Pinning),或者更好的公鑰pinning(Public Key pinning)。OWASP有一篇很好的文章詳細解釋了這一點,因此我不贅述了。最基本的是你只能和你所期待的人交談,例如,從給定的X509證書中生成一個摘要(digest),並把它與硬編碼摘要(hard coded digest)做比較。可是有一個問題,若是證書撤銷或者改變,服務將會被拒絕。更好的選擇是使用公鑰鎖定,由於公鑰存在於X509證書中,除非證書使用其餘密鑰對從新生成,不然不管是被撤銷仍是改變,均可以順利的經過公鑰被驗證。這些對移動應用程序也是必須的。


設置安全頭(Security Headers):經過在響應中設置安全頭,便可保護web應用免遭點擊劫持(Clickjacking)、反射型XSS(Reflected XSS)和 IE內容探測(IE content guessing)的攻擊(注:若是你發送配置正確,Ruby on Rails能爲你作大部分的工做)。更多細節,請查看OWASP頁面。

① X-FRAME-OPTIONS:用「否定」或「同源」來防止「點擊劫持」。


② X-XSS-Protection:「1;mode=block」迫使XSS反射保護,在Chrome中是默認的, IE中不支持。


③ X-Content-Type-Options:「nosniff」遺憾的是,IE試圖猜想web頁面的內容,即便這個content/type意味着其餘內容類型。若是IE檢測HTML代碼,它將容許txt文件執行腳本。經過使用這個標頭禁用它。


④ Strict-Transport-Security:「max-age=16070400;includeSubDomains」HTTP Strict-Transport-Security(HSTS)保證安全(HTTP經過SSL/TLS)的鏈接服務器。即便是用戶類型的HTTP(user types http),瀏覽器都將強制HTTPS,這是很棒的。


⑤ 還有其餘的,例如Content Security Policy(CSP),就不在這裏討論了。


在「註冊」和「忘記密碼」頁面使用驗證碼:多虧了谷歌的reCaptcha,現在的驗證碼已經不是很煩人了。今天,你能夠驗證用戶是不是基於他的行爲而不只僅是人類挑戰,從而防止假帳戶和瘋狂的發送電子郵件。


存儲API密鑰就像你存儲密碼同樣(或儘量這麼作):若是雙方泄漏的影響是相同的,那麼爲何儲存一個比另外一個更安全?其實是有一些不一樣之處的,但關鍵是不要在明文中存儲API密鑰。API密鑰應該是系統生成的隨機字符,因此他們不會受到字典攻擊(dictionary attack),就像密碼,可是,在數據庫/文件系統/ OS中,API密鑰將在未經加密的文字或數據中可用。也就是說,至少一些hash是必要的。若是你使用像scrypt或BCrypt這樣的工具,你就要當心了。scrypt或BCrypt由於其緩慢的哈希計算,很是建議用於密碼。緩慢的哈希計算也會致使服務被拒絕。你輸入一次密碼,獲得一個session ID;可是API就不一樣了,API驗證時刻都要被調用,因此速度緩慢會下降應用的可用性。存儲API Key的摘要,足夠知足你的使用了SHA256或SHA512算法的應用了。遠離MD5和SHA1。必定要遠離!


至少爲用戶使用UUID做爲主鍵,而不是順序ID:防止用戶賬戶的猜想/暴力破解和輕易複製。有更多的優點和少數劣勢,但它是值得的。注意:相較於連續整數,UUID並不會使應用更安全,僅是從安全的維度增長了不可法猜想性和模糊度。


忘記密碼和電子郵件確認的token:爲忘記密碼或電子郵件確認生成一個token時,請確保使用安全的僞隨機數生成器(RPNG),不然可能被猜到。使用能夠信任的庫/語言API。也爲這個token設置截止日期/時間。設想一下使用情景,用戶不想改變本身的密碼,但一週後,有人攔截了電子郵件,訪問了那個URL,並改變他的密碼。這是沒必要要的風險。


在郵箱更新時通知舊郵箱:帳戶侵權以後最多見的行爲是改變賬戶的電子郵箱,來防止其全部者恢復密碼和登陸,因此必定要發送一封電子郵件到過去的電子郵箱,在恢復過程添加一個選項。Facebook就是這樣作的。這招還適用於敏感數據更新。不管是誰在操做,但帳戶全部者必須被通知。


禁用端口80而不是重定向到443:這樣作以後會增大攻擊面。若是80端口不須要了,那就禁用它。記住,你的API只應該在443中監聽。若是你想從80重定向到443,在<插入CDN名>這個選項處操做。


老是使用通用類的錯誤信息:記住要始終使用通用的錯誤信息,例如,在登陸嘗試時,不要說「用戶名無效或密碼無效」,只說「證書無效」,讓暴力破解更難,雖然能夠在註冊時枚舉電子郵箱,由於你的系統可能會(也應該)讓每一個賬戶的電子郵箱是惟一的。若是你的應用程序產生一個異常,只是說「出錯了」,不要暴露異常堆棧。我也建議你使用一些方法來收集全部的異常,併發送到你的郵箱或展示在Raygun, Sentry, Airbrake的dashboard上。


確認用戶的電子郵箱或電話:在發送電子郵件或者通知以前要先確認這個郵箱或者電話是否屬於該用戶。值得推薦的作法是非阻塞法,即讓用戶能夠在沒有確認的狀況下登陸,但這也會影響線上用戶的使用。看看Facebook:你可使用未經證明的帳戶1天。以後,你必須在登陸以前確認郵件或電話。我常思考10分鐘後郵件失效這樣的服務,像上文提到的,好處並非發送郵件給並不須要它們的用戶,而是讓你免於被用戶標示爲垃圾郵件。


其餘方面(不排斥其餘安全措施)


不要由於供應商有很酷的功能或超低價格就選擇他們:你的數據是危險的,那麼你的名聲也是危險的。有一個叫作「不肯信任」原則,這意味着信任以前你須要當心。減小你的信任也是一件好事。你越相信,就越危險。也就是說,我一般建議安全第一。一開始Bitbucket彷佛比GitHub更便宜,但它沒有兩因素身份認證。你的源代碼值多少錢呢?AWS引起了公有云市場的競爭;當他們開始關注敏感信息的安全性時,他們彷佛作了一件偉大的工做。因此只是在價格便宜的狀況下還不足以讓我換一個服務商。全部的事情都要被考慮到,但要知道,靜態頁面接受任何東西,常常會看到企業主頁上宣稱它們經過APT和SSL(不推薦使用)實現了網站安全。儘可能不要輕易相信,當你信任時,先驗證!


(REST)面向API的開發:若是你細看AWS,你會發現API是第一位的,而後是web UI,最後是SDKs。API是可怕的,由於它是獨立於語言的。但我我的認爲,這是未來發展的必然趨勢。還值得仔細觀察的是HATEOAS。它使各部分之間的可視化隔離變得容易。客戶端是靜態頁面,服務器是接收輸入和爲前端產生輸出的大腦。它能更明確地分離角色和記錄,例如web服務器必須驗證輸入。不然non API的web應用程序更會混亂。


委託辦理信用卡:將風險委託給信任的實體是一個好建議。若是你本身去作這件事,就要從一開始就儲存信用卡數據,再想想,這樣你要擔負多大的責任。若是你委託給可信的支付提供商,如Stripe或PayPal,會不會更好?我以爲會,除非你能作的更好。因此,要確保你的應用不要碰觸信用卡數據。能夠重定向到他們的網站來完成整個過程。


如今去哪?


有太多的信息了,去搜索吧。OWASP和SANS會給你很大幫助。他們有不少項目、物品、清單和工具。我還建議關注你的工具和供應商方面的安全建議。除此以外,常去Reddit的/r/netsec逛逛。

原文地址:http://www.jointforce.com/jfperiodical/article/933?f=jf_tg_bky

相關文章
相關標籤/搜索