我们的网络应用程序的支付流程遇到了一个奇怪的问题,导致 session 数据丢失。
在此过程中,在我们的结帐页面之后,用户将被重定向到支付提供商的页面,并在完成后立即重定向回我们的网站(到我们指定的网址)。最后一个重定向是通过浏览器对支付提供商的 html 代码进行评估来完成的,该代码基本上由发布到我们网站的表单和在页面加载时发布该表单的几行 javascript 代码组成。此时,浏览器发出 post 请求,但不会设置“ASP.NET_SessionId”cookie,该 cookie 出现在之前对完全相同的域(我们的应用程序的域)发出的请求中。更奇怪的是,它设置了我们使用的另一个 cookie,名为“AcceptCookie”。它只是简单地选择删除“ASP.NET_SessionId”cookie。
为了说明情况,我截取了一些屏幕截图。 (在这些屏幕截图中,橙色和绿色矩形包含完全相同的值。)
- 这是当用户按下“ checkout ”按钮时(向我们的应用程序)发出的请求。发出此请求后,用户将被重定向到支付提供商的页面。
- 这是用户完成操作后支付提供商提供的最终页面。正如您所看到的,这只是一个简单的表单,会在页面加载时自动发布到我们的域。
payment provider's final response
- 但是此 post 请求不包含“ASP.NET_SessionId”cookie,这会导致获取新的 session ID 并丢失先前的 session 数据。再说一次,只是缺少“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 上就会出现上述问题。
有什么想法吗?
注意:这是一个 targetFramework="4.5.2"的 ASP.NET MVC 应用程序
祝你有美好的一天。
最佳答案
我们想通了。
不知何故,“ASP.NET_SessionId”cookie 的“SameSite”属性默认为“Lax”,这会导致 session cookie 未添加到支付网关的 javascript 代码发出的请求中。
我们在 web.config 文件中添加了以下规则,以覆盖此值并将其设置为“None”。
<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 答案中的建议相结合,我们提出了以下重写规则。我们一直在生产中使用此配置。 但要注意:它会为兼容浏览器标记所有具有“SameSite:None”属性的 cookie,并排除不兼容浏览器的 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>
关于asp.net - 浏览器不会在支付网关向我们网站发出的发布请求上设置 ASP.NET_SessionId cookie,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59269476/