又LAG隨性筆記
  • 關於我
  • 作品集
  • 生活隨筆
  • 與我聯絡
  • 隨手扎

隨手扎

October 13, 2022

你可能不知道的Web API--postMessage

前言

postMessage()是少數可以讓兩個不同頁面交換訊息的方式。如其名,傳遞訊息,postMessage()接收一段文字訊息,將這個文字訊息傳遞給通知的對象。通知的對象可以監聽message事件獲取訊息。

關於postMessage()

實際上現在瀏覽器網頁API上,存在的postMessage()API不只一個。有window.postMessage()、Worker.postMessage()、BroadcastChannel.postMessage()和Client.postMessage(),它們有著類似的使用方式。除了不同頁面溝通外,對於建立的Web Worker執行緒也有相似方式傳遞訊息,除Worker.postMessage()外,在Web Worker環境下還可以建立BroadcastChannel物件,使用BroadcastChannel.postMessage()方法。至於Client.postMessage()是在Service Worker使用的,有在寫PWA(Progressive Web Application,漸進式網頁應用程式)才比較會用到。

本節主要討論的是最基本的window.postMessage()。

基本用法

基本上的用法可以傳遞一個字串作為訊息發送出去,像是window.postMessage("msg")。然後監聽message事件。所以我們可以做一個簡單的例子。

現在建立兩個頁面index.html作為主畫面,sub.html作為用iframe嵌入在主畫面的子畫面。

主畫面內容如下,除了一個發送訊息的文字話框外,還有一個接收訊息的文字畫框,以及一個發送訊息的按鈕。

<!------ index.html ------>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <title>[DEMO] postMessage (Main)</title>
  </head>
  <body>
    <form>
      <textarea cols="30" id="msg" name="" rows="10"></textarea>
      <br/>
      <textarea cols="30" id="rev" name="" rows="10" disabled></textarea>
      <br/>
      <button>send</button>
    </form>

    <hr/>

    <iframe frameborder="0" src="sub.html"></iframe>

    <script type="text/javascript" src="main.js"></script>
  </body>
</html>

相對來說子畫面就簡單一些,只有一個接受訊息的地方。它會將接受到的訊息原封不動的回傳回去。

October 12, 2022

你可能不知道的CSS Injection

前言

當你只是簡單地設置了一個CSP以後:

Content-Security-Policy: defalut-src 'self';

會發現誒~為什麼inline-script也不工作了?

<script type="text/javascript">console.log('Hello, World');</script>

要解決這個問題,同樣必須計算腳本雜湊值,然後設置新的內容安全政策:

Content-Security-Policy: defalut-src 'self';script-src 'sha256-2cq9aRSFdLqAC0FNx8cqcUjxA2Bmk5ZjlSvbIPQ1x/U=';

當然不止這種做法,還可以設置nonce等方式。

October 11, 2022

你可能不知道的內容安全策略(Content-Security-Policy, CSP)

前言

當我們知道了XSS,瞭解到對於外部資源的引用檢查是何等重要。那麼除了開發上需要注意意外,還可以怎麼做?

服務器設置CSP相關回應頭

CSP,全名Content Security Policy,也就是內容安全政策。這是為了告訴瀏覽器,這個頁面允許什麼行為,讓瀏覽器幫忙在檢查一次。與設個相關的主要有兩個Response Headers:Content-Security-Policy和Content-Security-Policy-Report-Only。

Content-Security-Policy可以設定一些政策,當瀏覽器發現也面內容行為不符合這些政策的時候,就會被阻擋下來。

如果在一開始調整,有大量不確定受到影響的頁面,並不希望行為直接被阻擋下來,可以改先使用Content-Security-Policy-Report-Only。當發現不符合政策的頁面內容時,先發送到後端的一個端點記錄下來。再所有被發現存在問題的頁面都已經修正後,再改成設置為Content-Security-Policy。

繼續以前一個例子來用:A網站 http://a.127.0.0.1.nip.io:8000/index 有以下內容:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <title>CSP</title>
    <link rel="stylesheet" href="http://b.127.0.0.1.nip.io:8000/xss.css" type="text/css" media="screen" />
  </head>
  <body>
    <h1>Hello, World</h1>
  </body>
  <script type="text/javascript" src="http://b.127.0.0.1.nip.io:8000/xss.js"></script>
</html>

這次也真夠糟糕的,引用到兩份B網站 http://b.127.0.0.1.nip.io:8000 存在問題的內容–/xss.css和/xss.css

在部署的時候可以在伺服器回應的HTTP Response加入Header:

Content-Security-Policy: defalut-src 'self';

這次這兩個不安全的內容就會被瀏覽器阻擋下來。

如果確定外部資源內容是需要的話該怎麼辦?

October 10, 2022

你可能不知道的跨站腳本攻擊(Cross-Site Scripting,XSS)

前言

跨站腳本攻擊,英文Cross-Site Scripting,縮寫原本應該是CSS,但與階層樣式表–Cascading Style Sheets的縮寫相同,所以通常已X當做「交叉」的Cross,就變成是XSS。

在今天算是一個很嚴重的漏洞攻擊,因為有可能做到身份偽造,然後去進行資料竊取或破壞。但防禦跨站腳本攻擊,不單單只是前端開發工程師的責任,很大程度上也與服務如何部署有關係。

什麼是跨站腳本攻擊

跨站腳本攻擊,是在頁面上存在從其他來源引入的腳本,而這些腳本帶有惡意行為。要注意的是,腳本並不只是指JavaScript,HTML、CSS或其他資料內容也有可能是惡意注入的對象。

The malicious content sent to the web browser often takes the form of a segment of JavaScript, but may also include HTML, Flash, or any other type of code that the browser may execute.

惡意的JavaScript

比如說在A網站 http://a.127.0.0.1.nip.io:8000/index.html 有以下內容:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <title>XSS</title>
  </head>
  <body>
    <h1>Hello, World</h1>
  </body>
  <script type="text/javascript" src="http://b.127.0.0.1.nip.io:8000/xss.js"></script>
</html>

在這個頁面中,使用了另一個網站B http://b.127.0.0.1.nip.io:8000 的JavaScript。但是其實xss.js的檔案並不是自己可以掌控的,它的內容可能是:

October 9, 2022

你可能不知道的HTTP Header--If-Match和該怎麼設計Web API

前言

問個 假如我今天有個表單在前端要送,然後我想要先檢查這個表單有沒有被其他人更新

這是在一次和朋友的討論中有人提出來的問題。

恩…這是很典型資料競爭的問題,但並不是多個執行緒存取一份資料,而是多個瀏覽器客戶端(Browser Client)存取同份伺服器資源。

通常,我們在討論RESTful - 一種當下很常見的Web API設計方式的時候,只會提到建立(Create / POST)、讀取(Read / GET)、更新(Update / PUT)、補充(PATCH)和刪除(DELETE),也就是一般的CRUD。甚少會在深度討論一些細節,就如開頭提到的問題。並且通常在瀏覽網站,也很少去注意各個網站的API設計方式。

那麼這個問題要怎麼解決呢?

不管怎麼說,伺服器上的資料正確性最終需要由伺服器處理保護。 可以簡單一些在資料上添加版本(Version)或最後修改時間(Last Update time)的欄位。當收到更新或刪除訊息,該欄位應該保持一致。不過這就會出現一個很奇妙的現象:

> GET /data1


< 200 OK
< Content-Type: application/json
< 
< {"name": "Bob", "age": 18, "version": 1}

當我們有一個資料data1其版本為1,如果我們需要更新的話,下面的訊息代表什麼意思?

> PUT /data1
> Content-Type: application/json
> 
> {"name": "Alice", "age": 18, "version": 1}


< 204 No Content

這是一個更新,版本是不是應該跳到2?但是按照前述這個版本應該要與記錄一致才會被修改,但是這樣似乎又與PUT的意圖衝突?

或許有一些設計是檢查更新版本-1是不是與當前記錄版本相同作為檢查判斷條件。但是想想,其實版本號的決定,似乎不該由Client傳進的資料決定,而應該由後端資料管理行為決定。

那麼來介紹另一種設計方式,其實HTTP一般性規範都幫你準備好了,只是在我經驗觀察上,似乎並沒有那麼易用(也可能是因為了解的人真的不多),並不經常看到這樣的設計。這可能會包含一些你可能不知道的HTTP狀態碼(HTTP Status Code)和HTTP Headers。

October 8, 2022

你可能不知道的JS自動型別轉換

前言

如果要針對一個 物件陣列 取最大值該怎麼做?

一天,一位同事這麼問到。

首先需要先知道的是一般在JavaScript物件是無法比較大小的,所以這句話的意思是將物件特定屬性作為比較參考值,或是將物件數個屬性計算成一個可以比較的值後作為參考值,再進行比較尋找最大值。

起初,針對這個問題我第一想到的就是三種方式:

  1. 使用Array.prototype.reduce()
  2. 使用Array.prototype.sort()
  3. 使用Math.max()

最後一個你可能會很好奇:物件無法比較,並且Math.max()又不像Python的max()可以輸入key函式,這樣可以比較嗎?

沒錯,這個方式是存在問題的,但原因並不是因為「物件無法比較」,而是結果型別的問題,這個問題之後會提到。並且這也引發了今天的議題–「JS自動型別轉換」。

物件陣列怎麼取最大值?

這並不是今天主題的重點,也就不賣關子了,直接給出做法。

同樣拿Person類別為例子:

class Person {
    name = "";
    #birthday = new Date(); // 私有屬性
    get birthday() { return this.#birthday }; // 調整處
    addr = ""
    
    get age() {
        return new Date().getYear() - this.birthday.getYear();
    }
    
    constructor(name, birthday, addr = "") {
        this.name = name;
        this.#birthday = birthday; // 調整處
        this.addr = addr;
    }
    
    
    hello() {
        console.log(`Hello, ${this.name}.`);
    }
}

現在有10個年幼不依的人:

var people = [
    "Alice",
    "Bob",
    "Candy",
    "Danel",
    "Frank",
    "Grant",
    "Harry",
    "Iris",
    "Joe",
    "Kevin"];

people = people.map(name => new Person(name, new Date(Math.floor(Math.random() * 100) + 1990, 1), ""))

現在如果希望找到年紀最大的,這裡提供幾種方式:

  • ««
  • «
  • 1
  • 2
  • 3
  •  … 
  • 33
  • »
  • »»
© 又LAG隨性筆記 2023