iFrame 是否能任意地嵌入任何網頁

  • 18311
  • 0
  • Http
  • 2018-06-30

最近在整合其他系統的時候,需要把外部合作廠商的網頁給嵌入,卻遇到了無法嵌入的問題,問了較熟 HTML 的同事時,他問了我一句,iFrame 應該不能任意嵌入其他網頁吧?這樣叫Yahoo, Facebook 他們情何以堪,馬上被當成別人的子網頁...

有鑑於對 iFrame的不熟,所以決定以下做幾個實驗來研究一下

首先我們鎖定Yahoo 跟 Google 做個實驗,先準備一個簡單的 HTML 檔案並試著嵌入兩個外部網頁

<html>
	<head>
	</head>
	<body>
			<button onClick="testIframe();">load iframe</button>
			this is yahoo frame
            <iframe id="yahoo-frame" name="yahoo-frame" scrolling="auto" frameborder="0"></iframe>
			
			this is google frame
			<iframe id="google-frame" name="google-frame" scrolling="auto" frameborder="0"></iframe>
            
			<script  type="text/javascript">
        
		function testIframe() {
             document.getElementById("yahoo-frame").src = 'http://www.yahoo.com';
			 document.getElementById("google-frame").src = 'http://www.google.com.tw';
			
            }       
        </script>
	</body>
</html>

 

結果 Yahoo 可以嵌入,但 Google 卻不能

先說結論,每個網頁都應當有權利決定要不要被別人嵌入,或是被特定的 domain 嵌入

而這個結論就是Google 不希望被非 www.google.com 的 domain 的系統所嵌入

而這一切都是 response header 的 X-Frame-Options 所造成的,在最新的網頁規格是使用了 Content Security Policy


甚至在 chrome 的 console 介面會看到這個錯誤
Refused to display 'https://www.google.com.tw/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

若是設定 Content Security Policy 的話,可以設定至某些允許的 domain 是可以嵌入的

若response header 裡所指定的 特別 domain, 則會出現
Refused to display 'http://domain/xxx.aspx' in a frame because an ancestor violates the following Content Security Policy directive: "frame-ancestors *.someDomain.com

 

若我把這個 HTML (用 iFrame 包了 Yahoo 與 Google 的 網頁放至某台 Server 的 IIS 站台,並設定  X-Frame-Options 即可

如此一來該 IIS 站台的回應都會在 response header 裡加上 X-Frame-Options

若 A 站台 嵌入了 Yahoo, Google ,且這兩個站台是同時授權能被 A 站台的 domain 嵌入,那麼 A 站台就能正常呈現 iFrame裡的網頁資料

但是 A 站台若沒有在 response header 設定是否能給其他人嵌入的話

B 站台若嵌入了 A站台的網頁,那麼 B 是否也能正常顯示 Yahoo, Google 的網頁,造成了安全性的連鎖效應呢!?

接著來做個簡單的驗證

A': 角色為 facebook, google,在 response header 設定了只要是 *.contoso.com 的 domain 都能嵌入

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
				<add name="Content-Security-Policy" value="frame-ancestors *.contoso.com" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</configuration>

並置入 display 網頁 

<html>
	<head>
		
	</head>
	<body>
		this is A' site, only authorized to *.test.contoso.com
        </script>
	</body>
</html>

A: 角色為中繼網頁,用來呈現嵌入 A' ,domain 為 A.contoso.com

iFrame.html


<html>
	<head>		
		
	</head>
	<body>			
			this is a frame contain A' frame 
            <iframe id="main-frame" name="main-frame" style="height:600px;width:100%;border:none" scrolling="auto" frameborder="0" src="http://172.17.0.35/display.html" ></iframe>
			
			<script  type="text/javascript">
        
		function testIframe() {
             document.getElementById("main-frame").src = "http://A'/display.html";			
            }       
        </script>
	</body>
</html>

從 A 嵌入 A' 的網頁則不會出現問題,因為 Content-Security-Policy 有設定了 *.contoso.com

B: 角色為最終端嵌入者,將 A 的網頁嵌入

若要從 B 直接載入 A' 的網頁,則沒問題,但如果要嵌入 iframe 則會出現問題

果然就出現了Refused to display 'http://A'.Server/display.html' in a frame because an ancestor violates the following Content Security Policy directive: "frame-ancestors *.contoso.com".

但如果直接跳過 A',直接從沒有設定安全性的 A 來嵌入網頁的話呢?

這種偷吃步看起來難不倒 Content-Security-Policy 的設定

 

參考資料

http://blog.darkthread.net/post-2016-06-12-iframe-clickjacking.aspx