2023年5月6日 星期六

Updates regarding IIS and SameSite attribute

 Here is a post on the recent updates regarding IIS and SameSite attribute.

Recently, Microsoft announced that IIS can now be configured to use the SameSite attribute directly in the Web.config file. This means that IIS can now be configured to use the SameSite attribute without needing to use any code.

In the latest versions of IIS, all that is needed is to add the following configuration to the system.web node in the Web.config file to set the SameSite attribute to Strict:

```

<system.web>

  <httpCookies requireSSL="true" httpOnlyCookies="true" sameSite="Strict" />

</system.web>

```

This code sets the `requireSSL` attribute to `true`, which means that the cookie can only be accessed using the HTTPS protocol. It also sets the `httpOnlyCookies` attribute to `true`, which prevents client-side scripts from accessing the cookie through the `document.cookie` API. Finally, the `sameSite` attribute is set to `Strict`, which means that the cookie will only be sent to the website if the requested URL exactly matches the hostname of the website.

One of the benefits of using the Web.config file to configure the SameSite attribute is that it provides a centralized location for managing all the settings for the website. This simplifies the maintenance and management of the code, and increases the security and stability of the website. Additionally, because the Web.config file is typically stored in the root directory of the website, it is easy to deploy and manage the website.

It is important to note that the ability to configure the SameSite attribute using the Web.config file requires IIS version 10.0.17763.0 or higher. If your IIS version is older than this, you will need to use a different method to configure the SameSite attribute. Also, it is recommended that you carefully read Microsoft's official documentation to ensure that your configuration is correct and effective.

In summary, using the Web.config file to configure the SameSite attribute is a simple and effective way to increase the security and stability of your website. If you have not yet configured your website to use this feature, it is recommended that you update to the latest version of IIS and follow Microsoft's official guidelines for configuration.

2023年5月2日 星期二

Set the SameSite attribute of all cookies in every response

 When users browse a website, cookies may be used to store user settings or information for later use. However, cookies can also be a potential security risk, especially when they are used in cross-site request forgery (CSRF) attacks. To protect user security and privacy, modern web applications need to strictly control cookie usage and limit their use in cross-site requests.

ASP.NET is a popular web application framework that can be used to develop web applications using the C# language. In ASP.NET, an event handler can be used to set the SameSite attribute of all cookies in every response. Here's an example code:
```csharp
protected void Application_EndRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Response.Cookies != null)
{
foreach (var cookie in HttpContext.Current.Response.Cookies)
{
if (cookie is HttpCookie httpCookie)
{
httpCookie.SameSite = SameSiteMode.Strict;
}
}
}
}
```
In this code snippet, we can see the Application_EndRequest event handler in ASP.NET, which is an event that is fired at the end of each request. In this event handler, we check if the current response contains any cookies, and if so, we set their SameSite attribute to Strict.
SameSite is a security attribute of cookies that controls their behavior. The Strict value means that cookies can only be sent in a same-site context and not in any cross-site context. This helps protect websites from CSRF attacks since attackers cannot send requests with cookies without the victim's knowledge.
By setting the SameSite attribute to Strict, this code helps improve the security of an ASP.NET application by ensuring that cookies are only sent in a safe and controlled manner.
To use this code to set the SameSite attribute of cookies to Strict on an entire IIS website, simply add this code to the global.asax file of the ASP.NET web application.
In ASP.NET, the global.asax file is a core file of a web application that can execute initialization code at application startup and can set global-level event handlers. In this case, we can define an Application_EndRequest event handler in global.asax to set the SameSite attribute of cookies to Strict in all responses.
To add this code to global.asax, follow these steps:
1. Open the Visual Studio development environment and open your ASP.NET web application project.
2. In the Solution Explorer, find the file named "Global.asax."
3. Double-click the file to open it and add the following code:
```csharp
protected void Application_EndRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Response.Cookies != null)
{
foreach (var cookie in HttpContext.Current.Response.Cookies)
{
if (cookie is HttpCookie httpCookie)
{
httpCookie.SameSite = SameSiteMode.Strict;
}
}
}
}
```
4. Save the file and restart the web application.
Now, your ASP.NET web application will automatically set the SameSite attribute of all cookies in every response to Strict at the end of each request. This helps protect your website from CSRF attacks and enhances user security and privacy.
Note that if your web application needs to share cookies with other sites or needs to pass cookies in cross-site requests, setting the SameSite attribute to Strict may cause issues. In this case, you can set SameSite to Lax to achieve more lenient restrictions while still protecting your website from most CSRF attacks.
In summary, ASP.NET provides a simple and effective way to enhance web application security by setting the SameSite attribute of all cookies in every response at once. This helps protect user privacy and security and makes your website more secure and reliable.

web.config add sameSite="Strict"
<system.web> <httpCookies requireSSL="true" httpOnlyCookies="true" sameSite="Strict" />
</system.web>

2020年4月27日 星期一

工商時間─幫忙推薦公部門會計室常用資源系統

Accounting Office簡稱AO,是新北市教育局會計室委託科碩資訊有限公司建置開發的一套會計室常用資源(Web Application)。使用ASPX解決方案建置系統,伺服器端使用C#為主的程式語言;使用者端使用ASPX Tool Kit和Jquery技術;報表產出嵌入CrystalReports元件。AO系統共包含8大平台2大管理系統,8大平台包含:報表填報平台、訊息平台、經費收支管理平台、年度作業紀錄平台、專案經費收支平台、檔案彙整平台、基金賸餘滾存平台、資源統計分析平台;其中基金賸餘滾存平台於106年度擴建、資源統計分析平台於109年度擴建。


由於AO最近解決了系統上的2大困擾,讓系統的穩定性與可用性更令人激賞(哈哈,老王賣瓜自賣自誇),最近系統優化機制說明如下:

一、由於嵌入CrystalReports報表系統,此系統必須內含log4net元件,此元件為Apache公司早期使用了x86 CPU模式建置的開源軟體。啟用此元件使得IIS運用程式集區必須『啟用32位元應用程式』;啟用32位元應用程式的結果讓龐大的系統架構常出現out of memory的窘境。網路一查發現遇到此問題的文章還真不少,解決方案也五花八門、綜說紛紜,幾經折騰後終於找到快速而有效的解決方案。方法很簡單:只需移除Web Application bin區的log4net.dll,在Windows Server GAC安裝新版的log4net(MSIL版),就可以直接使用IIS整合模式應用程式,免啟用32位元應用程式。

二、面對會計資料數量的快速成長,查詢語言的優化是必須的,稍有不慎會讓使用者出現等待資料的窘境。面對此問題,AO系統GridView的資料繫結策略,改以ObjectDataSource元件代替SqlDataSource,透過客製化的開發讓龐大資料的查詢與顯示,可以快速簡潔的只取得顯示區的資料量,優化使用者經驗;就算是資料量成成長至數百萬筆,也可以順暢查覽操作系統(痛快)。


說了這麼多,各位看官可能會以為這只是一套未產品化的專案系統,為什麼需要推薦呢?重點來了,經過多年的努力,AO系統不但所有Table的建置程式化;所有的預設資料也可以客製化啟動後重新匯入;網站主題也可以整組更換、更是支援多種認證機制。所以只要是公部門的會計室都可以使用AO系統,讓會計工作經常遇到的難題:蒐集資料、控帳無法集中、資源宏觀數據取得,都可以透過本系統快速而正確的取得;讓Accounting Office雲端化、數位化、集中化。

2017年11月18日 星期六

網頁輸入頁面Enter鍵處理元件(KeyDownProcess Version:1.65)

function KeyDownProcess(e) {
    //-----------------------------------------------------------
    //網頁親和性輸入控制元件
    //元件功能:可以讓網頁資料的輸入更親切,功能如下:
    //1.按下Enter鍵自動找到下一個可輸入輸入元件
    //2.按下Enter鍵自動找尋submit按鍵,如果找到則模擬Click的功能
    //3.當輸入元件設定唯讀時,按下backspace鍵時,防止網頁轉址
    //4.支援無障礙導覽設定
    //5.支援Firefox、Google Chrome瀏覽器
    //6.支援按下shift鍵全域變數ShiftKeyDown(可實作連選)
    //7.支援頁面元件已輸入資料偵測(全域變數:CursorUserChanged CursorUserChangedDetect 可實作window.onbeforeunload confirm)
    //版本:1.65(2016.11.18)-Microsoft Visual Studio 2017 調校後版本(V1.00)
    //作者:科碩資訊有限公司
    //引用本元件,請保留作者和版本資訊
    //連絡mail:cursor@cursorinfo.com.tw
    //------------------------------------------------------------
    w3cEvent = e;
    ShiftKeyDown = false;
    _beforeunloadTarget = '';
    CursorUserChanged = false;
    var kv = 0;
    var rv = false;
    var isTextarea = false;
    var currentId = null; //目前元件
    var ex;
    var isAll = typeof (document.all) === "object";
    if (isAll) {
        ShiftKeyDown = event.shiftKey;
        ex = event;
        kv = ex.keyCode;
        isTextarea = (ex.srcElement.tagName === "TEXTAREA");
        currentId = ex.srcElement.id === undefined ? '' : ex.srcElement.id;
    }
    else {
        ShiftKeyDown = e.shiftKey;
        ex = e;
        kv = ex.which * 1;
        isTextarea = (ex.target === "[object HTMLTextAreaElement]");
        currentId = ex.target.id === undefined ? '' : ex.target.id;
    }
    switch (kv) {
        case 13:
            if (ex.srcElement.tagName === "A" || (ex.srcElement.tagName === "INPUT" && ex.srcElement.type === "BUTTON"))
                return true;
            if ((currentId.length === 0))
                return false;
            if (isTextarea === true) {
                rv = isTextarea;
            }
            else {
                var cts = isAll ? document.all : document.getElementsByTagName('*');
                var cid = "";
                var queryBu = null;
                var beginNext = false; //開始比對是否為輸入元件
                var nextInput = null; //下一個輸入元件
                var specialSubmitId = "textQueryBU"; //指定特例的submit id
                var exceptionSubmitId = "waitting"; //指定例外的submit Id
                for (ii = 0; ii < cts.length; ii++) {
                    if (cts[ii].type === null || cts[ii].id === null)
                        continue;
                    if (cts[ii].id === currentId) {
                        beginNext = true;
                        continue;
                    }
                    if (beginNext === true && (cts[ii].id.indexOf(specialSubmitId) > -1 || ((cts[ii].type === "submit" || cts[ii].type === "image") && cts[ii].id.indexOf(exceptionSubmitId) === -1))) {
                        queryBu = cts[ii]; //找到submit元件
                        break;
                    }
                    else if (beginNext === true && (cts[ii].type === "text" || cts[ii].type === "textarea" || cts[ii].type === "password") && cts[ii].readOnly === false && cts[ii].style.display !== "none") {
                        nextInput = cts[ii]; //找到下一個文字輸入元件
                        break;
                    }
                }
                if (null !== queryBu) {
                    setTimeout("doClickId('" + queryBu.id + "');", 100);
                    rv = false;
                }
                else if (null !== nextInput && nextInput.offsetWidth !== 0) {
                    try {
                        nextInput.focus();
                    } catch (Error) { return true; }
                    rv = false;
                }
            }
            break;
        case 8:
            if (currentId.length > 0)
                return !document.getElementById(currentId).readOnly;
            else
                return true;
            break;
        default:
            rv = true;
            CursorUserChanged = typeof CursorUserChangedDetect === 'undefined' ? false : CursorUserChangedDetect;
            break;
    }
    return rv;
}
//找到第一個可輸入元件,並設定focus
function getFirstInputControl() {
    var inputs = document.all && typeof (document.all) === "object" ? document.all : document.getElementsByTagName('input');
    for (i = 0; i < inputs.length; i++) {
        if ((inputs[i].type === "text" || inputs[i].type === "textarea") && inputs[i].readOnly === false && inputs[i].offsetWidth !== 0) {
            try { inputs[i].focus(); } catch (error) { }
            break;
        }
    }
}

2017年9月2日 星期六

糟糕,HTTP轉HTTPS WCF Web服務失效了!

一、問題描述:

Microsoft WCF(Windows Communication Foundation)可以說是SOA 最佳實作技術,最主要的概念包含『鬆散偶合力』 以及 『軟體服務』。而SOA全名為 Service Oriented Architecture。這些名詞和概念不是本文的重點,讀者可以自行在網路上查到相關資料。

今天的問題是建置使用WCF服務的網站,如果由HTTP轉為HTTPS,預設情況下WCF服務是無法正常運作的,必須調整Web.config的serviceModel設定,讓我們來看看該如何調整?

二、實作解決方案:

以下是可正常運作的設定:

<system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webHttpBindingConfig">
        </binding>
        <binding name="webHttpsBindingConfig">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>

      </webHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="behaviorName">
          <enableWebScript />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <services>
      <service name="CmsP.Ctrl.Buyone.BuyoneWCF">
        <endpoint address="" behaviorConfiguration="behaviorName" bindingConfiguration="webHttpBindingConfig" binding="webHttpBinding" contract="mycontract" />
        <endpoint address="" behaviorConfiguration="behaviorName" bindingConfiguration="webHttpsBindingConfig" binding="webHttpBinding" contract="mycontract" />
      </service>
    </services>
  </system.serviceModel>

 ※標示紅色字體部分為HTTPS運作的相關設定

2017年1月21日 星期六

在GridView中如何Confirm CheckBox On Client Click

問題說明:
在系統實作的過程有可能會遇到需要在GridView中加入CheckBox元件,當使用者觸發OnCheckedChanged時,希望可以先啟動讓使用者確認異動的視窗。

<asp:templatefield footertext="序號" headertext="序號";>
<itemtemplate;>
<asp:checkbox AutoPostBack="True" id="selectedCB" oncheckedchanged="selectedCB_OnCheckedChanged" runat="server";></asp:checkbox;>
<asp:label id="indexNo" runat="server";></asp:label;>
</itemtemplate;>
<itemstyle horizontalalign="Justify" width="6%";>
</itemstyle;>
</asp:templatefield;>

上面布署的CheckBox元件,當使用者觸發CheckedChanged 後,會直接回送至伺服器,處理selectedCB_OnCheckedChanged even,要如何在Page PostBack前,觸發Confirm視窗呢?又如何讓完成的方法可以反覆使用呢?

我的作法:
1、將元件的布署修改如下:(取消AutoPostBack)

<asp:templatefield footertext="序號" headertext="序號";>
<itemtemplate;>
<asp:checkbox  id="selectedCB" oncheckedchanged="selectedCB_OnCheckedChanged" runat="server";></asp:checkbox;>
<asp:label id="indexNo" runat="server";></asp:label;>
</itemtemplate;>
<itemstyle horizontalalign="Justify" width="6%";>
</itemstyle;>
</asp:templatefield;>

2、掛上Client 端jQuery Fuction

function CancelOrChanged4CB(idFixName) {
$("input[type='checkbox']").on('change', function (e) {
            if ($(this).attr('id').indexOf(idFixName) === -1)
                return;
            if ($(this).prop('checked') === false) {
                if (confirm('確定取消報名嗎?') === false) {
                    $(this).prop('checked', true);
                    return;
                }
            }
            else {
                if (confirm('確定報名嗎?') === false) {
                    $(this).prop('checked', false);
                    return;
                }
            }
            __doPostBack($(this).attr('id'), '');
        });
}

3、在需要啟動CheckBox Confirm的頁面,加入下面jQuery程式碼:

$(window).load(function () {
        CancelOrChanged4CB('selectedCB');
    });

結語:
上面的 CancelOrChanged4CB function是可以重複啟動的。所以可以加入自建的jQuery lib中等候呼叫。

網頁輸入頁面Enter鍵處理元件(KeyDownProcess Version:1.64)

修正:無障礙按鍵不正常問題

function KeyDownProcess(e) {
    //-----------------------------------------------------------
    //網頁親和性輸入控制元件
    //元件功能:可以讓網頁資料的輸入更親切,功能如下:
    //1.按下Enter鍵自動找到下一個可輸入輸入元件
    //2.按下Enter鍵自動找尋submit按鍵,如果找到則模擬Click的功能
    //3.當輸入元件設定唯讀時,按下backspace鍵時,防止網頁轉址
    //4.支援無障礙導覽設定
    //5.支援Firefox、Google Chrome瀏覽器
    //6.支援按下shift鍵全域變數ShiftKeyDown(可實作連選)
    //7.支援頁面元件已輸入資料偵測(全域變數:CursorUserChanged CursorUserChangedDetect 可實作window.onbeforeunload confirm)
    //版本:1.64(2016.1.22)
    //作者:科碩資訊有限公司
    //引用本元件,請保留作者和版本資訊
    //連絡mail:cursor@cursorinfo.com.tw
    //------------------------------------------------------------
    w3cEvent = e;
    ShiftKeyDown = false;
    _beforeunloadTarget = '';
    CursorUserChanged = false;
    var kv = 0;
    var rv = false;
    var isTextarea = false;
    var currentId = null; //目前元件
    var ex;
    var isAll = typeof (document.all) == "object";
    if (isAll) {
        ShiftKeyDown = event.shiftKey;
        ex = event;
        kv = ex.keyCode;
        isTextarea = (ex.srcElement.tagName == "TEXTAREA");
        currentId = ex.srcElement.id == undefined ? '' : ex.srcElement.id;
    }
    else {
        ShiftKeyDown = e.shiftKey;
        ex = e;
        kv = ex.which * 1;
        isTextarea = (ex.target == "[object HTMLTextAreaElement]");
        currentId = ex.target.id == undefined ? '' : ex.target.id;
    }
    switch (kv) {
        case 13:
            if (ex.srcElement.tagName == "A" || (ex.srcElement.tagName == "INPUT" && ex.srcElement.type == "BUTTON"))
                return true;
            if ((currentId.length == 0))
                return false;
            if (isTextarea == true) {
                rv = isTextarea;
            }
            else {
                var cts = isAll ? document.all : document.getElementsByTagName('*');
                var cid = "";
                var queryBu = null;
                var beginNext = false; //開始比對是否為輸入元件
                var nextInput = null; //下一個輸入元件
                var specialSubmitId = "textQueryBU"; //指定特例的submit id
                var exceptionSubmitId = "waitting"; //指定例外的submit Id
                for (ii = 0; ii < cts.length; ii++) {
                    if (cts[ii].type == null || cts[ii].id == null)
                        continue;
                    if (cts[ii].id == currentId) {
                        beginNext = true;
                        continue;
                    }
                    if (beginNext == true && (cts[ii].id.indexOf(specialSubmitId) > -1 || ((cts[ii].type == "submit" || cts[ii].type == "image") && cts[ii].id.indexOf(exceptionSubmitId) == -1))) {
                        queryBu = cts[ii]; //找到submit元件
                        break;
                    }
                    else if (beginNext == true && (cts[ii].type == "text" || cts[ii].type == "textarea" || cts[ii].type == "password") && cts[ii].readOnly == false && cts[ii].style.display != "none") {
                        nextInput = cts[ii]; //找到下一個文字輸入元件
                        break;
                    }
                }
                if (null != queryBu) {
                    setTimeout("doClickId('" + queryBu.id + "');", 100);
                    rv = false;
                }
                else if (null != nextInput && nextInput.offsetWidth != 0) {
                    try {
                        nextInput.focus();
                    } catch (Error) { return true; }
                    rv = false;
                }
            }
            break;
        case 8:
            if (currentId.length > 0)
                return !document.getElementById(currentId).readOnly;
            else
                return true;
            break;
        default:
            rv = true;
            CursorUserChanged = typeof CursorUserChangedDetect == 'undefined' ? false : CursorUserChangedDetect;
            break;
    }
    return rv;
}

//實做模擬Click的功能
function doClickId(source) {
    var o = document.getElementById(source);
    if (document.all && typeof (document.all) == "object") //IE
        o.click();
    else {
        if (document.createEvent) {
            var e = document.createEvent('MouseEvent');
            e.initEvent('click', true, false);
            o.dispatchEvent(e);
        } else {
            o.fireEvent('onclick');
        }
    }
}