# Week 8 回顧 ## 主題⼀:使⽤ Grid 完成排版 1. 說明 Grid 的基本設定觀念。 - 一組水平線(row)和垂直線(column)的交叉集合(intersecting set)。 - 相較於一維的 flex,grid 是二維的網格系統,可以指定內元件排版在網格的任何地方。 - 可以使用 px 指定絕對大小、 % 設定百分比或 fr 代表剩餘空間欲分配的比例。 2. 利⽤ Grid 完成第⼀週作業,下⽅主內容 ( 景點列表 ) 的前端排版。 ```html= <section> <div class="grid-box"> <div class="box-bottom"> <img src="./imgs/1.jpg" alt=""> <i class="fa fa-star" aria-hidden="true"></i> <figcaption>Title</figcaption> </div> </div> </section> ``` ```css= section{ display: flex; /*flex與grid不衝突*/ justify-content:center; } .grid-box{ display: grid; grid-template-columns: repeat(4, 1fr); /*網格區塊的寬度大小*/ grid-column-gap: 20px; /*網格區塊的左右間距*/ grid-row-gap: 20px; /*網格區塊的上下間距*/ } .box-bottom{ display: inline-grid; /*inline讓元素可以拼在一起*/ } .box-bottom img{ grid-area: 1 / 1 / 2 / 2; /*row-start/column-start/row-end/column-end*/ } ``` ## 主題⼆:完善資料驗證程序 1. 前端在使⽤者註冊、登入時,要先驗證資料是否忘記輸入,格式是否正確,並在錯誤時,直接提醒使⽤者。沒有任何錯誤,才發送請求到後端。 >https://www.javascripttutorial.net/javascript-dom/javascript-form/ ```html= <form action="/signup" method="POST" id="signup"> <div> <label for="password">密碼</label> <input type="password" id="password" name="password" onfocusout="addTip('password','密碼')" // 離開 input 時偵測,若為空白則出現提示 onkeyup="removeTip('password')" // 輸入文字進 input 時將提示移除 pattern="^[^ ].+[^ ]$"> /* 正則表達式 第一個 ^ 代表「字串的起始」; [^ ] 代表「除了空白以外的所有字符」; .+ 代表「最少一個字符」; 最後的 $ 代表「字串的結束」。 */ </div> <div id="error-password" class="errorText"></div> </form> ``` ```javascript= let signUpForm = document.getElementById("signup"); signUpForm.addEventListener("submit", function (event) { //暫停傳送表單 event.preventDefault(); //確認每個欄位是否有效 let nameValid = hasValue(signUpForm, 'name', '姓名'); let usernameValid = hasValue(signUpForm, 'username', '帳號'); let passwordValid = hasValue(signUpForm, 'password', '密碼'); //都有效時傳送表單 if (nameValid && usernameValid && passwordValid) { signUpForm.submit() } }); function hasValue(form, inputId, text) { if (form.elements[inputId].value.trim() === "") { return addTip(inputId, text); } return true; } function addTip(inputId,text){ let inputBox = document.getElementById(inputId); if(inputBox.value == ''){ inputBox.className = "error"; let errorDiv = document.getElementById("error-"+inputId); errorDiv.textContent = "請輸入"+text; } } function removeTip(inputId){ let inputBox = document.getElementById(inputId); if(inputBox.value != ''){ inputBox.classList.remove("error"); let errorDiv = document.getElementById("error-"+inputId); errorDiv.textContent = ""; } } ``` ```css= .error{ border: 2px solid red; } ``` 2. 後端接收到前端的請求時,要驗證資料是否存在,且資料格式是否符合預期,⼀切都正確才能執⾏後續的資料庫操作。否則就直接回應錯誤訊息給前端。 ```python= name = req.form["name"] username = req.form["username"] password = req.form["password"] sql = "SELECT * FROM member WHERE username = %s" val = (username) mycursor.execute(sql,(val,)) myresult = mycursor.fetchone() if not myresult == None: return redirect("/error?message=帳號已經被註冊") pass ``` 3. 請在你的註冊、登入會員的功能中,建立以上資料驗證程序並展⽰出來。 >https://github.com/Pinkowo/WeHelp/tree/main/wk8 4. 在我們的系統中,前端驗證和後端驗證的主要⽤途是什麼? - 前端驗證:向**使用者**確認每個欄位有被輸入以及格式皆正確。 - 後端驗證:向**前端**確認資料格式是否正確,以及向**資料庫**確認是否有該筆資料。 ## 主題三:AJAX 與 CORS 1. 什麼是 CORS? - 跨來源資源共用 ( Cross-Origin Resource Sharing )。 - 使用額外 HTTP 標頭令目前瀏覽網站的使用者代理 (en-US)取得存取其他來源(網域)伺服器特定資源權限的機制。 - 基於安全性考量,程式碼所發出的跨來源 HTTP 請求會受到限制。例如:XMLHttpRequest 及 Fetch 都遵守同源政策(same-origin policy,相同的通訊協定/網域/通訊埠)。這代表網路應用程式所使用的 API 除非使用 CORS 標頭,否則只能請求與應用程式相同網域的 HTTP 資源。 2. 我們可以在⾃⼰的網⾴中,使⽤ fetch() 或是 XMLHttpRequest 連結到 https://www.google.com/ 並取得回應嗎? ![](https://i.imgur.com/MCaaqeJ.png) 3. 我們可以在⾃⼰的網⾴中,使⽤ fetch() 或是 XMLHttpRequest 連結到 https://padax.github.io/taipei-day-trip-resources/taipei-attractions-assignment.json 並取得回應嗎?和上述的狀況,差別在哪裡? ![](https://i.imgur.com/JSxnXir.png) 4. 如何開放我們⾃⼰開發的 API,讓別的網站透過 fecth() 或是 XMLHttpRequest 連結,達到如同第 3 點的可能性。 - 授權的方法是在 response 裡加上 Access-Control-Allow-Origin header: >Access-Control-Allow-Origin: https://shubo.io - 如果 server 允許任何來源的跨來源請求,那可以直接回 *: >Access-Control-Allow-Origin: * ## 主題四:使⽤主鍵、索引優化資料庫查詢效率 1. 了解主鍵 (Primary Key) 和索引 (Index) 的觀念。 - 主鍵 - 具有唯一性且不可為 null。 - 可避免產生一堆重複又無法識別的 row。 - 方便撈到指定的 row。 - 外鍵參照(foreign key reference)。 - 索引: - 索引越多,需要的記憶體與維護索引的 cpu 運算也越多。 - 2. 請在 member 資料表中加入適當的索引,加快以下 SQL 語句的查詢效率:SELECT * FROM member WHERE username=’test’ and password=’test’ 3. 如何驗證查詢效率是否真的變更好了? 4. 為什麼索引的設置能有效地改善查詢效率? ## 主題五:使⽤ Connection Pool 連結資料庫 1. 什麼是 Connection Pool?能帶給我們什麼好處?為什麼? 1. mySQL的伺服器擴充套件。 2. 減少開關、統一出口、設定最大連線數、驗證 connection 是否正常。 3. 不使用 connection pool 的話,每當要查詢、新增、刪除等動作,都必須**重複打開資料庫後關閉**,每次連接都很耗時跟耗資源,而 **connection pool 可以保持數據庫持續連結**,以便有請求時就可以馬上使用,不必重新創建連結。 2. 如何使⽤官⽅提供的 mysql-connector-python 套件,建立 Connection Pool。 3. 需要從資料庫取得查詢資料時,如何從 Connection Pool 取得 Connection,並且在資料操作結束後,歸還 Connection 到 Connetion Pool 中。請展⽰你完成上述標準操作的程式碼。 > SHOW PROCESSLIST ## 主題六:了解並預防 Cross-Site Scripting (XSS) 攻擊 1. XSS 攻擊的基本介紹。 - 當網站讀取時,執行攻擊者提供的程式碼。 - XSS 攻擊分為兩種 1. Reflected Attack:使用者點擊連結。 2. Persistent Attack:單純瀏覽網頁,但該網頁中已植入惡意的語法。 2. 實際設計⼀個 XSS 攻擊情境。 ![](https://i.imgur.com/2e9sdei.png) ```html= <input type="text" id="input"> <button id="button">click me</button> <div id="div"></div> ``` ```javascript= let input = document.getElementById("input"); let button = document.getElementById("button"); let div = document.getElementById("div"); button.addEventListener('click',()=>{ div.innerHTML = input.value; }) ``` 3. 根據上述設計的情境,說明開發者該如何預防。 ![](https://i.imgur.com/XFLAgKL.png) ```javascript= button.addEventListener('click',()=>{ div.innerHTML = encodeURIComponent(input.value); }) ``` - 有可能被埋炸彈的地方,需各個擊破 1. HTML Content 2. HTML Attributes 3. URL 4. javaScript 5. CSS 6. 其他:Regex 、 JSON >![](https://i.imgur.com/Tz56j9A.png) >>[如何防禦 XSS 攻擊](https://medium.com/hannah-lin/xss-2-%E5%A6%82%E4%BD%95%E9%98%B2%E7%A6%A6-xss-%E6%94%BB%E6%93%8A-18fdf10ef5ef) #### 1. HTML Content 先透過編碼後再進行輸出。 在 Javascript 中使用 **encodeURI()** 和 **encodeURIComponent()**(escape() 已經沒在使用了)。 ```javascript= var set1 = ";,/?:@&=+$"; // 保留字符 var set2 = "-_.!~*'()"; // 不转义字符 var set3 = "#"; // 数字标志 var set4 = "ABC abc 123"; // 字母数字字符和空格 console.log(encodeURI(set1)); // ;,/?:@&=+$ console.log(encodeURI(set2)); // -_.!~*'() console.log(encodeURI(set3)); // # console.log(encodeURI(set4)); // ABC%20abc%20123 (空格被编码为 %20) console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24 console.log(encodeURIComponent(set2)); // -_.!~*'() console.log(encodeURIComponent(set3)); // %23 console.log(encodeURIComponent(set4)); // ABC%20abc%20123 (空格被编码为 %20) ``` #### 2. HTML Attributes 同上。 #### 3. URL 只推薦用 **encodeURIComponent()** ,因其包含 & ? + = 等字符的轉碼。 #### 4. javaScript 在特殊字符(" ' \` $ / \)的前面加上 \ 將其變為一般字元。 ```javascript= function addslashes( str ) { return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0'); } ``` #### 5. CSS 同上,通常 style/script 已經分離,因此相對安全,只需針對 \ 及 / 做重新編碼。 ### CSP 當今天已被安裝炸彈,設定 CSP (白名單) 可以防止炸彈爆炸。建議於 sever 端添加。