Skip to content

Commit

Permalink
更新 week11, 12, 13 與 common
Browse files Browse the repository at this point in the history
  • Loading branch information
aszx87410 committed Oct 25, 2020
1 parent 568f37e commit ac3611d
Show file tree
Hide file tree
Showing 5 changed files with 423 additions and 34 deletions.
6 changes: 5 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@

另外,並不是每一週都會有自我檢討或是參考解答,會視當週的狀況來決定。

參考解答也會放在 examples 資料夾裡面,你要自己點開各個檔案才會知道內容是什麼,通常可以從檔案名稱或是資料夾名稱猜測是哪個作業。
參考解答也會放在 examples 資料夾裡面,你要自己點開各個檔案才會知道內容是什麼,通常可以從檔案名稱或是資料夾名稱猜測是哪個作業。

## 常見重點整理

請參考[常見重點整理](common.md)
186 changes: 186 additions & 0 deletions examples/common.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# 常見重點整理

因為有些事情在計畫裡面提過很多遍了,不想每一次都重新再講一遍,因此拿這篇來當重點整理,講一些很重要的地方,提醒大家一下。有些可能之前忘了提到,或者是改作業的時候沒有特別挑出來,總之請以這篇為準。

## 什麼是不好的 code?

不好 code 的定義因人而異,但有很多工程師都有一個特點,那就是都有著「程式碼潔癖」。

這也是我第一份實習時我老闆很強調的一件事,在寫程式碼的時候你必須要細心,必須要對自己寫出的 code 負責,至少要讓你的 code 「看起來」是好的。

那到底怎樣才是好的?

這答案有可能出乎你意料,因為聽起來搞不好比你想像中的簡單,可是你卻發現自己多數時候都沒有做到。

兩點答案給大家:

1. 符合命名慣例以及命名一致性
2. 合理的變數名稱

先介紹三種常見的命名慣例以及它的專有名詞:

### 駝峰式命名(Camel case)

人如其名,駝峰就是會一高一低的,所以比如說你有一個 handle add post 的 function,就會叫做 `handleAddPost`,單字之間不空格,而是直接連起來,但是連起來的第一個字要大寫。

駝峰又有分兩種,分別是小駝峰(lower camel case)跟大駝峰(upper camel case)。

第一種很簡單,就是開頭是小寫,也就是我們上面說的 `handleAddPost`

第二種就只是開頭變成大寫而已,變成:`HandleAddPost`,而第二種大駝峰也被叫做 Pascal Case。

### 蛇型命名(Snake case)

其實蛇就是底線的意思啦,因此同樣以 handle add post 這個 function 來講,會取叫 `handle_add_post`,利用底線來連接單字,然後全部都小寫。

### 烤肉串命名(Kebab case)

Kebab 是烤肉串的意思,那哪一個符號看起來很像那根棍子呢?就是 `-`,所以看起來會像是 `handle-add-post` 這樣,這個就叫做 kebab case。

講完了這三種常見的命名慣例以及名詞之後,就可以來看看不同地方的命名慣例是用哪一種。沒錯,不同程式語言或是不同環境,使用的命名慣例會不一樣,所以大家需要一點時間熟悉一下。

雖然這是命名慣例,不是強制的,但基本上因為所有人都是這樣遵從這套慣例在開發,如果沒有照慣例的話,就會比較難跟其他人合作,你寫的 code 就會變成所謂的「不好的 code」。

### JavaScript

在 JS 裡面,大家習慣的是 lower camel case,也就是 `handleAddPost` 這一種。大家仔細想一下那些 WebAPI 或是內建的 function 就知道了,例如說:

1. setTimeout
2. requestAnimationFrame
3. getElementById

這些都是用 lower camel case 在命名的。當然不止 function,變數也會這樣子命名。

所以除非有特殊理由,不然你不應該在 JS 裡面寫出:

1. set_timeout
2. SetTimeout
3. Set_timeout

這幾種的命名方法

不過第二個那個開頭大寫的駝峰式,有些人可能會有印象看過,例如說:

1. XMLHttpRequest
2. Number
3. Set

為什麼這些可以開頭大寫呢?因為這些東西基本上都可以算是一個「class」,就是物件導向那個 class。在物件導向裡面,class 的命名慣例就是大寫開頭。

所以 JS 基本上都是小寫駝峰式,但有其他好理由的話,可以用大寫駝峰式。或者是之後大家會學到的 React,component 也都會用大寫開頭的駝峰式來命名。

最後還有一種,那就是常數,通常都是不會變的東西,但定義其實也沒那麼嚴謹,這種的就會是 snake case + 全大寫。

例如說圓周率 PI,就會是 `Math.PI`,例如說第八週的作業裡面有 client id,你就可以定義成:`const CLIENT_ID = 'xxxx'`,因為這在這整個程式裡面是不會變的值,不過這點之前沒有特別提是因為就算你取做 `const clientId = 'xxx'` 我也覺得 OK,這邊倒是還好。

但除此之外,請一律使用小寫駝峰式。

* isDelete(O)
* is_delete(X)
* is_delete(X)
* IsDelete(X)

是否刪除就是`isDelete`,不是`is_delete`也不是`is_Delete`,更不是`IsDelete`

### 資料庫 table 與欄位

資料庫走的是 snake case 為主,然後 table 名稱的慣例是複數,所以使用者資料會叫做 `users`

* users(O)
* Users(X)
* user(X)

欄位名稱的話就是 snake case,例如說建立時間,會是這樣:

* created_at(O)
* createdAt(X)
* CreatedAt(X)
* Created_At(X)
* created-at(X)

使用者的大頭貼可能會是這樣:

* user_avatar(O)
* userAvatar(X)
* UserAvatar(X)

Week17 講到 ORM 之後,通常只會關注 ORM 裡面我們的 model 的 naming,例如說我們用 JS,所以程式裡面就會是 createdAt,然後資料庫長怎樣就交給 ORM 處理,因此有可能跟上面講的會不一樣。

### Url

網址的話其實慣例沒那麼嚴格,比較常見的兩種我都有看過,例如說:

1. handle_add_post
2. handle-add-post

不過我自己比較喜歡的是第二種 kebab case,然後這邊順便幫大家補充一個小知識,像是那種部落格或英文網站,通常都會有一個單一文章的網址,例如說我的 medium 文章:

https://hulitw.medium.com/lidemy-mentor-program-4th-updates-c344302c8a2d

後面那一段 `lidemy-mentor-program-4th-updates-c344302c8a2d`,這個有個專有名詞叫做 slug

你用 `slug webpage` 去找可以找到滿多資料,這個通常會與 SEO 有關。

以上差不多就是幾個課程中有碰到的地方的 naming convention,這邊還沒提到變數命名是否恰當,光是變數名稱的慣例就已經先死一堆人了。

這種命名慣例如果不一致,例如說 JS 變數硬要叫做 IsDelete,然後其他地方都符合 camelCase 的話,就好像你在一篇文章裡面在再不分,對文字(對程式碼)比較敏感的人,就會直接覺得這一段程式碼寫得很爛,因為連在再都分不清楚。

這種細節才是最應該注意,然後影響也會比你想像中的大的地方。

## 變數命名

先給大家一個大原則,如果你不知道某個東西是否可以縮寫或簡稱,你就全部寫下去就沒事了,例如說 `lottery` 不要簡寫成 `lotto``controller`不要簡寫成 `ctler` 之類的,寫全稱頂多就是變數名稱長一點,但如果寫簡稱然後並不是一種慣例的話,那一樣就會被認定為是不好的 code。

舉例來說,event handler 傳進來的參數 event,常見的簡寫就是 evt 或是 e,這些都還可以接受,但像是 target 就沒有簡稱為 tar 或是 tgt 這種用法,所以不能簡稱。因此這些其實是很憑經驗的,經驗不足的時候就乖乖寫好寫滿就好,不要自作聰明。

然後 function 基本上都會搭配動詞開頭,像是第八週裡面的 `getGames` 或是 `getStreams` 之類的,就是去拿什麼東西,然後像 `setName` 或是 `setTitle`,一看也知道是要去設定一些什麼,get 跟 set 是兩種最常見的動詞。

如果你不知道哪些動詞很常用,請參考我之前提供過的所有範例程式碼,已經出現過很多範例了。然後通常很少有 function 不包含動詞,因為意義不明,不知道這個 function 到底要做些什麼。

## 程式碼排版

以後你去面試的時候,有可能面試官會先去你的 GitHub 看你的程式碼,所以沒有人會管在你的電腦上看起來怎樣,大家只會去看在 GitHub 上面顯示長怎樣。

所以請把自己編輯器的排版調整好,我都是統一設成只用 space 不用空格,然後一次空兩格之類的,在 GitHub 上顯示沒有出問題過。

## 資訊安全

請永遠記住一件事情,在考慮資訊安全的時候,你要假設一件事情,那就是:

> 程式碼都是公開的
這是什麼意思?

例如說部落格後台管理的功能,有些人可能會寫一個 `handle_delete_post.php` 來處理刪除文章,然後裡面就是接收一個 id,把文章刪除掉,沒有做任何的驗證。

為什麼沒有做驗證?因為這一頁照理來說只有管理員看得到,所以應該沒差吧?

這時候如果你想到我上面說的原則,「程式碼都是公開的」,你就知道這個檔案,這個 url 是會被發現的,一旦被發現之後,任何人都可以刪除文章。所以這是不對的,正解是做好權限管理,檢查 session 裡面是不是有登入,沒登入就把使用者導去其他地方。

儘管程式碼被別人看光,他也沒辦法破解這機制,因為他沒有登入,所以就不會被設定 session,裡面就不會有東西。

這點很重要,超級重要,權限管理是很多人會忘記的地方。當你在做 CRUD 的時候,或是在看別人程式碼的時候,一定要有的一個想法是:「這段 code 會不會可以讓其他人執行?」,只要你有把這個想法記住,就可以少寫很多漏洞出來。

再來第二點是:

> 不要存著僥倖心態
這點之前我自己在示範的時候其實沒有做得很好,例如說留言板我只對 nickname 跟 content 做跳脫,沒有對 id 或是建立時間做跳脫。但其實呢,這是應該要做的,你應該把任何要輸出資料的地方都編碼過後再輸出,才能確保不會有問題。

Week17 以後我們用的 template engine 就是這樣,永遠都預設把你要輸出的東西做編碼以後才輸出,而不是直接輸出,就可以避免掉很多的 XSS 攻擊。

所以無論你是要輸出 id 還是你確認輸出的只會是數字,都可以 escape 以後再輸出,比較保險一點。

### 前端檢查 vs 後端檢查

承上,請大家再記住一件事情,那就是:

> 前端做資料檢查,只是為了增進使用者體驗,後端做資料檢查才是真的檢查
原理很簡單,假設你註冊的時候前端會檢查使用者名稱是不是 > 8 個字,沒有的話就會跳錯誤出來,所以使用者還沒發 request 就可以知道有錯。

但如果你後端沒做檢查,我一樣可以繞過瀏覽器去打這個 endpoint,我自己帶資料去打就好,根本不需要過瀏覽器,那就根本不會執行到你寫的 JS 驗證邏輯,所以那些邏輯完全沒有任何效力,只是增進使用者體驗,只要後端沒檢查,我資料就一定寫得進去。

這點也很重要,大家一定要記住。所以後端驗證一定要做,前端可做可不做。

其他相關概念請參考 Week11 的參考範例,裡面也有講到很多資訊安全相關的概念。
78 changes: 48 additions & 30 deletions examples/week11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,54 @@

## 提示:在寫完作業之後看效果最佳,沒寫作業前請不要看

## header 跟你想的不一樣(2020-10-25 更新)

在 PHP 裡面,我們會用:

``` php
header('Location: ./index.php');
```

把使用者導去其他地方,背後原理就是之前第四週講過的 HTTP status code,這樣的寫法會回一個 302 status code,然後帶上一個 response header 叫做:`Location: ./index.php`,瀏覽器收到這個 response 之後,就把使用者導過去,然後再發一個 request 去要求新頁面的資料。

但要注意的事情是,這個並沒有中斷程式的執行,例如說以下程式碼:

``` php
header('Location: ./index.php');
echo 123;
```

你用瀏覽器執行是看不到東西的,因為你換頁了,所以瀏覽器把之前頁面的 response 丟掉了不讓你看,但如果你自己用 curl 或是其他方法去試的話,會發現 response body 是有這個 123 的。

總之呢,header 並不會中斷程式執行,底下的東西還是會繼續跑,這點應該跟你想的不一樣。所以請確保 header() 後面都有 exit 或是 die,確保有結束程式的執行。

## 資安意識再度補充(2020-09-05 更新)

大家在寫後端的時候,一定要有兩個認知:

1. 你要預設你的程式碼已經被駭客看光光了
2. 不要存著僥倖心態

這週的作業有很多人寫到管理後台的時候,相關的操作都沒有做權限檢查,背後的心理應該是:「這頁面只有管理員知道,不需要做吧」

但如果你心中存有上面認知的第一點,你就會知道這是要做的。因為你不應該假設駭客看不到你的程式碼,正好相反,你應該假設程式碼被看光了。在程式碼被看光的前提下維持安全性,這才是最重要的,也是大家一定要有的一個心態。

所以後台的頁面依然要做權限檢查,要檢查現在的使用者是管理員才能進行操作,否則請把 user 導去其他地方。

再來,第二點,不要存著僥倖心態。

承上,管理員頁面有些人並沒有做 escape,所以身為一個惡意使用者,我可以註冊一個帳號是`<script>alert(1)</script`,一般使用者不會有事,但如果管理員打開後台管理頁面,就被 XSS 了!

因此,該做的地方都要做。或如果你不知道哪邊要做,你就全部都做就不會有事了。

這邊其實也是我教學裡面沒有示範好的地方,應該全部輸出資料庫內容的地方都用 escape 會比較好。為什麼呢?因為漏洞是可以結合的。

舉例來說,你文章的建立時間沒有 escape,因為你預期他只會是一個時間,但駭客找到了一個 SQL Injection 漏洞,把文章時間改成一個惡意的 XSS 字串,你的網站就從 SQL Injection 演變成 SQL Injection + XSS 了。

最後,使用 GET 傳遞的參數要特別注意,有些同學的程式碼會直接把 GET 的東西印到畫面上,這是非常危險的,因為這就是 reflected XSS,使用者點了某個網址之後就會中 XSS,也是 XSS 的一種。

大概就是這樣,如果不清楚的可以去看其他同學的作業,有很多人都有被我抓到錯誤XD

## prepare statement

大家要注意到 prepare statement 其實不是 PHP 的東西,是資料庫的。是你在 PHP 呼叫以後他幫你去告訴資料庫要做這件事。
Expand Down Expand Up @@ -294,33 +342,3 @@ $conn->close();

(上面 code 來自同學的真實案例,可參考:https://github.com/Lidemy/mentor-program-3rd-ChihYang41/pull/28/files/c09007e1b0c8f2f54662cbbb7e1bb90eebcd6e2b

## 資安意識再度補充

2020-09-05 新增

大家在寫後端的時候,一定要有兩個認知:

1. 你要預設你的程式碼已經被駭客看光光了
2. 不要存著僥倖心態

這週的作業有很多人寫到管理後台的時候,相關的操作都沒有做權限檢查,背後的心理應該是:「這頁面只有管理員知道,不需要做吧」

但如果你心中存有上面認知的第一點,你就會知道這是要做的。因為你不應該假設駭客看不到你的程式碼,正好相反,你應該假設程式碼被看光了。在程式碼被看光的前提下維持安全性,這才是最重要的,也是大家一定要有的一個心態。

所以後台的頁面依然要做權限檢查,要檢查現在的使用者是管理員才能進行操作,否則請把 user 導去其他地方。

再來,第二點,不要存著僥倖心態。

承上,管理員頁面有些人並沒有做 escape,所以身為一個惡意使用者,我可以註冊一個帳號是`<script>alert(1)</script`,一般使用者不會有事,但如果管理員打開後台管理頁面,就被 XSS 了!

因此,該做的地方都要做。或如果你不知道哪邊要做,你就全部都做就不會有事了。

這邊其實也是我教學裡面沒有示範好的地方,應該全部輸出資料庫內容的地方都用 escape 會比較好。為什麼呢?因為漏洞是可以結合的。

舉例來說,你文章的建立時間沒有 escape,因為你預期他只會是一個時間,但駭客找到了一個 SQL Injection 漏洞,把文章時間改成一個惡意的 XSS 字串,你的網站就從 SQL Injection 演變成 SQL Injection + XSS 了。

最後,使用 GET 傳遞的參數要特別注意,有些同學的程式碼會直接把 GET 的東西印到畫面上,這是非常危險的,因為這就是 reflected XSS,使用者點了某個網址之後就會中 XSS,也是 XSS 的一種。

大概就是這樣,如果不清楚的可以去看其他同學的作業,有很多人都有被我抓到錯誤XD


20 changes: 18 additions & 2 deletions examples/week12/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
# Week12 作業自我檢討

本週無自我檢討

SPA 的部分請參考:

1. [前後端分離與 SPA](https://blog.techbridge.cc/2017/09/16/frontend-backend-mvc/)
2. [跟著小明一起搞懂技術名詞:MVC、SPA 與 SSR](https://medium.com/@hulitw/introduction-mvc-spa-and-ssr-545c941669e9)

## SPA 補充

這邊稍微講一下,有些人會以為 SPA 的網址沒辦法變,這是錯誤的。SPA 的網址還是可以變,但是跟之前後端為主的方式是不同的。

後端是這樣的:

1. 你現在在網址 A
2. 點了某個按鈕,跳到網址 B,瀏覽器發一個 request 到 server
3. 瀏覽器接收到 response,render B 頁面資料

而 SPA 是這樣的:

1. 你現在在網址 A
2. 點了某個按鈕,跳到網址 B,這時候我們其實只是用 JS 來改變網址列,所以並不會發一個 request 到 server
3. SPA 自己處理網址變動,render 頁面 B 的資料

JS 會利用一個叫做 history API 的東西來做,詳情大家可以自己去尋找相關資料,想快速試試看的可以隨便開一個頁面,然後打開 console 輸入 `history.pushState(null, null, '/hello')`,就能夠看到什麼叫做用 JS 來改變網址了。

## Todos 範例說明

1. [只有 UI 切好的版本](hw2/todo-ui-only.html)
Expand Down
Loading

0 comments on commit ac3611d

Please sign in to comment.