在*一些* WebAPI 控制器上禁用 SSL 客户端证书?
Posted
技术标签:
【中文标题】在*一些* WebAPI 控制器上禁用 SSL 客户端证书?【英文标题】:Disable SSL client certificate on *some* WebAPI controllers? 【发布时间】:2015-02-04 02:16:36 【问题描述】:为未来的读者编辑:不幸的是,赏金授予了答案 不工作;我现在对此无能为力。但请阅读我自己的答案 下面(通过测试) - 确认可以使用最少的代码更改
我们有一个完全在 ASP.NET WebAPI 2.2 中的 Azure 云服务 (WebRole)(没有 MVC,前端是 Angular)。我们的一些控制器/REST 端点通过 SSL(客户端证书身份验证/相互身份验证)与第 3 方云服务通信,其余控制器/端点与 html5/AngularJS 前端通信,也通过 SSL(但更传统的服务器身份验证SSL)。我们没有任何非 SSL 端点。我们通过云服务启动任务启用了客户端 SSL,例如:
IF NOT DEFINED APPCMD SET APPCMD=%SystemRoot%\system32\inetsrv\AppCmd.exe
%APPCMD% unlock config /section:system.webServer/security/access
问题:该设置是站点范围的,因此即使用户点击第一页(比如https://domain.com,返回 angularJS 的 index.html),他们的浏览器也会要求他们提供客户端 SSL 证书。 (下图)
如果有办法
-
将客户端 SSL 证书请求限制为仅与第 3 方云服务通信的 WebAPI 控制器?
或
-
为我们的前端驱动 webapi 控制器跳过客户端 SSL 身份验证?
我们服务器的web.config比较复杂,但是相关的sn-p如下:
<system.webServer>
<security>
<access sslFlags="SslNegotiateCert" />
</security>
</system.webServer>
客户端访问常规 WebAPI 端点但尝试客户端 SSL 身份验证的屏幕截图(发生在任何浏览器、Chrome、Firefox 或 IE 中)
【问题讨论】:
你能显示你的结果 serverWebConfig 和应用程序 WebConfig 吗? 服务器 web.config 上面,应用程序 webconfig 不存在,因为客户端应用程序是浏览器 我认为这个问题更适合服务器故障。 【参考方案1】:不幸的是,cleftheris 获得赏金的答案不起作用。它试图在 HTTP 服务器管道/处理中工作太晚以获取客户端证书,但this post 给了我一些想法。
该解决方案基于 web.config
,它要求对“目录”进行特殊处理(也适用于虚拟文件夹或 WebAPI 路由)。
这是所需的逻辑:
https://www.server.com/acmeapi/** => 带有客户端证书的 SSL
https://www.server.com/** => SSL
这里是对应的配置
<configuration>
...
<system.webServer>
<!-- This is for the rest of the site -->
<security>
<access sslFlags="Ssl" />
</security>
</system.webServer>
<!--This is for the 3rd party API endpoint-->
<location path="acmeapi">
<system.webServer>
<security>
<access sslFlags="SslNegotiateCert"/>
</security>
</system.webServer>
</location>
...
</configuration>
奖励积分
以上将相应地设置 SSL 握手。现在,您仍然需要检查代码中的客户端 SSL 证书是否符合您的预期。这样做如下
控制器代码:
[RoutePrefix("acmeapi")]
[SslClientCertActionFilter] // <== key part!
public class AcmeProviderController : ApiController
[HttpGet]
[Route("userId")]
public async Task<OutputDto> GetInfo(Guid userId)
// do work ...
执行 SSL 客户端验证的实际属性如下。可用于装饰整个控制器或只是特定方法。
public class SslClientCertActionFilterAttribute : ActionFilterAttribute
public List<string> AllowedThumbprints = new List<string>()
// Replace with the thumbprints the 3rd party
// server will be presenting. You can make checks
// more elaborate but always have thumbprint checking ...
"0011223344556677889900112233445566778899",
"1122334455667788990011223344556677889900"
;
public override void OnActionExecuting(HttpActionContext actionContext)
var request = actionContext.Request;
if (!AuthorizeRequest(request))
throw new HttpResponseException(HttpStatusCode.Forbidden);
private bool AuthorizeRequest(HttpRequestMessage request)
if (request==null)
throw new ArgumentNullException("request");
var clientCertificate = request.GetClientCertificate();
if (clientCertificate == null || AllowedThumbprints == null || AllowedThumbprints.Count < 1)
return false;
foreach (var thumbprint in AllowedThumbprints)
if (clientCertificate.Thumbprint != null && clientCertificate.Thumbprint.Equals(thumbprint, StringComparison.InvariantCultureIgnoreCase))
return true;
return false;
【讨论】:
这就像一个魅力。我找不到任何其他推荐可行解决方案的参考资料。谢谢【参考方案2】:您可以简单地在 web.config 级别允许纯 http 流量,并为此在 Web Api 管道中编写自定义委托处理程序。您可以找到客户端证书委托处理程序 here 和 here。然后你可以让这个处理程序激活“Per-Route”,就像在这个例子中找到的here:
这就是您的路由配置的样子。
public static class WebApiConfig
public static void Register(HttpConfiguration config)
config.Routes.MapHttpRoute(
name: "Route1",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
config.Routes.MapHttpRoute(
name: "Route2",
routeTemplate: "api2/controller/id",
defaults: new id = RouteParameter.Optional ,
constraints: null,
handler: new CustomCertificateMessageHandler() // per-route message handler
);
config.MessageHandlers.Add(new SomeOtherMessageHandler()); // global message handler
请注意,如果您需要“按路由”委派处理程序,则必须不要将它们放在全局消息处理程序列表中。
【讨论】:
请注意,这是我回答中选项 #3 的实现,因此存在建立 SSL 会话两次的性能问题。 @DeepSpace101 看看这个codeplex workitem。列出的代码应该可以正常工作。 致未来的读者:请阅读我对自己问题的回答。它已确认可以工作,并且需要最少的代码更改【参考方案3】:很遗憾,这无法在控制器级别进行配置。服务器根据 HTTP 请求的内容(通常是请求路径)决定使用哪个控制器。 SSL 通过加密来保护 HTTP 消息的内容,请求路径是加密消息的一部分。 SSL 通道需要在发送任何 HTTP 消息之前建立,这就是为什么 SSL 通道的配置(例如,服务器是否尝试协商客户端证书)不能依赖任何 HTTP 的内容消息。
以下是您的选择:
启动第二个 Web 角色,该角色配置为不协商客户端证书。为此,您需要第二个域,因为它本质上是一项单独的服务。因此,https://domain.com 指向非客户端证书,https://foo.domain.com 指向需要客户端证书的证书。
使用相同的 Web 角色,但设置第二个端口供 IIS 侦听,并将该端口配置为不协商客户端证书。不过,使用非标准端口很痛苦,因为您的一个客户将不得不使用https://domain.com:444(或除 443 之外的其他端口)。
全面禁用客户端证书协商。这可能不起作用,具体取决于您的服务如何访问客户端证书,但通常当您访问 System.Web.HttpRequest 对象(或等效对象)上的 ClientCertificate 属性时,它会按需协商证书。这意味着它透明地拆除现有的 SSL 会话并建立一个新的会话,这一次挑战客户端的证书。这是非常低效的,因为首先建立一个 SSL 连接需要多次往返,而且这样做两次很痛苦。但根据您的可用选项、使用客户端证书的请求的性能要求,以及您是否会从保持活动状态中获得大量连接重用,此选项可能有意义。
希望这会有所帮助。
【讨论】:
如果不是控制器,我可以告诉 IIS 本身在哪些路径上强制(或跳过)客户端 SSL 吗?根据您的描述,它是做出该决策的正确层。 不幸的是,你不能。您唯一可以使用的是 IP 和端口。当一个 HTTP 请求进来时,SSL 协商需要在服务器甚至找到路径之前发生。选项 #3 是您可以根据路径/控制器做出不同决策的最接近的方法。但效率较低。 @DeepSpace101 您能否尝试使用自己的 web.config 创建一个空的物理文件夹,然后在该路径下创建您的安全控制器路由?然后,您可以使用 Appcmd Unlock Config 命令仅解锁该位置的访问元素。以上是关于在*一些* WebAPI 控制器上禁用 SSL 客户端证书?的主要内容,如果未能解决你的问题,请参考以下文章
API Gateway w/WebAPI 端点在同一用户桥接网络上。 WebAPI 需要 SSL?
为啥在禁用 webdav 后,在本地工作的 WebApi 的 DELETE 请求上出现 405 方法不允许错误?
Service Fabric 多个 SSL 安全 WebAPI 和证书翻转