Wesker
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 資訊安全處理紀錄 (DotNet Security) ## SQL Injection >注入攻擊實作: >在 Textbox 內輸入字串內容 ` ' OR '1'='1 ` >或是 ` '; DROP TABLE Employees; ` **防範措施:** - 參數化查詢。而不是使用「組字串」的方式來執行SQL。 - 對輸入的數據進行驗證,以確保數據符合預期的格式或範圍。 >[什麼是 SQL Injection?該如何避免?](https://www.explainthis.io/zh-hant/swe/sql-injection) ## Poor Error Handling: Unhandled Exception 在程式碼中,應該改善錯誤處理,以避免未處理的例外情況,並提供更友好和有用的錯誤訊息。 **防範措施:** - 在程式碼中使用 try-catch 區塊,以捕獲可能發生的例外情況。 - 在 catch 區塊中,應該適當地記錄錯誤訊息,並向使用者提供有用的錯誤訊息,以便他們理解問題所在。 - 可以使用 ASP.NET 的全局錯誤處理事件(Application_Error)來捕獲未處理的例外情況,並將它們記錄下來或顯示自定義的錯誤頁面。 這些修改將幫助改善網頁伺服器的配置安全性和錯誤處理,使程式碼更堅固和安全。請按照上述建議進行修改後,重新審查程式碼並測試確保正常運作。 ## Cross-Site Scripting: Reflected 跨站腳本攻擊(Cross-Site Scripting, XSS):反射型 >黑箱掃描工具的攻擊實作: > >- `https://xxx.com/InventoryMgmt/Home/(A(WeBiNsPeCt))/Index` >- `https://xxx.com/InventoryMgmt/Home/(A())/Index` > >正常的網址應該是 > >- `https://xxx.com/InventoryMgmt/Home/Index` **防範措施:** 在 Global.asax.cs 內加入程式嗎 ```csharp= protected void Application_BeginRequest(object sender, EventArgs e) { // 到這時,Url 已被改成沒包含 SessionId // 但它的值會放在 Response._appPathModifier 變數之中 FieldInfo appPathModifierFieldInfo = Context.Response.GetType().GetField("_appPathModifier", BindingFlags.NonPublic | BindingFlags.Instance); object appPathModifier = appPathModifierFieldInfo.GetValue(Context.Response); if (appPathModifier != null) { // Url 中有 SessionId throw new HttpException(404, "Not found"); } // deny HTTP GET with request body if (Request.HttpMethod == "GET" && Request.ContentLength > 0) { throw new HttpException(403, "Forbidden"); } } ``` >- [為什麼 Url 中有 (A(XXXX)) 在 ASP.NET 中,卻不會噴 404 或是錯誤,反而回正常的頁面(200)呢?](https://rainmakerho.github.io/2020/11/11/aspnet-cookieless-url/) ## Reflected XSS All Clients 主要透過用戶發出惡意的請求,倘若後端沒有過濾而直接將結果回傳前端的話,就有可能執行到惡意的程式碼。 **防範措施:** ```csharp= using Ganss.Xss; // NuGet 加入 HtmlSanitizer 套件 #region XSS 的第一道防線:Sanitization (AntiXSS,HtmlSanitizer) /// <summary> /// 通過使用 HtmlSanitizer 來過濾輸入字串,再 HtmlEncode 以防止跨站腳本(XSS)攻擊。 /// </summary> public static string SanitizeHtmlEncode(string inputStr) { if (string.IsNullOrWhiteSpace(inputStr)) return string.Empty; var sanitizer = new HtmlSanitizer(); //sanitizer.AllowedAttributes.Add("class"); //sanitizer.AllowedAttributes.Add("id"); //sanitizer.AllowedSchemes.Add("mailto"); // 允許 <a href="mailto:" // 使用 HtmlSanitizer 進行消毒處理 (https://github.com/mganss/HtmlSanitizer) string sanitized = sanitizer.Sanitize(inputStr); string encoded = HttpUtility.HtmlEncode(sanitized); return encoded; } /// <summary> /// 在數據輸出到網頁時進行欄位內容過濾,以防止儲存型跨網站指令碼(Stored XSS)攻擊。 /// </summary> public static DataTable SanitizeHtmlEncodeInDataTable(DataTable dataTable) { foreach (DataRow row in dataTable.Rows) { foreach (DataColumn column in dataTable.Columns) { if (row[column] != DBNull.Value && !column.ReadOnly) { row[column] = SanitizeHtmlEncode(row[column].ToString()); } } } return dataTable; } #endregion XSS 的第一道防線:Sanitization (AntiXSS,HtmlSanitizer) ``` ## Reflected XSS Specific Clients **防範措施:** 同 Reflected XSS All Clients ## Stored XSS 被保存在資料庫中的 Javascript 引起的攻擊稱為 Stored XSS。 最常見的就是文章、留言等,因為用戶可以任意輸入內容,若沒有檢查,則 `<script>` 等標籤就會被視為正常的 HTML 做執行。 **防範措施:** Reflected XSS All Clients 的 `SanitizeHtmlEncodeInDataTable` 方法 ## Code Injection >漏洞描述: >用 Invoke 動態執行 Web service URL **防範措施:** 透過加入 Web 參考,以靜態的方式連線 Web service ## Cross-Frame Scripting >攻擊實作: >[Clickjacking 點擊劫持攻擊](https://blog.huli.tw/2021/09/26/what-is-clickjacking/) **防範措施:** - [X-Frame-Options](https://a42033.gitbooks.io/system/content/security/user/X_Frame_Options.html) ```xml <add name="X-Frame-Options" value="SAMEORIGIN" /> ``` - 內容安全策略(Content-Security-Policy, CSP) ```xml <add name="Content-Security-Policy" value="child-src 'self';" /> ``` ```xml! <add name="Content-Security-Policy" value="frame-ancestors 'self' tw.yahoo.com www.google.com;" /> ``` iframe_test.html 舉證、驗證 ```html= <!DOCTYPE html> <html> <head> <title>iframe test</title> </head> <body> <h1>iframe test</h1> <iframe src="要嵌入的網頁網址" width="600px" height="400px" frameborder="0" scrolling="no"></iframe> </body> </html> ``` ## HTML5: Missing Content Security Policy HTML5 引入了 Content-Security-Policy(CSP),這是一種用於增強網站安全性的機制。 當網站沒有設定 Content-Security-Policy 時,就稱為 Missing Content Security Policy。 **防範措施:** - [內容安全策略(Content-Security-Policy, CSP)](https://content-security-policy.com/) >Google 提供的網站:[CSP Evaluator](https://csp-evaluator.withgoogle.com/),它會偵測你的 CSP 是否有錯誤,以及是不是安全。 > >[XSS 防禦 - CSP script-src 設定](https://blog.darkthread.net/blog/csp-script-src/) ## HTML5: Overly Permissive Message Posting Policy HTML5 的新機制 Message Posting Policy 主要是指跨文件通訊的設定規則。這個機制允許使用 Script 腳本在不同窗口或框架之間傳送消息。在設定這個機制時,使用者可以指定目標窗口的來源,以確保只有特定合法來源的窗口才能接收消息。 當 Message Posting Policy 過於寬鬆時,也就是認為了使用不當而導致安全風險的情況下,就稱為 Overly Permissive Message Posting Policy。 JavaScript/TypeScript: ```javascript o.contentWindow.postMessage(message, '*'); ``` 使用 `*` 做為目標來源值,代表 Script 會傳送訊息至視窗而不論其來源。 **防範措施:** - 不要使用萬用字元 `*`,改為指定目標窗口的來源。 ## HTML5: CORS Functionality Abuse 同 Cache Management: Headers ## Web Server Misconfiguration: Insecure Content-Type Setting **防範措施:** ```xml <add name="X-Content-Type-Options" value="nosniff" /> ``` ## Insecure Transport: HSTS not Set HTTP Strict-Transport-Security 回應標頭 (HSTS) 告知瀏覽器該站點應僅使用 HTTPS 訪問,並且所有將來的 HTTP 訪問應自動轉換為 HTTPS。 **防範措施:** ```xml <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" /> ``` ## ASP.NET Misconfiguration: Missing Error Handling Web.config 檢測到 ```xml <customErrors mode="Off" /> ``` **防範措施:** ```xml <customErrors mode="On" defaultRedirect="Errors/ErrorPage.aspx"> <error statusCode="404" redirect="Errors/NotFound.aspx" /> <error statusCode="500" redirect="Errors/ServerError.aspx" /> </customErrors> ``` ## Compliance Failure: Missing Privacy Policy 對掃描範圍內可存取的所有網頁進行採樣,以取得通常構成隱私權政策聲明的文字內容。 **解決方法:** - 新增隱私權政策(Privacy Policy)的靜態頁面。 ## Cache Management: Headers ```xml <!-- CORS_Origins 因應資訊安全「同源政策」,請加入主機網域網址(含開發機, 測試機, 正式機)。若未加入會顯示 HTTP ERROR 403 --> <add key="CORS_Origins" value="http://主機網域網址1, https://主機網域網址1, http://主機網域網址2, https://主機網域網址2" /> ``` ```csharp= protected void Application_BeginRequest(object sender, EventArgs e) { /** 修復資訊安全漏洞 * Cache Management: Headers * HTML5: CORS Functionality Abuse */ string cors_origins = ConfigurationManager.AppSettings["CORS_Origins"] ?? ""; // 允許的來源列表 var allowedOrigins = new List<string>(cors_origins.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)); // 去除字串前後的空白 allowedOrigins = allowedOrigins.ConvertAll(o => o.Trim()); HttpContext context = HttpContext.Current; string origin = context.Request.Headers["Origin"]; // 如果是非跨域請求(沒有 Origin),跳過 CORS 驗證 if (string.IsNullOrEmpty(origin)) { return; // 直接返回,不執行 CORS 邏輯 } // 檢查來源 if (allowedOrigins.Contains(origin)) { // 設置 CORS 標頭 context.Response.AddHeader("Access-Control-Allow-Origin", origin); context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With"); context.Response.AddHeader("Access-Control-Allow-Methods", "POST, OPTIONS"); } else { context.Response.StatusCode = 403; // 禁止訪問 //Logger.Warn($"未授權的 CORS 來源: {origin}"); context.Response.End(); return; } // 處理 OPTIONS 方法 if (context.Request.HttpMethod == "OPTIONS") { context.Response.End(); } } ``` ## Cache Management: Insecure Policy 不安全的快取策略可能允許攻擊者進行內容欺騙或資訊竊取攻擊。 >瀏覽器 F12 進入開發人員畫面 => 選擇 Network => 選擇網頁名稱 >可看到 Response Headers 的 Cache-Control: 內容為 no-cache, no-store **防範措施:** - ASP.NET MVC 在 Global.asax.cs 內加入 ```csharp= protected void Application_PreSendRequestHeaders(object sender, EventArgs e) { HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache); HttpContext.Current.Response.Cache.SetNoStore(); } ``` - ASP.NET WebForms 在 Web.config 內加入 ```xml= <system.webServer> <httpProtocol> <customHeaders> <add name="Cache-Control" value="no-cache, no-store, must-revalidate" /> <add name="Pragma" value="no-cache" /> <add name="Expires" value="0" /> </customHeaders> </httpProtocol> </system.webServer> ``` ## HTML5: Cross-Site Scripting Protection **防範措施:** ```xml <add name="X-XSS-Protection" value="1; mode=block" /> ``` ## Web Server Misconfiguration: Server Error Message Web 伺服器配置不當(Web Server Misconfiguration)是指在設置和管理 Web 伺服器(如 IIS)時出現的錯誤或不正確的配置。 比如 HTTP Response Headers 配置。 ## CSRF >[零基礎資安系列(一)-認識 CSRF(Cross Site Request Forgery)](https://tech-blog.cymetrics.io/posts/jo/zerobased-cross-site-request-forgery/) >想像你到一家餐廳吃飯,陌生人`(駭客)`拿了一張有你桌號的菜單`(Request)`點餐之後給老闆`(Server)`,結果老闆問也不問便收了菜單並將帳記到了你的身上,這就是 CSRF 的基礎概念。 >[如何防範?](https://www.explainthis.io/zh-hant/swe/what-is-csrf#csrf-%E9%98%B2%E7%A6%A6%E6%96%B9%E6%B3%95) >1. 加上驗證 >2. 不要用 `GET` 請求來做關鍵操作 >3. 檢查 Referrer >4. 使用 Anti-XSRF Token >5. 瀏覽器本身防護 - SameSite cookies **實際的防範措施:** - 不要使用 `GET` 請求來做關鍵操作 ```csharp= if (!IsPostBack) { } else { // CSRF 防護:限制僅接受 POST 請求,防止 GET 請求觸發狀態變更。 if (Request.HttpMethod != "POST") { throw new InvalidOperationException($"資訊安全:{Request.HttpMethod} 為不允許的請求方法。僅接受 POST 請求。"); } } ``` - [使用 Anti-XSRF Token](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html#asp-net-web-forms-guidance) ```csharp== private const string AntiXsrfTokenKey = "__AntiXsrfToken"; private const string AntiXsrfUserNameKey = "__AntiXsrfUserName"; private string _antiXsrfTokenValue; protected void Page_Init(object sender, EventArgs e) { // 從請求的 Cookie 中獲取 Anti-XSRF Token var requestCookie = Request.Cookies[AntiXsrfTokenKey]; if (requestCookie != null && Guid.TryParse(requestCookie.Value, out Guid requestCookieGuidValue)) { // 如果 Cookie 中存在有效的 Anti-XSRF Token,則使用該 Token _antiXsrfTokenValue = requestCookie.Value; Page.ViewStateUserKey = _antiXsrfTokenValue; } else { // 如果 Cookie 中不存在 Token,則生成新的 Anti-XSRF Token 並保存到 Cookie _antiXsrfTokenValue = Guid.NewGuid().ToString("N"); Page.ViewStateUserKey = _antiXsrfTokenValue; var responseCookie = new HttpCookie(AntiXsrfTokenKey) { HttpOnly = true, // 設置 HttpOnly 屬性以防止客戶端腳本訪問 Cookie Value = _antiXsrfTokenValue }; // 如果需要 SSL 且當前連接是安全的,則設置 Cookie 的 Secure 屬性 if (FormsAuthentication.RequireSSL && Request.IsSecureConnection) { responseCookie.Secure = true; } // 將新的 Cookie 添加到回應中 Response.Cookies.Set(responseCookie); } } protected void Page_PreLoad(object sender, EventArgs e) { if (!IsPostBack) { // 設置 ViewState 中的 Anti-XSRF Token 和使用者名稱 ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey; ViewState[AntiXsrfUserNameKey] = Context.User.Identity.Name ?? string.Empty; } else { // 驗證 ViewState 中的 Anti-XSRF Token 和使用者名稱 if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue || (string)ViewState[AntiXsrfUserNameKey] != (Context.User.Identity.Name ?? string.Empty)) { throw new InvalidOperationException("Validation of Anti-XSRF token failed."); } } } ``` - 調整程式碼執行流程 ```csharp= //string logonid = HttpUtility.HtmlEncode(Request.QueryString["logonid"] ?? ""); if (!IsPostBack) { string logonid = HttpUtility.HtmlEncode(Request.QueryString["logonid"] ?? ""); string NTUser = Utility.DecryptNTUser(logonid); ``` ## Data Filter Injection 使用 DataTable.Select 方法進行查詢時,當查詢條件是通過**字符串拼接**產生的(如 "ID='" + id + "'"),就存在 Data Filter Injection 的風險。 >攻擊場景: >假設攻擊者將 id 設置為 `1' OR '1'='1`. >查詢語句將被解析為:ID='1' OR '1'='1',這樣會返回所有行,從而泄露不應該訪問的數據。 ```csharp= // 資訊安全漏洞:Data Filter Injection //DataRow[] dr = dt.Select("ID='" + id + "'"); // 修復:改用 LINQ 查詢 DataRow[] dr = dt.AsEnumerable() .Where(r => r.Field<decimal>("ID").ToString() == id) .ToArray(); ``` ## Persistent Connection String ## Client Potential XSS **防範措施:** - 前端使用 [DOMPurify](https://github.com/cure53/DOMPurify) 避免 XSS 攻擊 ```javascript= $('.inject-defense').each(function () { var inputVal = $(this).val(); if (inputVal) { var cleanHTML = DOMPurify.sanitize(inputVal, { USE_PROFILES: { html: false } }); $(this).val(cleanHTML); } }); ``` ## Privacy Violation: Autocomplete **防範措施:** `<asp:TextBox>` 和 `<input type="text">` 的元素都加上 `autocomplete="off"` ## Privacy Violation >[Checkmarx | 使用 DefaultRequestHeaders.Authorization 卻被 Checkmarx 判斷有 Privacy Violation 的 Issue](https://rainmakerho.github.io/2022/11/25/checkmarx-headers-authorization-privacy-violation/) **防範措施:** 將變數名稱 `string userAccount` 調整成 `string user`。 ## Insufficient Connection String Encryption ## Path Traversal/Stored Path Traversal ```csharp= XmlReaderSettings settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit, // 禁用 DTD XmlResolver = null, // 禁用外部資源解析 IgnoreWhitespace = true, // 忽略空白 IgnoreComments = true // 忽略註解 }; XmlDocument xmlDoc = new XmlDocument(); using (var reader = XmlReader.Create(new StringReader(vendorData), settings)) { xmlDoc.Load(reader); } ``` ## Missing HSTS Header HTTP Strict-Transport-Security 回應標頭 (HSTS) 告知瀏覽器該站點應僅使用 HTTPS 訪問,並且所有將來的 HTTP 訪問應自動轉換為 HTTPS。 **防範措施:** ```xml <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" /> ``` ## SSL Verification Bypass >```csharp >ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; >``` > >這段程式碼會禁用所有 SSL 憑證的驗證,無論伺服器的憑證是否可信都會自動接受。這可能導致攻擊者進行中間人攻擊 (MITM),攔截和篡改 HTTPS 傳輸中的敏感資料。 **修復方法:** 移除不安全的憑證驗證邏輯 ```csharp ServicePointManager.ServerCertificateValidationCallback = null; ``` ## Often Misused: File Upload **防範措施:** - 前端對上傳的檔案進行副檔名限制 ```html <asp:FileUpload ID="oFileUpload" runat="server" accept=".xlsx,.xls" /> ``` - 後端對上傳的檔案進行副檔名、content-type 檢查 ```csharp= // 取得檔案副檔名 string fileExtension = Path.GetExtension(oFileUpload.FileName); // 取得 MIME 類型 string contentType = oFileUpload.PostedFile.ContentType; if (fileExtension.ToLower() != ".xls" && fileExtension.ToLower() != ".xlsx" && contentType != "application/vnd.ms-excel" && contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { Utility.Alert("僅允許上傳 Excel (.xls, .xlsx) 檔案。", this, false); return; } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully