# Web應用程式安全參考指引(V.2.0) [PHP指引整理]
###### tags: `資安`, `Apache`, `PHP`
> ※相關資訊參照自技服中心:
> 1. [106年Web應用程式安全參考指引 (V.2.0) ](https://www.nccst.nat.gov.tw/CommonSpecification?lang=zh)
> 2. [108第1次政府資通安全防護巡迴研討會 - 政府資訊作業委外安全管理](https://download.nccst.nat.gov.tw/attachfilehandout/%E8%AD%B0%E9%A1%8C%E5%9B%9B%EF%BC%9A%E6%94%BF%E5%BA%9C%E8%B3%87%E8%A8%8A%E4%BD%9C%E6%A5%AD%E5%A7%94%E5%A4%96%E5%AE%89%E5%85%A8%E7%AE%A1%E7%90%86V5.pdf)
> 3. [107第2次政府資通安全防護巡迴研討會 - 資通系統符合資安法規之安全強化說明](https://download.nccst.nat.gov.tw/attachfilehandout/%E8%AD%B0%E9%A1%8C%E4%B8%89%EF%BC%9A%E5%9B%A0%E6%87%89%E8%B3%87%E5%AE%89%E6%B3%95%E6%96%BD%E8%A1%8C-%E8%B3%87%E9%80%9A%E7%B3%BB%E7%B5%B1%E7%AC%A6%E5%90%88%E8%B3%87%E5%AE%89%E6%B3%95%E8%A6%8F%E4%B9%8B%E5%AE%89%E5%85%A8%E5%BC%B7%E5%8C%96%E8%AA%AA%E6%98%8Ev2.pdf)
> 4. [106安全資訊系統開發訓練研討會 - 資訊系統安全強化要點](https://download.nccst.nat.gov.tw/attachfilehandout/%E8%AD%B0%E9%A1%8C%E4%BA%8C%EF%BC%9A%E8%B3%87%E8%A8%8A%E7%B3%BB%E7%B5%B1%E5%AE%89%E5%85%A8%E5%BC%B7%E5%8C%96%E8%A6%81%E9%BB%9E.pdf)
>
> ※其他參照資訊
> 1. [OWASP PHP Configure Cheatsheet](https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html)
> 2. [Content Security Policy (CSP) 筆記](https://hackmd.io/@Eotones/BkOX6u5kX)
* 資通系統防護基準構面分析 (根據「[資通安全責任等級分級辦法](https://law.moj.gov.tw/LawClass/LawAll.aspx?pcode=A0030304)」之「附表十」)
* 存取控制
* 稽核與可歸責性
* 營運持續計畫
* 識別與鑑別
* 系統與服務獲得
* 系統與通訊保護
* 系統與資訊完整性
## 1 存取控制
#### 1.1 帳號管理
* 使用者的 Session 至多在 30 分鐘內未活動即自動失效。 (普、中、高)
```ini
; 在 php.ini 中做調整設定,預設值為 1440 (單位為秒)
; 預設值為 24 分鐘,若要設定為 30 分鐘,則改為 1800
session.gc_maxlifetime = 1440
```
* 使用者的 Session 應該手動登出機制,並且在登出後失效。 (普、中、高)
```php
<?php
session_start();
// 清空 Session
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie( session_name(), time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
}
session_destory();
```
* 管理者介面限制存取來源IP。 (普、中、高)
```php
<?php
// 取得使用者 IP,以便限制存取範圍
function getClientIP() {
/**
* 若使用者和 Web 服務中間,有 proxy server 者,
* 則應先檢查 $_SERVER['HTTP_CLIENT_IP']、$_SERVER['HTTP_X_FORWARDED_FOR']
*/
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
```
#### 1.2 最小權限
* 對使用者以最小權限方式賦予功能。 (中、高)
* 以較低權限的使用者去啟動軟體程序或伺服器服務。 (中、高)
* 建立 apache 使用者和群組。
```sh
group add -r apache
useradd apache -r -g apache -d /var/www -s /sbin/nologin
```
* 在 httpd.conf 中,設定僅有apache使用者和群組可使用。
```apacheconf
User apache
Group apache
```
#### 1.3 遠端存取
* 對於每一種允許遠端存取類型,都應先取得集中式的授權。
## 2 稽核與可歸責性
#### 2.1 稽核事件
* 針對「身份鑑別失敗」、「存取資源失敗」、「重要行為」、「重要資料異動」、「功能錯誤」及「管理行為」進行日誌紀錄。 (普、中、高)
* 在專案內,安裝 log4php 套件。
```sh
composer require apache/log4php
```
* 在專案內建立一個 `config.xml` 檔案。
```xml
<configuration xmlns="http://logging.apache.org/log4php/">
<appender name="myConsoleAppender" class="LoggerAppenderConsole" />
<appender name="myAppender" class="LoggerAppenderFile">
<layout class="LoggerLayoutPattern">
<param name="conversionPattern" value="%date{Y-m-d H:i:s,u} [%logger] %message%newline" />
</layout>
<param name="file" value="myLog.log" />
</appender>
<root>
<level value="WARN" />
<appender_ref ref="myAppender" />
</root>
</configuration>
```
* 在專案內使用 log4php 來紀錄 log 。 「[參考來源](http://logging.apache.org/log4php/)」
```php
<?php
// Import log4php
require_once __DIR__.'/vendor/autoload.php';
// Tell log4php to use our configuration file.
Logger::configure('config.xml');
// Fetch a logger, it will inherit settings from the root logger
$log = Logger::getLogger('myLogger');
// Start logging
$log->trace("My first message."); // Not logged because TRACE < WARN
$log->debug("My second message."); // Not logged because DEBUG < WARN
$log->info("My third message."); // Not logged because INFO < WARN
$log->warn("My fourth message."); // Logged because WARN >= WARN
$log->error("My fifth message."); // Logged because ERROR >= WARN
$log->fatal("My sixth message."); // Logged because FATAL >= WARN
```
* 定義log level多配合log4php,不過也可透過 `trigger_error()`,即可靠php本身達成此效果。
```php
<?php
trigger_error("Notice Message",E_USER_NOTICE);
trigger_error("Warning Message",E_USER_WARNING);
trigger_error("Error Message",E_USER_ERROR);
```
* Log事件等級
Level | Severity | Description
--------|----------|----------------
FATAL | Highest | 嚴重錯誤 - 例如系統無法運作
ERROR | ... | 錯誤訊息 - 通常是指例外發生(Exception)
WARN | ... | 警告,非嚴重的錯誤,系統仍能正常運作
INFO | ... | 一般資訊,通常Production環境會使用
DEBUG | ... | 偵錯,一般在Production環境不會使用
TRACE | Lowest | 詳細資訊,只有在Development環境使用
* Apache Log設定等級 (政府組態 TWGCB-04-005-0037)
```apacheconf
#預設值是warn
#LogLevel warn
LogLevel notice core:info
```
#### 2.2 稽核紀錄內容
* 日誌紀錄內容包含以下項目: (普、中、高)
1. 識別使用者之ID(不可為個資類型)。
2. 經系統校時後的時間戳記。 (若在config.xml的pattern中已經定義時戮,則可略過)
3. 執行的功能或存取的資源。
4. 事件類型或等級(prority)。
5. 事件描述。
* 採用單一的日誌紀錄機制,確保輸出格式的一致性。 (普、中、高)
* PHP實作範例:
```php
<?php
// Import log4php
require_once __DIR__.'/vendor/autoload.php';
// Tell log4php to use our configuration file.
Logger::configure('config.xml');
// Fetch a logger, it will inherit settings from the root logger
$log = Logger::getLogger('myLogger');
// Who - 使用者ID
$ClientUser = $_SESSION['acc'];
// What - 系統代碼 (對應t_sso_sys)
$SysID = "D05";
// What - 執行的功能 (程式檔名或ajax.php內的動作名稱)
$FuncName = "up_grade.php";
// Where - 使用者IP
$ClientIP = getClientIP();
// How - 存取的資料表(主要含有個資的資料表或者關鍵的資料表,若沒有,以 - 字眼替代)
$DBTable = "TASTUD";
// How - 事件類型
// 身份鑑別失敗 - LF (登入失敗login faliure)
// 資源失敗 - RF (系統存取失敗resource faliure)
// 重要行為 - AC 權限新增 / AD 權限刪除 / AU 權限異動
// 重要資料異動 - C/R/U/D
// 功能錯誤 - FF
// 管理行為(如重要參數代碼表的異動) - M
$EventType = "R";
// How - 事件描述 (中文描述,使用者定義或開發者於程式流程中定義要紀錄的資訊,避免「/」字眼)
$EventDesc = "";
// How - 錯誤代碼 (開發者於程式流程中定義例外狀況的代碼)
$ErrorCode = "";
// Start logging
$log->info($CleintUser."/".$SysID."/".$FuncName."/".$ClientIP."/".$DBTable."/".$EventType."/".
$EventDesc."/".$ErrorCode);
```
* apache的access.log應記載格式為combined (政府組態 TWGCB-04-005-0038)
```apacheconf
CustomLog "logs/qry-ssl-all.log" combined
```
* [補充] Apache2 前面加上 Load Balancer (F5 Big IP LTM),走得就類似 Reverse Proxy 的模式,會由 F5 將 X-Forwarded-For 轉送到 Apache。
> ※相關連結資訊:
> 1. [APACHE的記錄檔格式 LogFormat 語法](http://n.sfs.tw/content/index/10147)
> 2. [Apache2 抓取 Load Balancer Client IP 的設定](https://blog.longwin.com.tw/2016/12/f5-get-source-ip-apache2-log-2016/)
* apache log所記錄到的client ip會是 load balancer 的ip,而不是實際user的ip。更改httpd.conf設定如下:
```apacheconf
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
# 複製一行combined,把%h改成%{X-Forwarded-For}i,把combined改成proxy
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" proxy
# 原本CustomLog註解掉,新增一行設定format為proxy
#CustomLog logs/access_log combined
CustomLog logs/access_log proxy
```
#### 2.3 稽核儲存容量
* 依據稽核紀錄儲存需求,配置稽核紀錄所需容量 (普、中、高)
* 檢視磁碟剩餘空間和資料庫中稽核紀錄使用空間,評估配置稽核紀錄所需之儲存容量。
* PHP實作指引:
```php
<?php
// $df contains the number of bytes available on "/"
$df = disk_free_space("/");
// or on windows
$df_c = disk_free_space("C:");
```
* Oracle Tablespace 空間查詢 ([註](https://johnchen2.webnode.tw/news/oracle-tablespace-%E7%A9%BA%E9%96%93%E6%9F%A5%E8%A9%A2/)):
```plsql
SELECT a.TABLESPACE_NAME,
to_char(a.BYTES/(1024*1024),'999,999') "Size(MB)",
to_char(round((a.BYTES-b.BYTES)/(1024*1024),0),'999,999') "Used(MB)",
to_char(b.BYTES/(1024*1024),'999,999') "Avail(MB)"
FROM
(select TABLESPACE_NAME, sum(BYTES) "BYTES"
from dba_data_files
group by tablespace_name) a,
(select TABLESPACE_NAME,sum(BYTES) "BYTES"
from dba_free_space
group by tablespace_name) b
WHERE a.TABLESPACE_NAME=b.TABLESPACE_NAME;
```
* Oracle Table 使用空間查詢 ([註](https://techgoeasy.com/query-to-check-table-size-in-oracle/)):
```plsql
select
owner as "Schema"
, segment_name as "Object Name"
, segment_type as "Object Type"
, round(bytes/1024/1024,2) as "Object Size (Mb)"
, tablespace_name as "Tablespace"
from dba_segments
where segment_name='<table_name>'
and owner='<Table owner>';
```
#### 2.4 稽核處理失效之回應
* 控制措施有以下兩項:
1. 資訊系統應在稽核處理失效(如儲存容量不足)時,採取適當行動,例如:關閉資訊系統、覆寫最舊的稽核紀錄或停止產生稽核紀錄等。 (普、中、高)
2. 當機關規定需要即時通報的稽核失效事件發生時,資訊系統應在機關規定之時效內,對機關特定之人員、角色提出告警。 (高)
* 作業系統層次,採用logrotate機制
* `/etc/logrotate.conf` 設定,日誌至少保留13週 (政府組態 TWGCB-04-005-0039)。
```
# keep 13 weeks worth of backlogs
rotate 13
```
* PHP實作指引:
```php
<?php
// Import library
require_once __DIR__.'/vendor/autoload.php';
try {
// 做某些事
doSomething();
Logger::configure('config.xml');
$log = Logger::getLogger('myLogger');
// 寫入稽核日誌
$log->info("");
} catch(Exception $e) {
echo "Caught exception:". $e->getMessage()."\n";
$to = "收件人信箱";
$subject = "[XX系統告警] 稽核日誌失效!";
$msg = "something wrong";
$header = "From: 寄件人信箱"."\r\n";
// e-mail通知相關人員
mail($to, $subject, $msg, $header);
}
```
#### 2.5 時戳
* 控制措施有以下兩項:(普、中、高)
1. 資訊系統應使用系統內部時鐘產生稽核紀錄所需時戳,並可以對映到世界協調時間(UTC)或格林威治標準時間(GMT)。
2. 系統內部時鐘應具備定期同步機制。
* 實作指引:設定作業系統每日執行定時校時任務。
> ntp.org 在 2012 年時開始, 表示將讓 ntpd & sntp 來取代 ntpdate
[[註](http://support.ntp.org/bin/view/Dev/DeprecatingNtpdate)]
* 設定檔位置: `/etc/ntp.conf`,設定 `server 140.130.2.254`
```
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
#server 0.centos.pool.ntp.org iburst
#server 1.centos.pool.ntp.org iburst
#server 2.centos.pool.ntp.org iburst
#server 3.centos.pool.ntp.org iburst
server 140.130.1.254
```
* 確認ntpd服務是否啟動
1. CentOS6版本
* 查詢ntpd服務狀態 `service ntpd status`
* 啟用ntpd服務 `service ntpd start`
* 確認ntpd服務,於開機時啟用 `chkconfig ntpd on`
2. CentOS7版本
* 查詢ntpd服務狀態 `systemctl status ntpd`
* 啟用ntpd服務 `systemctl start ntpd`
* 確認ntpd服務,於開機時啟用 `systemctl enable ntpd`
* ntpstat指令:可以列出我們的 NTP 伺服器有跟上層連線否。由下述的輸出結果可以知道,時間有校正約 57 * 10^(-3) 秒,且每隔 1024 秒會主動去更新時間喔!
```
[root@wsj1 pdata]# ntpstat
synchronised to NTP server (140.130.1.254) at stratum 4
time correct to within 57 ms
polling server every 1024 s
```
#### 2.6 稽核資訊的保護
* 對稽核紀錄之存取管理,僅限於有權限的使用者。(普、中、高)
* 運用加密機制,以保護稽核資訊。(中、高)
* 產生sha512雜湊值的實作範例:
```php
<?php
require_once __DIR__.'/vendor/autoload.php';
Logger::configure('config.xml');
$log = Logger::getLogger('myLogger');
// Who - 使用者ID
$ClientUser = $_SESSION['acc'];
// What - 系統代碼 (對應t_sso_sys)
$SysID = "D05";
// What - 執行的功能 (程式檔名或ajax.php內的動作名稱)
$FuncName = "up_grade.php";
// Where - 使用者IP
$ClientIP = getClientIP();
// How - 存取的資料表(主要含有個資的資料表或者關鍵的資料表)
$DBTable = "TASTUD";
// How - 事件類型
// 身份鑑別失敗 - LF (登入失敗login faliure)
// 資源失敗 - RF (系統存取失敗resource faliure)
// 重要行為 - AC 權限新增 / AD 權限刪除 / AU 權限異動
// 重要資料異動 - C/R/U/D
// 功能錯誤 - FF
// 管理行為(如重要參數代碼表的異動) - M
$EventType = "R";
// How - 事件描述 (中文描述,使用者定義或開發者於程式流程中定義要紀錄的資訊,避免「/」字眼)
$EventDesc = "";
// How - 錯誤代碼 (開發者於程式流程中定義例外狀況的代碼)
$ErrorCode = "";
// 完整log內容
$allMsgTxt = $CleintUser."/".$SysID."/".$FuncName."/".$ClientIP."/".$DBTable."/".$EventType."/".
$EventDesc."/".$ErrorCode;
// 存成雜湊值
$enAllMsgTxt = hash('sha512', $allMsgTxt);
// Start logging
$log->info($allMsgTxt." - ".$enAllMsgTxt);
```
* 用 `hash()` 產生雜湊值,至於支援哪些演算法種類,可以用 `hash_algos()` 來查詢。
* 定期備份稽核紀錄到與原稽核系統不同之實體系統(如Log伺服器)。(高)
* 首先先編輯php.ini,設定使用syslog
```ini
error_log = syslog
```
* 接著設定syslog-ng.conf
```apacheconf
# 將紀錄傳送至10.10.10.10的5140port
destination php { tcp("10.10.10.10" port(5140))};
log { source(src); filter(f_php); destination(php); };
```
## 3 持續營運計畫
#### 3.1 資訊系統備份
* 定時同步至備份或備援環境。 (普、中、高)
* [Laravel-backup](https://github.com/spatie/laravel-backup)
#### 3.2 資訊系統備援
* 採用「高可用性(HA)」架構或「負載平衡(LB)」架構。 (高)
## 4 識別與鑑別
#### 4.1 內部使用者的識別
* 採用多因子身份鑑別。 (高)
* 資訊系統在建立連線前,應識別允許存取之特定來源(如鎖IP)。(高)
#### 4.2 身份鑑別管理
* 確實規範使用者密碼強度(GCB密碼原則或配合ISMS規範)。 (普、中、高)
* 規範密碼長度為12個字元以上、包含英文大小寫、數字,以及特殊字元。
* 在PHP中,可以使用 `preg_match()` 以及正規表示式,來檢核格式正確性。
```php
<?php
$pattern = '/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/';
if (!(preg_match($pattern, $loginpassword))) {
echo "Password must include one uppercase letter, one lowercase letter, one number, and one special character such as $ or %.";
die;
}
```
* [The Stack Overflow Regular Expressions FAQ](https://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean/22944075#22944075)
* 使用者必須定期更換密碼,具至少不可以與前3次使用過之密碼相同。 (普、中、高)
* 具備帳號鎖定機制,帳號進行身分鑑別失敗達5次後,至少15分鐘內不允許該帳號及來源IP繼續嘗試登入。 (普、中、高)
* 實作指引:參照「[PHP lockout user after 3 failed log ins for 10 minutes](https://stackoverflow.com/questions/16509643/php-lockout-user-after-3-failed-log-ins-for-10-minutes)」
* 身分鑑別相關資訊不以明文傳輸(採用HTTPS協定)。 (普、中、高)
* 身分鑑別機制,應防範自動化程式攻擊,如採用圖形驗證碼(CAPTCHA)機制。 (中、高)
* 實作指引:可使用第三方套件,如:Google reCAPTCHA。
* [在 Laravel 中使用 Google 的 reCAPTCHA](https://docfunc.com/posts/13/%E5%9C%A8-laravel-%E4%B8%AD%E4%BD%BF%E7%94%A8-google-%E7%9A%84-recaptcha-post)
* 密碼重設機制對使用者重新身分確認後,發送一次性及具時效性token,檢查傳回token有效性後,才允許使用者重設密碼。 (中、高)
```php
<?php
function uniqidReal($lenght = 13) {
// uniqid gives 13 chars, but you could adjust it to your needs.
if (function_exists("random_bytes")) {
$bytes = random_bytes(ceil($lenght / 2));
} elseif (function_exists("openssl_random_pseudo_bytes")) {
$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
} else {
throw new Exception("no cryptographically secure random function available");
}
return substr(bin2hex($bytes), 0, $lenght);
}
$token = uniqidReal($lenght = 13);
```
#### 4.3 鑑別資訊回饋
* 資訊系統應遮蔽在鑑別過程中之資訊(如密碼),以防止未授權之使用者可能之窺探/使用。
* 實作指引:密碼欄位應遮蔽(input屬性type設定為password)
```html
<input type="password" name="pwd">
```
#### 4.4 加密模組鑑別
* 密碼添加salt進行雜湊函式處理後,分別儲存亂數及雜湊後的密碼。
* md5、sha1加密的密碼,以及明碼密碼不宜儲存在資料庫中。
* md5、sha1的安全性已經不足夠了!應使用強度更高的sha-256以上的演算法。
* 實作指引:
> PHP5.5之後才支援 `password_hash()` 及 `password_verify()` ,如果使用的PHP版本是更早的版本,可以改用 [password_compat library](https://packagist.org/packages/ircmaxell/password-compat)
1. PHP提供 `password_hash()` 函式,可以產生有 salt 的密碼 ([註](https://www.php.net/manual/en/function.password-hash.php))。
```php
<?php
$options = array(
'costs' => 11,
'salt' => mcrypt_create_iv(22, MCRYPT_DEV_RANDOM)
);
echo password_hash("密碼明文", PASSWORD_BCRYPT, $options);
```
2. 驗證密碼正確性用 `password_verify()` ([註](https://www.php.net/manual/en/function.password-verify.php))。
```php
<?php
// $_POST["pwd"] ---> Is The User's Input
// $hashed_password ---> Is The Hashed Password You Can Store In Your DataBase
if(password_verify($_POST["pwd"],$hashed_password))
echo "Welcome";
else
echo "Wrong Password";
```
3. 在 Laravel Hash 內儲存的密碼使用 Bcrypt 加密方式 ([註](https://laravel.com/docs/7.x/hashing))。
```php
<?php
// 使用 Bcrypt 加密密碼
$password = Hash::make('secret');
// 對加密的密碼進行驗證
if (Hash::check('secret', $hashedPassword))
{
// The passwords match...
}
```
## 5 系統與服務獲得
> [安全軟體設計參考指引(V1.0)](https://download.nccst.nat.gov.tw/attachfilecomm/%E5%AE%89%E5%85%A8%E8%BB%9F%E9%AB%94%E8%A8%AD%E8%A8%88%E5%8F%83%E8%80%83%E6%8C%87%E5%BC%95.pdf)
#### 5.1 安全系統發展生命週期(SSDLC)需求階段
* 針對與系統安全需求,以檢核表方式進行確認。 (普、中、高)
#### 5.2 安全系統發展生命週期(SSDLC)設計階段
* 應根據系統功能與要求,識別可能影響系統的威脅,進行風險分析及評估。並將風險評估結果回饋需求階段的檢核項目,並提出安全需求修正。 (中、高)
#### 5.3 安全系統發展生命週期(SSDLC)開發階段
* SQL Injection 防範措施 (普、中、高)
* 直接組合使用者輸入變數進SQL語法是危險的!
* 透過PDO實作不同的資料庫操作介面,採用Parameter綁定的方式使用SQL語法,可避免其他無法預期的SQL行為。
* [使用prepared指令避免sql injection](https://pjchender.blogspot.com/2015/08/php-data-objects-pdo-2-preparedsql.html)
* [使用 ADOdb 預防SQL injection簡述](https://blog.travis.idv.tw/2019/04/03/php-%E4%BD%BF%E7%94%A8-adodb-%E9%A0%90%E9%98%B2sql-injection%E7%B0%A1%E8%BF%B0/)
* XSS (Cross-Site Scripting) 防範措施 (普、中、高)
* 對輸入 `<input type="text">` 、 `<input type="hidden">` 的欄位,在存入資料庫前,進行格式檢查。
* cookie有一項屬性HttpOnly。這個屬性可以禁止以JavaScript讀取出cookie。藉由這個屬性可以防止XSS攻擊之一(Session ID竊取)。在php.ini中設定:
```ini
session.cookie_httponly = On
```
* Laravel XSRF-Token 預設沒有 httponly,必須要在 `app/Http/Middleware/VerifyCsrfToken.php` 中,加入下面這段程式碼才會讓 httponly 生效
```php
<?php
use Symfony\Component\HttpFoundation\Cookie;
// 在VerifyCsrfToken類別內櫎增以下屬性及方法
protected $addHttpCookie = true;
protected function addCookieToResponse($request, $response)
{
$response->headers->setCookie(
new Cookie('XSRF-TOKEN',
$request->session()->token(),
time() + 60 * 120,
'/; samesite=strict',
null,
config('session.secure'),
true)
);
return $response;
}
```
* 在呈現資料時,PHP提供 `htmlspecialchars()` 或 `htmlentities()` 來呈現使用者儲存在資料庫的文字,可以避免XSS攻擊 (如果該欄位不是允許html語法的欄位,就該過濾) ([註](https://www.php.net/manual/en/function.htmlspecialchars.php))。
```php
<?php
echo htmlspecialchars('<a href="">測試</a>', ENT_QUOTES);
```
* 針對允許html排版語法的欄位進行避免XSS攻擊加強改善作法:
1. 在該欄位套用過濾語法的函式stripUnwantedTagsAndAttrs($html_str),讓使用者可以使用合格語法(不合格者濾掉),再丟存檔程序。
2. 被動等待使用者回饋問題及狀況,Case by Case檢視使用者送出訊息是否有異常,並提出WAF例外申請。
```php
<?php
function stripUnwantedTagsAndAttrs($html_str){
$xml = new DOMDocument();
//Suppress warnings: proper error handling is beyond scope of example
libxml_use_internal_errors(true);
//List the tags you want to allow here, NOTE you MUST allow html and body otherwise entire string will be cleared
$allowed_tags = array("html", "body", "b", "br", "em", "hr", "i", "li", "ol", "p", "s", "span", "table", "tr", "td", "u", "ul", "a");
//List the attributes you want to allow here
$allowed_attrs = array ("class", "id", "style");
if (!strlen($html_str)) {
return false;
}
if ($xml->loadHTML($html_str, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD)) {
foreach ($xml->getElementsByTagName("*") as $tag) {
if (!in_array($tag->tagName, $allowed_tags)) {
$tag->parentNode->removeChild($tag);
} else {
foreach ($tag->attributes as $attr) {
if (!in_array($attr->nodeName, $allowed_attrs)) {
$tag->removeAttribute($attr->nodeName);
}
}
}
}
}
return $xml->saveHTML();
}
```
* 跨站請求偽造(Cross-Site Request Forgery, CSRF) 防範措施 (普、中、高)
* 在行為前先動態產生獨立而唯一的request token,並於執行行為前檢查。
* 實作方案
1. [Laravel CSRF Protection](https://laravel.com/docs/9.x/csrf)
2. [How to properly add cross-site request forgery (CSRF) token using PHP](https://stackoverflow.com/questions/6287903/how-to-properly-add-cross-site-request-forgery-csrf-token-using-php)
3. [steveclifto/phpcsrftokens](https://github.com/steveclifton/PhpCsrfTokens)
* 發生錯誤時,使用者頁面僅顯示簡短錯誤訊息及代碼,不包含詳細的錯誤訊息。 (普、中、高)
* [精通 PHP 錯誤處理,讓除錯更自在 by Simon Asika](https://youtu.be/QYjklJz5ed0)
* 所有功能皆進行錯誤及例外處理,並確保將資源正確釋放。 (普、中、高)
* 程式的進入點之後,盡可能採用 `try-catch-finally` 的語法結構,捕捉可能發生的錯誤與例外狀況。
* `try-catch-finally` 的語法結構
```php
<?php
// PHP 5.5 以後增加 finally 區塊語法
try {
// do something
} catch(Exception $e) {
echo 'Exception:'.$e->getMessage()."\n";
}
// continue execution
echo 'Hello';
```
* [PHP Advanced Exceptions Tutorial](https://scoutapm.com/blog/php-advanced-exceptions-tutorial)
* 具備系統嚴重錯誤的通知機制(如:e-mail或簡訊)。 (高)
* [實務補充] Ajax請求處理端,應檢核是否循正常Ajax請求管道請求服務。
```php
<?php
/* ajaxRequest check variable */
$ajaxRequest = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') ;
//判別是否接收ajax請求,如果不是請它走開
if(!$ajaxRequest)
{
http_response_code(403);
exit(0);
}
```
#### 5.4 安全系統發展生命週期(SSDLC)測試階段
* 執行「弱點掃描」安全檢測。 (普、中、高)
* 執行「滲透測試」安全檢測。 (高)
* 「弱點掃描」v.s.「滲透測試」
比較構面 | 滲透測試(Vulnerability Scan) | 弱點掃描(Penetration Testing, PT)
----------|--------------------------------|----------------
執行方式 | 資安顧問手動進行 | 使用自動化工具對目標進行掃描
弱點種類 | 可以發現規則外或未知的潛在弱點 | 僅能根據規則發現已知弱點
攻擊手法 | 可依客戶需求,利用各式弱點攻擊 | 無法進一步利用弱點進行攻擊
誤判率 | 低 | 高
報告內容 | 例出詳細攻擊手法,並提供客製化的修補建議 | 僅陳列弱點報表,通常不包含修補建議
成本 | 高,須聘請專業顧問手動執行 | 低,有低價的商業工具或免費工具
#### 5.5 安全系統發展生命週期(SSDLC)部署與維運階段
> [政府組態基準GCB_Apache HTTP Server 2.4說明文件(V1.0)(預告版)](https://www.nccst.nat.gov.tw/GCBDownloadDetail?lang=zh&seq=1107)
* 作業平台定期更新並關閉不必要的服務及Port。 (普、中、高)
1. 作業系統自動檢查及更新(CentOS安裝及設定yum-cron)。
* [yum-cron – 自動更新 RHEL / CentOS 7 套件](https://www.opencli.com/linux/rhel-centos-7-auto-update-packages)
2. Apache移除不必要的DSO(Dynamic Shared Object)模組。以下是依照GCB停用的module:
* 00-dav.conf
```apacheconf
#LoadModule dav_module modules/mod_dav.so
#LoadModule dav_fs_module modules/mod_dav_fs.so
#LoadModule dav_lock_module modules/mod_dav_lock.so
```
* 00-base.conf
```apacheconf
#LoadModule status_module modules/mod_status.so
#LoadModule autoindex_module modules/mod_autoindex.so
#LoadModule userdir_module modules/mod_userdir.so
#LoadModule info_module modules/mod_info.so
```
* 00-proxy.conf: mod_proxy.so、mod_proxy_http.so,由於引用造字會需要使用到proxy設定,故須列入GCB例外管理清單。
```apacheconf
LoadModule proxy_module modules/mod_proxy.so
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
#LoadModule proxy_express_module modules/mod_proxy_express.so
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
```
* 00-lua.conf:關閉 mod_lua.so 的支援
> Apache軟體基金會於12月20日釋出的HTTP Server專案最新2.4.52版,最新版本除了功能更新,最主要是修補2個漏洞包括CVE-2021-44790及CVE-2021-44224。
> 其中有關 CVE-2021-44790 的部分,CVSS 3.1風險值9.8,屬重大漏洞!是和mod_lua有關的漏洞,攻擊者可傳送惡意呼叫觸發mod-lua multipart解析器的緩衝溢位,本漏洞影響Apache HTTP Server 2.4.51及以前版本。
```apacheconf
#LoadModule lua_module modules/mod_lua.so
```
* 針對系統依賴的外部元件或軟體,注意其安全漏洞通告,定期評估更新。 (普、中、高)
* 系統依賴的外部元件或軟體,不使用預設密碼或空的密碼。 (普、中、高)
* [MySQL8: How to Reset the Root Password](https://dev.mysql.com/doc/refman/8.0/en/resetting-permissions.html)
* [MySQL5.6: Assigning Account Passwords](https://dev.mysql.com/doc/refman/5.6/en/assigning-passwords.html)
```sql
-- MySQL >= 5.7.6
ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass';
-- MySQL <= 5.7.5
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('MyNewPass');
```
#### 5.6 安全系統發展生命週期(SSDLC)委外階段
> [資通系統委外開發RFP資安需求範本(V2.0)](https://download.nccst.nat.gov.tw/attachfilespmo/資通系統委外開發RFP資安需求範本(V2.0).rar)
* 資訊系統開發若委外服務應將系統發展生週期各階段依安全等級將安全需求納入委外合約。 (普、中、高)
#### 5.7 獲得程序
* 開發、測試以及正式作業環境應作區隔。 (中、高)
1. 開發人員以本機電腦為開發環境。
2. 待開發完畢則將Web應用程式部署至測試主機,並連結測試用資料庫。
3. 待測試完畢,再將web應用程式部署至正式環境,並連結至正式資料庫提供上線服務。
#### 5.8 資訊系統文件
* 應儲存與管理系統發展生命週期之相關文件。 (普、中、高)
* 以書面化或電子化形式進行文件保存,列如可建立系統專案用軟體發展目錄,儲存應被納入管理的文件。
* 相關文件包含軟體需求規格書、系統規格書、軟體發展計畫、軟體測試計畫、軟體測試報告、使用手冊、維護手冊等。
## 6 系統與通訊保護
#### 6.1 傳輸之機密性與完整性
* 機敏資料傳輸時,採用加密機制(HTTPS協定導入)。 (中、高)
* Windows設定檔位置: 在apache的目錄裡 `conf\extra\httpd-ssl.conf`
* Linux設定檔位置: `/etc/httpd/conf.d/ssl.conf`
* 至少設定為TLSv1.2 (SSLv3、SSLv2、TLSv1、TLSv1.1都是在弱點掃描時被認為是弱點的項目)
```apacheconf
SSLProtocol -all +TLSv1.2
```
* 使用公開、國際機構驗證具未遭破解的演算法 (避免使用已遭破解的加密演算法)。 (高)
* ssl.conf 的 cipherSuite 的調校。
```apacheconf
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
```
#### 6.2 資料儲存之安全
* 參數設定或系統設定存放處,限制存取或進行適當保護。 (高)
* 系統設定會集中存放在一個或少數幾個檔案(例如:config.php、.env檔、.git目錄等),而其內容無法被使用者直接存取。
* 搭配 .htaccess檔 的設定,排除使用者直接存取。
```apacheconf
# Disable index view
Options -Indexes
# Hide a specific directory
<Directorymatch "^/.*/\.git/">
Order deny,allow
Deny from all
</Directorymatch>
RedirectMatch 404 /\.git
# Hide a specific file
<Files .env>
Order allow,deny
Deny from all
</Files>
```
* 機敏資料儲存時,採用加解密機制。 (高)
* [PHP 實做 AES 資料加密(含範例)](https://cola.workxplay.net/encrypt-and-decrypt-data-in-php-using-aes-example/)
* [PHP 實做 RSA 資料加密(含範例)](https://cola.workxplay.net/encrypt-and-decrypt-data-in-php-using-rsa-example/)
## 7 系統與資料完整性
#### 7.1 漏洞修復
* 系統的漏洞修復應測試有效性及潛在影響,並律定時間週期更新。
* 定期確認系統漏洞修復之狀態。
#### 7.2 資訊系統監控
* 發現資訊系統有被入侵跡象時,應通報機關特定人員。 (普、中、高)
* 監控資訊系統,以偵測攻擊及未授權之連線,並識別資訊系統之未授權使用。 (中、高)
* 資訊系統應採用自動化工具監控進出之通信流量,並於發現不尋常或未授權活動時針對該事件進行分析。 (高)
* 實作指引:
1. 評估架設 WAF、IDS/IPS 等監控系統。 (註:目前已導入WAF、IPS)
2. 評估自行建置或委外「資訊安全監控中心(Security Operation Center, SOC)」,以進行監控服務。
#### 7.3 軟體及資訊完整性
* 於伺服器端以正規表示式(Regular Expression)方式,檢查使用者輸入資料合法性。 (中、高)
* [PHP正規表達式比對](https://medium.com/verybuy-dev/php%E6%AD%A3%E8%A6%8F%E8%A1%A8%E9%81%94%E5%BC%8F%E6%AF%94%E5%B0%8D-89b03ebc10eb)