對於WEB應用安全,我too young了
文章概要
開發 WEB 應用的同學應該不少,不知道大家平時開發中,有沒有關注過應用開發的「安全問題」。
所謂安全問題,就是開發中不要留下漏洞,給入侵者破壞者機會。
比如,我們常掛在嘴邊的安全問題有 「SQL 注入」,為了防止出現問題,一般都會使用預編譯的 SQL,而不是拼接SQL,以此來保證安全。再比如,我們一個允許使用者輸入的文字框中,會禁用程式碼的渲染,比如像
這種會被做為純文字對待。類似需要注意的問題有很多,如果我們不留意,就會容易造成安全問題。
不過有些「洞」,是你我開發中不曾留意的。或者說你只有先意識到有這一類問題需要注意,開發的時候才會把它堵上。
比如最近開發的一個WEB 應用,公司做安全的同事幫忙看了看,就給報了幾個問題。
這裡簡單總結和各位一起分享下,歡迎留言討論。
同事給指出的問題主要有兩個:
不安全的 TRACE、OPTIONS 等方法未禁用
使用者提交的資訊裡,可以包含第三方連結,容易出現釣魚問題。
問題一:
通過TRACE、OPTIONS 方法,可以暴露不少關鍵資訊。
我們知道,HTTP 請求裡可以使用不同的 Method 來實現不同的請求功能, 比如常見的 GET/POST/PUT/DELETE 。除此之外還有一些,比如 TRACE/OPTIONS 等,每種請求簡單描述如下。詳細描述,可以檢視RFC2616(https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
GET 請求指定的頁面資訊,並返回實體主體。
HEAD 類似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
POST 向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案)。資料被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。
PUT 從客戶端向伺服器傳送的資料取代指定的文件的內容。
DELETE 請求伺服器刪除指定的頁面。
CONNECT HTTP/1.1協議中預留給能夠將連線改為管道方式的代理伺服器。
OPTIONS 允許客戶端檢視伺服器的可用內容。
TRACE 回顯伺服器收到的請求,主要用於測試或診斷
從上面內容可以看出,TRACE 方法,OPTIONS方法,一般都會返回大量和實際業務處理無關的內容,如果開放會暴露許多伺服器的關鍵資訊,可能引出安全問題。
處理方式:
對於傳統的web專案, 直接在web.xml 裡增加安全限制「security-constraint」即可。
對於 Spring Boot應用,如果是使用 Tomcat做為容器,可以通過宣告獨立的配置,實現類似web.xml的效果:
web.xml的配置
<security-constraint>
<web-resource-collection>
<web-resource-name>restricted methods</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
Spring Boot 應用中的配置
@Configuration
public class TomcatCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.setSessionTimeout(8, TimeUnit.HOURS);
tomcat.addContextCustomizers(new ContextSecurityCustomizer());
}
private static class ContextSecurityCustomizer implements TomcatContextCustomizer {
@Override
public void customize(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.setName("restricted_methods");
securityCollection.addPattern("/*");
securityCollection.addOmittedMethod(HttpMethod.POST.toString());
securityCollection.addOmittedMethod(HttpMethod.GET.toString());
constraint.addCollection(securityCollection);
constraint.setAuthConstraint(true);
context.addConstraint(constraint);
}
}
}
如果使用 Jetty,配置方式也類似。
當然,如果想要做到不與容器依賴緊耦合,還可以自己定義Filter,然後在請求中判斷請求頭裡的 Method 如果是不允許的方法,可以直接返回錯誤。
問題二:
具體是這樣,我們的應用裡允許使用者上傳圖片。上傳的圖片會儲存到物件儲存中,然後返回一個URL,最後使用者完整的資訊裡,是直接儲存了一個URL。但是,我們看到,整個上傳到提交的過程,並不是一步完成的,所以,使用者可以自行構造這個提交的請求,並將圖片的URL,替換成任意連結。
這個時候,在頁面再次渲染時,是會請求圖片URL來展示圖片的。如果是一個惡意的第三方釣魚連結,容易造成盜取使用者資訊等安全問題。
處理方式:
這個問題,一般在後端對提交的內容進行檢驗,如果有「不符合規定」的域名,就報錯。一般是維護一個白名單,判斷URL資訊在不在白名單中。
判斷一個URL的字尾,你一般用什麼方式呢?正則表示式?字串擷取?
這裡有個方式供參考,可以直接取出我們預期的域名字尾:
String host = "";
URL fullUrl = null;
try {
fullUrl = new URL("http://blog.tomcat8080.com/abc=def");
} catch (MalformedURLException e) {
e.printStackTrace();
}
host = fullUrl.getHost();
InternetDomainName domainName = InternetDomainName.from(host);
String top = domainName.topPrivateDomain().toString();
這些安全技術,彷彿是和WEB開發平行的一個領域,屬於另一個工種。這些不屬於架構,與設計模式無關,也不在資料庫設計、虛擬機器優化的範圍內,但是,也是我們不得不去認真瞭解和重視的。可能不需要安全工程師那麼深入,但一些常見的安全問題要了解,類似於一個 「CheatSheet」避免自己開發的時候暴露,保證我們開發的應用安全,不被攻擊和洩露資料。對於WEB安全,我還是 too young了,歡迎各位留言述說自己的經驗。