最近遇到一個防呆驗證問題,
一個檔案上傳的功能,即使有了副檔名偵測,也會有可能遇到有心人故意弄非正規檔案傳入,
那該怎麼在後端去防呆呢?

找到了以下這篇Wiki,統整了大部分檔案的File Signature (檔案特徵)
List of file signatures
其中Hex signature欄位是主要需要的資訊,可以利用讀取檔案的前幾個Byte,然後比對公版的檔案特徵值,判斷是否符合來防呆這個檔案不是允許的
這裡我只需要用到JPG、GIF、PNG的標頭就好

要注意這份清單是不固定的,可能會誕生新的格式,或者有漏,但是就是一個參考,也可以去個別檔案的規格文件查看file signature章節來設定

有些檔案會有多種標頭格式,我找到的驗證做法是取前面幾個相同的部分驗證,後面不同的部分就不驗證
例如jpg就有四種:
FF D8 FF DB
FF D8 FF E0 00 10 4A 46 49 46 00 01
FF D8 FF EE
FF D8 FF E1 ?? ?? 45 78 69 66 00 00
所以乾脆只比對前面的 FF D8

實作的話則是參考這篇stackoverflow解答:
Validate image from file in C#
主要概念是把檔案的Byte傳入方法後,根據設定好的規格長度取出byte,用SequenceEqual比對是否相符

我先建立一個通用的圖檔類型enum:

1
2
3
4
5
6
public enum ValidImageTypeEnum
{
Jpeg,
Gif,
Png
}

然後作一個根據這個enum回傳指定的byte array的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static IEnumerable<byte[]> GetImageValidateHeaders(ValidImageTypeEnum[] fileTypes)
{
foreach (var ft in fileTypes)
{
switch (ft)
{
case ValidImageTypeEnum.Jpeg:
yield return new byte[] { 0xFF, 0xD8 };
break;
case ValidImageTypeEnum.Gif:
yield return Encoding.ASCII.GetBytes("GIF");
break;
case ValidImageTypeEnum.Png:
yield return new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
break;
default:
break;
}
}
}

這裡用16進位是方便比對wiki清單,原本解答中的是放10進位的數字

再做一個把圖片byte array傳入,比對是否為圖片格式的header擴充方法

1
2
3
4
public static bool IsImage(this byte[] fileBytes, IEnumerable<byte[]> headers)
{
return headers.Any(x => x.SequenceEqual(fileBytes.Take(x.Length)));
}

然後使用上就是當你在操作Stream時把前幾個byte讀取出來後送來驗證

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
IEnumerable<byte[]> validImageFileHeaders = 
ValidationHelper.GetImageValidateHeaders(new ValidateImageFileTypeEnum[]
{
ValidateImageFileTypeEnum.Gif,
ValidateImageFileTypeEnum.Jpeg,
ValidateImageFileTypeEnum.Png
});

using (var stream = file.Stream)
{
//buffer 我是設定一個很小的值,不用讀取檔案全部的內容
byte[] fileHeaderBuffer = new byte[16];
int num = stream.Read(fileHeaderBuffer, 0, 16);
if (num == 0 || !fileHeaderBuffer.IsImage(validImageFileHeaders))
{
throw new ArgumentException("圖片不是正常格式");
}

//重設stream的讀取位置以上傳,很重要不然後面上傳處理會變空檔案
stream.Position = 0;

//Do your work
}

再來就是一個很重要的

1
stream.Position = 0;

我在判斷完檔案標頭之後一直遇到圖檔上傳都變空的,查了一下才知道,
因為Stream在讀取時會改變他的讀取位置,
所以後面要繼續使用同一個Stream時必須重設位置,這樣後面在讀取時才不會從中間或尾巴讀取導致變空的檔案
這個問題卡了一小時,關鍵字不太會下…

有個線上的 Azure Web app 站台要導入 WAF,選擇用 Azure Front Door,然後再掛 WAF,
在上線演練時使用交大 nctu.me 免費網域,搭配sslforfree
來達到現行網站擁有自有網域以及自有 SSL 憑證這個情境,
在測試的訂閱帳號下操作,可以達到自動驗證通過,而不需要手動驗證,
但是在上線前開始建立資源時發現網域認證無法自動通過,拖了好幾天仍沒反應,
開了 Support ticket 詢問,得到的答案是如果使用afdverify網域的話,就一定得用手動驗證,
一細看文件,還真的是這樣

如果該 CNAME 記錄仍然存在且不包含 afdverify 子網域,則 DigiCert 憑證授權單位會使用它來自動驗證您自訂網域的擁有權。

也就是你要馬擁有那些域設信箱地址能夠收信點擊連結,要馬就是聯絡客服來處理。
那為什麼演練的時候可以自動驗證通過呢?
其實 Azure 客服他也只能做確認文件,然後轉詢問 Digicert 客服,
但結論是,照理來說就是不會自動驗證通過,我演練時遇到的情況算是特殊情況,沒有確切的答案。

至於手動驗證時,若都沒有文件上列出的信箱地址,還有一個方式可以驗證,
要跟 Azure 客服求救,他再轉 Digicert 客服,拿到一個 TXT record,把他加入到 DNS 裡面,
再通知 Azure 客服,他再轉 Digicert 客服手動驗證,就可以通過網域所有權驗證了
這個 TXT record 據客服說,一般來說是一個月內有效,
也就是如果像我這個情況中,因為網域管理非我所能控制,中間來回時間較久,會超過 Azure Front Door 受控憑證的 6 天期限,
也只要過期後重新按認證就好了,不用再去要一次新的 TXT record。

最近都改用 hexo 寫筆記丟到 github 上,不過沒有很頻繁寫常常忘記指令,
寫一篇給自己用
安裝跟設定 git 遠端推到 github 等的做法很多大大寫過文章了


寫作

hexo new post <title> 產生一篇文章的 md 檔

本機檢視

hexo server 在本機起 server 看 BLOG,起來之後可以編輯 md 檔,存檔後重整會即時更新

發佈

hexo cl 清除所有產生的靜態檔案
hexo g 產生靜態檔案
hexo d 佈署

windows 本機debug時使用NPOI匯出EXCEL都好好的
但是打包丟去docker ubuntu container裡面時就會跳出
Exception thrown: System.TypeInitializationException' in ZKWeb.System.Drawing.dll

原因也不難找,查了一下就是因為linux環境下還需要另外安裝相依的套件libgdiplus
然後其實這個repo裡面也有提到要裝
https://github.com/dotnetcore/NPOI

於是在dockerfile裡面加入一行安裝指令就解決了

1
2
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
RUN apt-get update && apt-get install -y libgdiplus

ref: https://www.cnblogs.com/weihanli/p/use-npoi-in-docker-alpine.html

有個需求是要塞生日月份,因為欄位用了月份的Enum以供產生下拉選單,
但測試時發現塞錯誤值進去也不會跳ModelState error,
google了一下找到這篇參考,使用EnumDataType可以達成驗證目的
https://stackoverflow.com/a/42568074/9520752

1
2
3
4
5
public class ViewModel
{
[EnumDataType(typeof(MyEnum), ErrorMessage = "請選擇正確的值")]
public MyEnum Property { get; set; }
}

最近看到 elmah 收到奇怪的錯誤訊息

A potentially dangerous Request.Path value was detected from the client (?)

看網址,路由也正常,path?querystringname=querystringvalue這樣的格式
後來查了一下
原來要注意的是HTTP_X_WAWS_UNENCODED_URL這個欄位
可以發現他丟過來的是邊碼過的問號符號%3F

所以實際上並不符合查詢參數格式,就判斷為危險字元了

丟這個的還是 AdsBot-Google (+http://www.google.com/adsbot.html)
WTF…

有個需求是要自動播一個 mp4 影片在活動網頁頂部
在 mobile 上也要自動播
影片本身沒有音效需求,只是影像

將 video tag 設定了autoplay 自動播放、 muted 靜音,
在 android 的 chrome 上是會自動播放,但是在 ios 的 safari & chrome 上無效,

根據這篇文章應該是靜音就可以了,
嘗試拿免費軟體shotcut將影片檔重新匯出無音軌的,也沒有用

後來看 apple 的官網文章
https://developer.apple.com/documentation/webkit/safari_tools_and_features/delivering_video_content_for_safari#3030250

還要再加上一個 playsinline 屬性才行

加上了之後的確可以了,不需要無音軌影片,靜音即可

0%