PHP 說明
名詞
雜湊(Hash) : 將目標文字轉換成具有相同長度的、不可逆的雜湊字串
加密(Encrypt) :是將目標文字轉換成具有不同長度的、可逆的密文
如 MD5, SHA256
跨網站指令碼(Cross-site scripting,簡稱XSS) : 是一種網站應用程式的安全漏洞攻擊,是代碼注入的一種
1 | <!-- 測試網站是否有正確處理特殊字元: --> |
SQL injection
也稱SQL隱碼或SQL注碼,是發生於應用程式與資料庫層的安全漏洞。簡而言之,是在輸入的字串之中夾帶SQL指令,在設計不良的程式當中忽略了字元檢查,那麼這些夾帶進去的惡意指令就會被資料庫伺服器誤認為是正常的SQL指令而執行,因此遭到破壞或是入侵。也稱為駭客的填字遊戲.
1 | # 原指令 |
資料庫正規化
Database schema templates
資料庫正規化,又稱正規化、標準化,是資料庫設計的一系列原理和技術
CSRF(Cross Site Request Forgery) 跨站請求偽造
- 現在有些 Framework 都有內建防禦 CSRF 的功能,可以很簡單的開啟
- 使用者的防禦 : 每次使用完網站就登出,就可以避免掉 CSRF
- Server 的防禦 :
- 檢查 Referer
- 加上圖形驗證碼, 簡訊驗證碼等等
- 加上 CSRF token
在 form 裡面加上一個 hidden 的欄位,叫做csrftoken,這裡面填的值由 server 隨機產生,並且存在 server 的 session 中。1
2
3
4
5<form action="https://small-min.blog.com/delete" method="POST">
<input type="hidden" name="id" value="3"/>
<input type="hidden" name="csrftoken" value="fj1iro2jro12ijoi1"/>
<input type="submit" value="刪除文章"/>
</form>
- browser 本身的防禦 : Chrome 51 版的時候正式加入了這個功能:SameSite cookie
1
2
3
4// 你原本設置 Cookie 的 header 長這樣:
Set-Cookie: session_id=ewfewjf23o1;
// 你只要在後面多加一個 SameSite 就好:
Set-Cookie: session_id=ewfewjf23o1; SameSite
1 | // 假設刪除文章命令如下 |
CORS(全名為 Cross-Origin Resource Sharing) 跨來源資源共享
1 | header('Access-Control-Allow-Origin: *'); |
Offset/limit-based and Cursored-based Pagination
- offset 在運作的時候需要從頭開始尋訪 (Scan table) 才能知道要取哪一段資料,所以當分頁越後面的時候查詢時間就會越久,在小型資料庫並不會有特別的感覺,但是當資料量非常龐大 (到達幾千萬或是上億時) 時,就能感受到時間明顯的差距
- 對於大型資料庫做分頁的其中一個替代方案便是 Cursor-based pagination,其能夠有效使用資料庫的 index 進行尋訪,不會對於資料庫造成傷害
設計參考
PHP Security 注意事項
- add session
- password save by hash
- XSS : print add htmlspecialchars(check echo 有無呼叫 escape function)
- SQL injection : add prepared statement
- 權限管理 : 做以下動作前,要再 check user 是否符合
modify, deleted, show private information
setup XAMPP
- XAMPP是最流行的PHP開發環境
- MariaDB是MySQL關聯式資料庫管理系統的一個復刻,由社群開發,有商業支援
install XAMPP
XAMPP Config
run XAMPP
![pic1](/2021/05/31/php-1/pic1.png)
Start Apache and MySQL
![pic2](/2021/05/31/php-1/pic2.png)
![pic3](/2021/05/31/php-1/pic3.png)
check Admin and PHP PHPInfo
![pic4](/2021/05/31/php-1/pic4.png)
![pic5](/2021/05/31/php-1/pic5.png)
![pic6](/2021/05/31/php-1/pic6.png)
Config PHP
點選 Config -> <browse> [PHP]
選 xampp > htdocs 目錄
新增工作目錄 和 檔案 a.php
1
2
3
4
// a.php
echo "Hello!"browser 執行 a.php
PHP 基本語法
1 |
|
1 | # output |
Disable cache
使用XAMPP環境,有時程式改變但 browser 顯示卻不正確,可能是 cache 的影響,選擇 Disable cache 再從新整理即可
![pic11](/2021/05/31/php-1/pic11.png)
from 含不顯示資料 input’s type=”hidden”
1 | <form class="input-form" method="POST" action="update_content_handle.php"> |
PHP example
Query string
1 |
|
1 | # web browser |
show time
1 |
|
1 | # web browser |
form by GET
1 | <!-- data.php --> |
1 | <!-- index.php --> |
form by POST - 要改用$_POST 抓參數
1 | <!-- data.php --> |
1 | <!-- index.php --> |
Query strig 標示錯誤
1 |
|
產生亂數字串
1 | function creatToken() { |
global 全域變數
1 |
|
htmlspecialchars for XSS
1 | // default 第二個引數為ENT_COMPAT,只轉化雙引號,不對單引號做轉換,故加入 ENT_QUOTES |
Snippet for mySQL
special parameter
1 | // check update/detelet success - $conn->affected_rows |
connect database
1 | <!-- conn.php --> |
1 | <!-- data.php --> |
query time from database
1 | <!-- data.php --> |
1 | <!-- data.php --> |
讀取 mySQL
1 | <!-- index.php --> |
讀取 mySQL(check error)
1 |
|
新增 mySQL 資料
1 | <!-- add.php --> |
新增 mySQL 資料(使用 form)
1 | <!-- index.php --> |
1 | <!-- add.php --> |
新增 mySQL 資料(check error)
1 |
|
刪除 mySQL 資料
1 | <!-- index.php --> |
1 | <!-- delete.php --> |
刪除 mySQL 資料(soft delete)
1 |
|
修改 mySQL 資料
1 | <!-- index.php --> |
1 | <!-- update.php --> |
MySQL Prepared Statements (預防 SQL injection)
1 | $sql = "select * from users where username= ?"; |
MySQL join
1 | # INNER JOIN |
1 | $sql = 'select C.id, C.content, C.create_at, U.username, U.nickname ' . |
MySQL limit and offset
1 | $sql = 'select C.id, C.content, C.create_at, U.username, U.nickname ' . |
MySQL 檢查欄位是否 NULL 或空白
1 | # 欄位 col_name 是 NULL 的紀錄 |
login 檢查
1 | // login_handle.php |
- check login state
1 |
|
PHP MySQL Get Last Inserted ID
1 | $id = $conn->insert_id; |
PHP 語法
變數
array
- array 設定
1
2
3
4$array = array(1, 1, 1, 1, 1, 8 => 1, 4 => 1, 19, 3 => 13);
$firstquarter = array(1 => 'January', 'February', 'March');
$foo = array('bar' => 'baz');
echo "Hello {$foo['bar']}!"; // Hello baz! - 設空 array - $levelArr = [] or levelArr = array();
- push array - array_push($levelArr, $row );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31function getManageLevels() {
global $conn;
$levelArr = [];
$sql = 'select * from user_levels order by user_level';
$stmt = $conn->prepare($sql);
$result = $stmt->execute();
if ($result) {
// 真正取資料要執行 $stmt->get_result();
$result = $stmt->get_result();
while($row = $result->fetch_assoc()) {
array_push($levelArr, $row );
}
}
return $levelArr;
}
// show array
$levelArr = getManageLevels();
$length = count($levelArr);
for ($i=0 ; $i < $length ; $i++) {
echo $levelArr[$i]['id'] . ' ' ;
echo $levelArr[$i]['user_level'] . ' ' ;
echo $levelArr[$i]['level_describe'] . ' ' ;
echo $levelArr[$i]['all_modify'] . ' ' ;
echo $levelArr[$i]['all_delete'] . ' ' ;
echo $levelArr[$i]['self_create'] . ' ' ;
echo $levelArr[$i]['self_modify'] . ' ' ;
echo $levelArr[$i]['self_delete'] . ' ' ;
echo ' <br>';
}
object
1 | // object 設定 |
$_SERVER PHP預定義的超全局變量
- $_SERVER[“SCRIPT_NAME”] - /index.php
當前腳本路徑 - $_SERVER[“REQUEST_URI”] - /index.php?id=1
訪問頁面URI,包含查詢字符串 - $_SERVER[“QUERY_STRING”] - id=1
查詢字符串,不存在為” “ - $_SERVER[“REQUEST_METHOD”] - GET
請求方法,如”POST”、”PUT”等 - $_SERVER[“SERVER_PROTOCOL”] - HTTP/1.1
通信協議的名稱和版本 - $_SERVER[“GATEWAY_INTERFACE”] - CGI/1.1
服務器使用的CGI 規範的版本 - $_SERVER[“REMOTE_PORT”] - 60599
用戶連接服務器使用的端口 - $_SERVER[“SCRIPT_FILENAME”] - E:/WWW/example/index.php
當前腳本的絕對路徑 - $_SERVER[“DOCUMENT_ROOT”] - E:/WWW/example/
當前腳本文檔根目錄的絕對路徑 - $_SERVER[“REMOTE_ADDR”] - 127.0.0.1
用戶的IP地址 - $_SERVER[“SERVER_PORT”] - 80
服務器使用的端口 - $_SERVER[“SERVER_ADDR”] - 127.0.0.1
服務器的IP地址 - $_SERVER[“SERVER_NAME”] - www.example.com
服務器的主機名,注:如果腳本運行於虛擬主機中,該名稱是由那個虛擬主機所設置的值決定。在Apache 2裡,必須設置UseCanonicalName = On和ServerName。否則該值會由客戶端提供,就有可能被偽造。上下文有安全性要求的環境裡,不應該依賴此值。 - $_SERVER[“SERVER_SOFTWARE”] - Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
響應頭中Server的內容 - $_SERVER[“SERVER_SIGNATURE”] -
包含了服務器版本和虛擬主機名的字符串 - $_SERVER[“HTTP_HOST”] - www.example.com
請求頭中Host項的內容 - $_SERVER[“HTTP_CONNECTION”] - keep-alive
請求頭中Connection項的內容 - $_SERVER[“HTTP_PRAGMA”] - no-cache
請求頭中Pragma項的內容 - $_SERVER[“HTTP_CACHE_CONTROL”] - no-cache
請求頭中Cache-Control項的內容 - $_SERVER[“HTTP_UPGRADE_INSECURE_REQUESTS”] } |1
請求頭中Upgrade-Insecure-Requests項的內容 - $_SERVER[“HTTP_USER_AGENT”] - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
請求頭中User-Agent項的內容 - $_SERVER[“HTTP_ACCEPT”] - text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, / ;q=0.
請求頭中Accept項的內容 - $_SERVER[“HTTP_ACCEPT_ENCODING”] - gzip, deflate
請求頭中Accept-Encoding項的內容 - $_SERVER[“HTTP_ACCEPT_LANGUAGE”] - zh-CN,zh;q=0.8
請求頭中Accept-Language項的內容 - $_SERVER[“PHP_SELF”] - /index.php
當前執行腳本的文件名 - $_SERVER[“REQUEST_TIME_FLOAT”] - 1510112348.8084
請求開始的時間戳,微秒級別精準度 - $_SERVER[“REQUEST_TIME”] - 1510112348
請求開始的時間戳 - $_SERVER[‘HTTP_REFERER’] - localhost/robert/comment/register.php
前一頁面的URL 地址
1 | // 前頁 uri |
Snippet for PHP
error code 處理
1 |
|
下拉式選單 - <select>, <option>
1 | <select name="level"> |
分頁
1 |
|
1 | .page { |
utils
check accountValid
1 |
|
設定 HTML 跳脫字元 - check echo 有無呼叫
1 | // 設定 HTML 跳脫字元 |
回傳 json
1 | header('Content-Type: application/json; charset=utf-8'); |
html + php 回傳 json
1 | <!-- index_api.html --> |
1 |
|
1 |
|
PHP function
simple function
- exit() - 結束程式
轉換
轉數字
1 | $page = (int) $_GET['page']; |
floor()/ceil()/round()
1 | // 取整數(無條件進位) |
ASCII code 轉換 ord()/chr()
1 | // 字元轉換為 ASCII 碼 |
string
strpos() - check 是否包含字串,不包含傳回 false
1 | // check 是否包含字串,不包含傳回 false |
substr()/mb_substr() - 擷取字串
1 | // 擷取字串 |
date()
1 |
|
header 開啟網頁
1 | header("Location: http://$host$uri/$extra"); |
cookie
1 | // set cookie - 1 hour --> *24*30 = 30days |
session
1 | // 啟用 session |
die() - 印出後結束程式
1 | if (!$result) { |
rand() - 產生亂數
1 |
|
hash() - 雜湊產生
1 | /* |
password_hash()/password_verify() - 雜湊密碼產生與驗證
1 | // password_hash ( string $password , mixed $algo , array $options = ? ) : string|false |
htmlentities 和 htmlspecialchars
htmlspecialchars 只轉化一些特殊html程式碼,而 htmlentities 卻會轉化所有的html程式碼,連同裡面的它無法識別的中文字元也給轉化,所以有時會讓中文變成亂碼,所以最好用 htmlspecialchars
1 | /* |
require()/require_once() and include()/include_once()
- require 通常放在 PHP 程式的最前面,PHP 程式在執行前,就會先讀入 require 所指定引入的檔案,使它變成 PHP 程式網頁的一部份。常用的函式,亦可以這個方法將它引入網頁中。引入是無條件的,發生在程式執行前,不管條件是否成立都要導入(可能不執行)
- include 一般是放在流程控制的處理區段中。PHP 程式網頁在讀到 include 的檔案時,才將它讀進來。這種方式,可以把程式執行時的流程簡單化。可以是有條件的,發生在程式執行時,只有條件成立時才導入(可以簡化編譯生成的代碼)。
- require_once()/include_once() 會檢查檔案是否已匯入,若已匯入則不重覆匯入
1 | require_once('conn.php'); |
echo , print_r() and var var_dump()
- echo 用於輸出數值變數或者是字串
1
2
3// 雙引號可加變數
$user = 'Robert';
echo "Hello $user!<br>" ; // Hello Robert! - print_r() 用於輸出一個陣列
- var_dump() 用於輸出<變數型別,變數值,變數長度>,可以是各種變數型別