Lidemy / mentor-program-3rd

程式導師實驗計畫第三期

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

第十五週網站前後端開發基礎測試參考解答

aszx87410 opened this issue · comments

commented

這個 Issue 主要是第十五週會出現的網站前後端開發基礎測試的參考解答。

原本在 Google 表單提交之後就會有參考解答可以看,但一來修改內容不太方便,二來有疑問也沒辦法馬上討論,所以最後就決定在 GitHub 也開一個 Issue 來講一下解答。

如果有任何問題歡迎在下面留言,解完有收穫或是有寫成心得筆記也歡迎分享在下面。

底下解答都是點開才會顯示的,不用怕被雷到。

Q1

有天小明來找你,跟你說:

欸欸,主管剛出了一個作業,跟我說要我寫一個 Todo list 的後端 API,要回傳代辦事項的列表,還要能新增、編輯以及刪除 todo。

但是有一個限制,主管要我只能用 GET 這個 HTTP Method 來實作!開玩笑,這怎麼可能嘛,新增要用到 POST,刪除要用到 DELETE,只用 GET 怎麼想都是不可能的,你說呢?

請問小明說得正確嗎?
如果你只能用 GET 寫一個 Todo list 的後端,你做得到嗎?如果做不到,原因為何?

點我看 Q1 解答

答案:做得到。

這一題想考的是「慣例」跟「限制」的差別。

之所以刪除動作會用 DELETE,是因為這是一種好習慣,而不是因為「必須要」這樣做。所以儘管你只有 GET 這個 method,你依然可以寫出新增、刪除以及編輯的功能,只是網址就會不符合 RESTful 的規則,大概會變成這樣:

  1. add_todo?id=xx
  2. edit_todo?id=xx&content=xx
  3. delete_todo?id=xx

總而言之,HTTP Method 的使用本來就不是強制的規範,這題希望讓大家理解的是這件事,不過能夠符合 method 的語義當然是最好的。

Q2

承上,如果變成只能用 POST 這個 method,做得到嗎?如果做不到,原因為何?

點我看 Q2 解答

第一題講解過了,做得到。

Q3

公司內部有一個 API 是拿來刪除文章的,只要把文章 id 用 POST 帶過去即可刪除。

舉例來說:POST https://lidemy.com/deletePost 並帶上 id=13,就會刪除 id 是 13 的文章。

公司前後端的網域是不同的,而且後端並沒有加上 CORS 的 header,因此小明認為前端用 ajax 會受到同源政策的限制,request 根本發不出去,所以前端沒辦法利用 ajax 呼叫這個 API 刪除文章。

請問小明的說法是正確的嗎?如果錯誤,請指出錯誤的地方。

點我看 Q3 解答

答案:錯誤。

這題考的是你對同源政策的理解程度。

首先,同源政策限制的對象有兩種,簡單請求跟非簡單請求,前者像是 GET 跟 POST(還必須沒有加上任何 custom header),後者像是 DELETE 之類的。這邊可以自己去找 MDN 的參考資料來看。

簡單請求跟非簡單請求的差別在於:

  1. 簡單請求限制的是「拿到 response」而不是「發出 request」,這是超級無敵重要的一點,但我想很多人都會搞混

  2. 非簡單請求會先發出一個 OPTIONS 的 request 去查看後端是否允許非同源的 request,如果不允許,則不會把請求發出去。舉例來說,使用 DELETE 方法就會先發出 OPTIONS 的 request,確認後端有帶 CORS 的 header 才會把 DELETE 的 request 發出去。

所以,以這題的狀況來說,儘管後端沒有加上 CORS 的 header,刪除文章的 request 依舊發的出去,只是前端的 JS 拿不到 response,然後 console 會出現錯誤。所以文章還是被刪掉了,只是前端不知道到底有沒有成功。

Q4

有天小明表示他對 XSS 的防禦方法不太理解,他說:

不是啊,幹嘛要 escape 字串,既然 XSS 的問題是因為可以執行 script,那我就直接把 <script> 標籤取代掉就好了,例如說:str_replace(“<script>”, “”, html),這樣就不能執行 JS 了,不就沒問題了嗎?

請問小明的說法是正確的嗎?如果錯誤,請指出錯誤的地方。

點我看 Q4 解答

答案:錯誤

這題考的是你對 XSS 的認知是否太薄弱。

的確,照小明的說法 <script> 會被封掉,但是如果攻擊者把標籤改成 <SCRIPT> 就可以繞過這個字串取代了。

而且就算改成無論大小寫都會被取代掉,我依然可以不用 script 標籤執行 JS,例如說 <img onerror="alert(1)" /> 也可以成功執行 XSS。

Q5

小明在學習到 SQL Injection 之後非常興奮,他說 SQL Injection 就是靠攻擊者傳入惡意字串與原本的 query 拼在一起導致攻擊成功,只要用 prepared statement,交給資料庫來處理這些惡意字串就好。

以 PHP 為例,就是像這樣:

$stmt = $mysqli->prepare(“SELECT * from Todos where id = ?”)
$stmt->bind_param("i", $id)
$stmt->execute()

這樣子就能防止 SQL Injection 了!

請問小明的說法是否正確?如果錯誤,請指出錯誤的地方。

點我看 Q5 解答

基本上是正確的,除非哪天 prepared statement 被發現漏洞。

Q6

在公司內部有一個留言板的系統,必須註冊才能留言,而且只有自己能夠刪除自己的文章。在前端的實作上為了確保不能刪除到別人的文章,只會在自己的文章旁邊顯示刪除按鈕。

而後端則是一個簡單的 API:DELETE /posts/:id。程式碼大概是長這樣的,就只是依據傳進來的 id 把文章給刪除掉:deletePost($id)

小明覺得這樣沒什麼問題,既然前端不會顯示刪除文章的按鈕,就不可能刪除到別人的文章。

請問小明的說法是正確的嗎?如果錯誤,請指出錯誤的地方。

點我看 Q6 解答

答案:錯誤

這題考的是你對前後端的理解是否正確。

前端歸前端,後端歸後端。任何人都可以發 request 到你的後端程式去,帶著任何的資料。

儘管你前端把按鈕藏起來,但沒人可以阻止我自己發 request 到後端。因為後端沒有做權限驗證,所以我只要自己發 request 過去,就可以刪除任何文章。

記住,後端的權限管理是一定要做的。在處理 CRUD 時要特別注意到權限管理這一塊,不要讓使用者能夠改到其他人的東西。

Q7

小明正負責寫一個專案,網址是:https://best-project.com 。這網站會需要用到公司網站某個檔案,裡面是一些使用者資料,例如說:https://lidemy.com/users.json ,小明直接點開這個網址,發現用瀏覽器可以看到內容,於是就說:

既然我用瀏覽器可以看得到內容,那用 Ajax 的時候也一定可以拿得到資料啦!我們來用 Ajax 拿資料吧!

請問小明的說法是正確的嗎?如果錯誤,請指出錯誤的地方。

點我看 Q7 解答

答案:錯誤

可以用瀏覽器直接打開網頁是一回事,但用 Ajax 又是另外一回事。我可以打開 Facebook.com,不代表我可以用 Ajax 拿到 Facebook.com 的內容。這完全是兩回事,是完全不相干的事情。

同源政策限制的是 Ajax,而不是你在瀏覽器上面直接造訪網站,這兩者是完全不同的。

因此這個網址的 Ajax 會受到同源政策的限制,可能拿得到也可能拿不到,要看後端有沒有帶 CORS header,所以小明說「一定也可以拿得到」是錯誤的。

Q8

公司要小明負責寫一個會員系統,小明在實作註冊與登入功能時,想起以前前輩教的事情,那就是在把密碼存入資料庫以前必須先經過 hash。

於是小明使用了 bcrypt,密碼在存入資料庫以前都會先使用 bcrypt 來做雜湊。

請問小明的做法是正確的嗎?如果錯誤,請指出錯誤的地方。

點我看 Q8 解答

答案:正確

bcrypt 是雜湊的演算法之一,大家要記得絕對不要存 plaintext 的密碼進資料庫喔!

Q9

小明寫了一段程式碼,希望能在網頁載入完成時執行一個 function:

function start() {
  console.log('網頁載入完成')
}

window.onload = start()

而小明在網站載入成功後打開 console,發現文字的確有被印出來。

請問小明的程式碼是否有問題?如果有的話,為什麼文字還是被印出來了?

點我看 Q9 解答

答案:有問題

這樣寫是代表要把 start 這個 function 執行後的結果傳給 window.onload,所以在網頁還沒載入好時其實就會執行 start()。

正確的寫法應該是 window.onload = start

Q10

小明在執行程式的時候出現了一個錯誤:Uncaught TypeError: Cannot read property 'selfId' of undefined,但百思不得其解,不知道是哪裡出了問題,以下是出錯的「部分」程式碼:

const result = list.filter(item =>
  item.parent.id === matches[0].parent.id &&
  item.parent.name === matches[0].parent.name &&
  item.selfId === homeData.selfId
).sort(
  (a, b) => a.typeId - b.typeId
);

根據你的推理,會出現這個錯誤的原因是什麼?

點我看 Q10 解答

這一題是我覺得最好玩的一題,考驗你對 JS 錯誤訊息的理解。

你可能會認為說:「不對吧,你很多資訊都沒有給齊,這題怎麼解?狀況很多吧!」

讓我來幫你解惑,首先你可能會答說:「homeData 上面可能沒宣告」,但如果是這種狀況,錯誤訊息就會是 ReferenceError: homeData is not defined,所以這種狀況可以排除,代表 homeData 一定有宣告。

同理,matches 也一定有宣告,不然錯誤訊息會不一樣。

再來,錯誤訊息告訴我們說:Cannot read property 'selfId' of undefined,代表我們試圖對一個 undefined 讀取 selfId 這個屬性。

出現 selfId 的是這行:item.selfId === homeData.selfId。

如果 item 是 undefined,前面 item.parent.id 時就會出錯,所以 item 是沒有問題的。

因此,會出現這個錯誤訊息的原因是 homeData 是 undefined。

commented

如果 Q2 不行 Q3 是不是題目就不成立 XD

在解 Q4 有查到一個https://xss-game.appspot.com/自己第三關破關中還蠻有趣的

Q1 google 問卷上的解答是

HTTP method 雖然有這麼多種,但如果不管語義的話,依然可以用 GET 實作出所有的 API。例如說 GET delete.php?id=1 就可以實作刪除功能,GET new_todo.php?content=xxx 就可以實作新增 Todo 的功能等等(但可能字數會有限制)

這裡有點好奇,就去查說到底限制多少字數,結果查到了這篇,上面有講說每個瀏覽器限制的字數,可是基本上這些都是瀏覽器在 address bar 跟windows.location 跟 <a> tag 的限制,所以單純用 JS 發 request 應該是沒有限制。所以如果是利用 ajax 發送 GET 就算在 URL 上面帶一本資治通鑑理論上也是沒問題(?)

commented

如果 Q2 不行 Q3 是不是題目就不成立 XD

好像是這樣沒錯XD

在解 Q4 有查到一個https://xss-game.appspot.com/自己第三關破關中還蠻有趣的

這個第三期其實有給大家玩,第四期拿掉因為難度太高一堆人破不出來XDD

有關限制字數

基本上 HTTP protocol 沒限制沒錯,但是:

  1. 瀏覽器的 ajax 可能也有限制(要去翻 source code)
  2. 伺服器也有可能有限制(這我比較確定一點,但不能 100% 肯定)