【问题标题】:Browser won't set ASP.NET_SessionId cookie on payment gateway's post request to our site浏览器不会在支付网关对我们网站的发布请求中设置 ASP.NET_SessionId cookie
【发布时间】:2020-04-03 18:32:15
【问题描述】:

我们的网络应用程序的支付过程遇到了一个奇怪的问题,导致会话数据丢失。

在此过程中,在我们的结帐页面用户被重定向到支付提供商的页面并在他/他完成后立即重定向回我们的网站(到我们指定的网址)。最后一个重定向是通过浏览器评估支付提供商的 html 代码完成的,该代码基本上由一个发布到我们网站的表单和几行在页面加载时发布该表单的 javascript 代码组成。此时,浏览器发出 post 请求,但没有设置“ASP.NET_SessionId”cookie,该 cookie 存在于对完全相同的域(我们的应用程序的域)发出的先前请求中。更奇怪的是它设置了另一个我们使用的名为“AcceptCookie”的cookie。它只是简单地选择删除“ASP.NET_SessionId”cookie。

为了说明情况,我截取了一些截图。 (在这些屏幕截图中,橙色和绿色矩形包含完全相同的值。)

  1. 这是用户按下“签出”按钮时(向我们的应用程序)发出的请求。在此请求之后,用户被重定向到支付提供商的页面。

check-out request

  1. 这是用户完成后支付提供商提供的最后一页。如您所见,它只是一个简单的表单,会在页面加载时自动发布到我们的域。

payment provider's final response

  1. 但此发布请求不包含“ASP.NET_SessionId”cookie,这会导致获取新的会话 ID 并丢失以前的会话数据。同样,只是缺少“ASP.NET_SessionId”,而不是另一个名为“AcceptCookie”的。

post request that brings the user back to our site (made with javascript in the previous step)

最后我们发现在旧版本的浏览器上不会出现这个问题。在 Firefox 52 上它就像一个魅力,但在 Firefox 71 上会发生上述问题。

有什么想法吗?

注意:这是一个 ASP.NET MVC 应用程序,targetFramework="4.5.2"

祝你有美好的一天。

【问题讨论】:

    标签: asp.net session cookies


    【解决方案1】:

    如果您不想部署安全的SameSite=None cookie,那么另一种选择是让浏览器在页面加载时在 JavaScript 中设置 cookie。这种方法适用于所有浏览器,而不需要针对各种浏览器的任何特殊情况。在 ASP.NET MVC 应用程序中,只需将以下内容添加到 layout.cshtml 页面即可完成:

    <script type="text/javascript">
        // If being rendered in an iFrame, set a client-side cookie for the ASP.NET Session ID
        if (window != window.top) {
            document.cookie = "ASP.NET_SessionID=@HttpContext.Current.Session.SessionID";
        }
    </script>
    

    这实际上是通过 HTML 有效负载将 cookie 值传递给客户端,然后客户端会覆盖浏览器可能接受也可能不接受的 ASP.NET_SessionID cookie。设置 cookie 后,发出的任何请求都会将 cookie 传递回服务器。请注意,这种方法不允许让您为会话 cookie 指定 HttpOnly

    我不建议将这种方法用于面向公众的网站,但对于 Intranet 应用程序来说,这是一种非常快速的解决方法。

    【讨论】:

      【解决方案2】:

      我们想通了。

      不知何故,“ASP.NET_SessionId”cookie 的“SameSite”属性默认为“Lax”,这导致会话 cookie 未添加到支付网关的 javascript 代码发出的请求中。

      我们在 web.config 文件中添加了以下规则,以便覆盖此值并将其设置为“无”。

      <configuration>
        <system.webServer>
          <rewrite>
            <outboundRules>
              <rule name="Add SameSite" preCondition="No SameSite">
                <match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
                <action type="Rewrite" value="{R:0}; SameSite=None" />
                <conditions>
                </conditions>
              </rule>
              <preConditions>
                <preCondition name="No SameSite">
                  <add input="{RESPONSE_Set_Cookie}" pattern="." />
                  <add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=None" negate="true" />
                </preCondition>
              </preConditions>
            </outboundRules>
          </rewrite>
        </system.webServer>
      </configuration>
      

      更新 1:只需添加上述配置即可解决现代浏览器的问题,但我们意识到旧版本的 Micosoft Edge 和 Internet Explorer 仍然存在问题。

      所以我们需要在 web.config 文件的 sessionState 节点中添加 cookieSameSite="None" 属性。

      <sessionState cookieSameSite="None" />
      

      请注意此配置更改,因为较旧的 .net 框架版本不支持它并导致您的网站显示错误页面。

      顺便说一句,我们在 IOS 12 中的浏览器仍然存在问题。但我认为这与 this confirmed bug 有关

      更新 2:请参阅 zemien 的回答,了解有关 IOS 问题的可能修复方法

      更新 3:通过将我们的发现与 zemien 回答中的建议相结合,我们提出了以下重写规则。我们一直在生产中使用这种配置。 但请注意:对于兼容的浏览器,它会将所有 cookie 标记为“SameSite:None”属性,而对于不兼容的浏览器,它会排除 SameSite 属性(如果存在)。这可能看起来很复杂,但我试图通过注释行来解释。

      这是我们在生产中使用的最终配置:

      <configuration> 
      
        <system.webServer>
      
          <rewrite>
      
            <outboundRules>
      
              <preConditions>
                <!-- Browsers incompatible with SameSite=None -->
                <preCondition name="IncompatibleWithSameSiteNone" logicalGrouping="MatchAny">
                  <add input="{HTTP_USER_AGENT}" pattern="(CPU iPhone OS 12)|(iPad; CPU OS 12)" />
                  <add input="{HTTP_USER_AGENT}" pattern="(Chrome/5)|(Chrome/6)" />
                  <add input="{HTTP_USER_AGENT}" pattern="( OS X 10_14).*(Version/).*((Safari)|(KHTML, like Gecko)$)" />
                </preCondition>
      
                <!-- Rest of the browsers are assumed to be compatible with SameSite=None -->
                <preCondition name="CompatibleWithSameSiteNone" logicalGrouping="MatchAll">
                  <add input="{HTTP_USER_AGENT}" pattern="(CPU iPhone OS 12)|(iPad; CPU OS 12)" negate="true" />
                  <add input="{HTTP_USER_AGENT}" pattern="(Chrome/5)|(Chrome/6)" negate="true" />
                  <add input="{HTTP_USER_AGENT}" pattern="( OS X 10_14).*(Version/).*((Safari)|(KHTML, like Gecko)$)" negate="true" />
                </preCondition>
      
              </preConditions>
      
              <!-- Rule 1: Remove SameSite part from cookie for incompatible browsers if exists -->
              <rule name="Remove_SameSiteCookie_IfExists_ForLegacyBrowsers" preCondition="IncompatibleWithSameSiteNone">
                <match serverVariable="RESPONSE_Set-Cookie" pattern="(.*)(SameSite=.*)" />
                <action type="Rewrite" value="{R:1}" />
              </rule>
      
              <!-- Rule 2: Override SameSite's value to None if exists, for compatible browsers -->
              <rule name="Override_SameSiteCookie_IfExists_ForModernBrowsers" preCondition="CompatibleWithSameSiteNone">
                <match serverVariable="RESPONSE_Set-Cookie" pattern="(.*)(SameSite=.*)" />
                <action type="Rewrite" value="{R:1}; SameSite=None" />
              </rule>
      
              <!-- Rule 3: Add SameSite attribute with the value None if it does not exists, for compatible browsers -->
              <rule name="Add_SameSiteCookie_IfNotExists_ForModernBrowsers" preCondition="CompatibleWithSameSiteNone">
                <match serverVariable="RESPONSE_Set-Cookie" pattern=".*"/>
                <!-- Condition explanation: Cookie data contains some string value but does not contain SameSite attribute -->
                <conditions logicalGrouping="MatchAll">
                  <add input="{R:0}" pattern="^(?!\s*$).+"/>
                  <add input="{R:0}" pattern="SameSite=.*" negate="true"/>
                </conditions>
                <action type="Rewrite" value="{R:0}; SameSite=None" />
              </rule>
      
            </outboundRules>
      
          </rewrite>    
      
        </system.webServer>  
      
      </configuration>
      

      【讨论】:

      • 感谢@EÖzgür。此问题来自 KB4533097 (support.microsoft.com/en-us/help/4533097/kb4533097),尤其是 12 月 10 日发布的 KB4533011(.net 4.7 及更低版本)和 KB4533004 (.net 4.8)。
      • 我也有同样的问题,但有时 asp.net mvc 给客户的 ASP.NET_SessionId cookie 带有 LAX,有时带有 NONE。我不确定它为什么会发生。我的意思是它应该一直是 LAX,但是当我在网站上登录时仍然可以得到 NONE。
      • 天哪!我已经为这个问题疯狂了两天。最后,您的回答挽救了我的一天和沮丧。谢谢。
      • 在应用 12 月更新后,我们在 Server 2016 上发生了这个问题。 (KB4530689)。非常感谢您找到解决方案!
      • 这仅适用于 dotnet 核心吗?在我的框架应用程序中,我将您的选项显示为要设置的无效值。
      【解决方案3】:

      我修改了几个 SO 答案以提出此 URL 重写,将 SameSite=None 添加到会话 cookie,并从大多数不兼容浏览器的 所有 cookie 中删除 SameSite=None。此次重写的目的是保留 Chrome 80 之前的“遗留”行为。

      我的Coder Frontline blog

      <rewrite>
        <outboundRules>
          <preConditions>
            <!-- Checks User Agent to identify browsers incompatible with SameSite=None -->
            <preCondition name="IncompatibleWithSameSiteNone" logicalGrouping="MatchAny">
              <add input="{HTTP_USER_AGENT}" pattern="(CPU iPhone OS 12)|(iPad; CPU OS 12)" />
              <add input="{HTTP_USER_AGENT}" pattern="(Chrome/5)|(Chrome/6)" />
              <add input="{HTTP_USER_AGENT}" pattern="( OS X 10_14).*(Version/).*((Safari)|(KHTML, like Gecko)$)" />
            </preCondition>
          </preConditions>
      
          <!-- Adds or changes SameSite to None for the session cookie -->
          <!-- Note that secure header is also required by Chrome and should not be added here -->
          <rule name="SessionCookieAddNoneHeader">
            <match serverVariable="RESPONSE_Set-Cookie" pattern="((.*)(ASP.NET_SessionId)(=.*))(SameSite=.*)?" />
            <action type="Rewrite" value="{R:1}; SameSite=None" />
          </rule>
      
          <!-- Removes SameSite=None header from all cookies, for most incompatible browsers -->
          <rule name="CookieRemoveSameSiteNone" preCondition="IncompatibleWithSameSiteNone">
            <match serverVariable="RESPONSE_Set-Cookie" pattern="(.*)(SameSite=None)" />
            <action type="Rewrite" value="{R:1}" />
          </rule>
        </outboundRules>
      </rewrite>
      

      这应该适用于大多数 ASP .Net 和 ASP .Net Core 应用程序,尽管较新的框架具有适当的代码和配置选项来让您控制这种行为。我建议在使用上面的重写之前研究所有可用的选项。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-12-05
        • 1970-01-01
        • 2016-02-10
        • 1970-01-01
        • 2021-11-01
        • 2018-01-11
        • 1970-01-01
        相关资源
        最近更新 更多