MiniAPI(十五)：CORS対策（下）

前回は比較的寛容なCORS要求の方法について述べました。CORSにはセキュリティリスクが伴うため、必要最小限のリソースへのアクセス許可をすることが望ましいです。今回は、リクエストメソッド、ヘッダー、クレデンシャルからCORS設定を詳しく見ていきます。

リクエストメソッド：
APIプロジェクトにおいて、GETとPOSTはデフォルトでアクセスが許可されています。ここではPUTのみアクセス許可を設定します。

```C#
using Microsoft.AspNetCore.Cors;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>{
    options.AddPolicy(name: "Policy2",                
                      builder =>                
                      {                    
                          builder                    
                          .WithOrigins("http://localhost:5280")                            
                          .WithMethods("PUT");                
                      });
});
var app = builder.Build();
// 注意、ここではポリシー名を使用していません
app.UseCors();
app.MapGet("/test2", [EnableCors("Policy2")] () => "GETの結果");
app.MapPost("/test2", [EnableCors("Policy2")] () => "POSTの結果");
app.MapDelete("/test2", [EnableCors("Policy2")] () => "DELETEの結果");
app.MapPut("/test2", [EnableCors("Policy2")] () => "PUTの結果");
app.Map("/test2", [EnableCors("Policy2")] () => "全てマップ");
app.Run();
```

ウェブプロジェクト：

```HTML
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
<div class="text-center">    
    <h1 class="display-4">MiniAPI学習へようこそ</h1>    
    <p>この例はCORSに関する情報を共有するものです。</p>
</div>
<p id="test2-get"></p>
<p id="test2-post"></p>
<p id="test2-delete"></p>
<p id="test2-put"></p>
@section Scripts{
<script>      
    $(function(){          
        $.ajax({              
            url: 'http://localhost:5001/test2',              
            type: 'GET',          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test2-get").html("test2 get:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test2-get").html("test2 get:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test2',              
            type: 'POST',          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test2-post").html("test2:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test2-post").html("test2:"+textStatus)          
        });

       $.ajax({              
            url: 'http://localhost:5001/test2',              
            type: 'DELETE',          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test2-delete").html("test2 delete:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test2-delete").html("test2 delete:"+textStatus)          
        });

       $.ajax({              
            url: 'http://localhost:5001/test2',              
            type: 'PUT',          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test2-put").html("test2 put:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test2-put").html("test2 put:"+textStatus)          
        });
    });
</script>}
```

実行結果、DELETEが拒否されました。

![代替テキスト](https://mmbiz.qpic.cn/mmbiz_png/4CAA63nvHRDC5YkU2W5hb5yYKtaT0fcG3mFPooxPBzHI7UQDL5icibGNEU18mL6qbwjW1MIuVTJGqysnNxeRSBLQ/640?wx_fmt=png)

リクエストヘッダー：
APIプロジェクトでは、全てのリクエストメソッドが通過する設定です。

```C#
using Microsoft.AspNetCore.Cors;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>{
    options.AddPolicy(name: "Policy3",
            builder =>            {
                builder.WithOrigins("http://localhost:5280")
                .WithHeaders("x-cors-header")                    
                .AllowAnyMethod();            
            });
   });

var app = builder.Build();
app.UseCors();
app.MapGet("/test3", [EnableCors("Policy3")] () => "GETの結果");
app.MapPost("/test3", [EnableCors("Policy3")] () => "POSTの結果");
app.MapDelete("/test3", [EnableCors("Policy3")] () => "DELETEの結果");
app.MapPut("/test3", [EnableCors("Policy3")] () => "PUTの結果");
app.Map("/test3", [EnableCors("Policy3")] () => "全てマップ");
app.Run();
```

ウェブプロジェクト：

```HTML
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
<div class="text-center">    
    <h1 class="display-4">MiniAPI学習へようこそ</h1>    
    <p>この例はCORSに関する情報を共有するものです。</p>
</div>
<p id="test3-get"></p>
<p id="test3-post"></p>
<p id="test3-delete"></p>
<p id="test3-put"></p>
@section Scripts{
<script>      
    $(function(){          
        $.ajax({              
            url: 'http://localhost:5001/test3',              
            type: 'GET',              
            headers: {                  
                "x-cors-header":"gsw"              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test3-get").html("test3 get:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test3-get").html("test3 get:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test3',              
            type: 'POST',              
            headers: {                  
                "x-cors-header":"gsw"              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test3-post").html("test3 post:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test3-post").html("test3 post:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test3',              
            type: 'PUT',              
            headers: {                  
                "x-cors-header":"gsw"              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test3-put").html("test3 put:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test3-put").html("test3 put:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test3',              
            type: 'DELETE',              
            headers: {                 
                "x-cors-header":"gsw",                     
                "x-key":"gsw",                             
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test3-delete").html("test3 delete:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test3-delete").html("test3 delete:"+textStatus)          
        });                 
    });
</script>}
```

実行結果、DELETEが失敗しました。DELETEリクエスト内にx-keyヘッダーが含まれていたため、通過しませんでした。

![代替テキスト](https://mmbiz.qpic.cn/mmbiz_png/4CAA63nvHRDC5YkU2W5hb5yYKtaT0fcGt8KJxWwaSL86k55xuRepdUpd2vibxZrbulHnzuErCYUWUUlWYlAbtyw/640?wx_fmt=png)

リクエストクレデンシャル：
CORSリクエストでクレデンシャルを特別に扱います。デフォルトでは、ブラウザはクロスオリジンリクエストでクレデンシャルを送信しません。クレデンシャルにはcookiesやHTTP認証スキームが含まれます。クロスオリジンリクエストでクレデンシャルを送信するには、クライアントがXMLHttpRequest.withCredentialsをtrueに設定する必要があります。

APIプロジェクト：

```C#
using Microsoft.AspNetCore.Cors;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>{
    options.AddPolicy(name: "Policy4",
            builder =>            {
                builder.WithOrigins("http://localhost:5280")
                .AllowCredentials()                    
                .AllowAnyMethod();            
            });
});
var app = builder.Build();
app.UseCors();
app.MapGet("/test4", [EnableCors("Policy4")] () => "GETの結果");
app.MapPost("/test4", [EnableCors("Policy4")] () => "POSTの結果");
app.MapDelete("/test4", [EnableCors("Policy4")] () => "DELETEの結果");
app.MapPut("/test4", [EnableCors("Policy4")] () => "PUTの結果");
app.Map("/test4", [EnableCors("Policy4")] () => "全てマップ");
app.Run();
```

ウェブプロジェクト：

```HTML
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
<div class="text-center">    
    <h1 class="display-4">MiniAPI学習へようこそ</h1>    
    <p>この例はCORSに関する情報を共有するものです。</p>
</div>
<p id="test4-get"></p>
<p id="test4-post"></p>
<p id="test4-delete"></p>
<p id="test4-put"></p>
@section Scripts{
<script>      
    $(function(){          
        $.ajax({              
            url: 'http://localhost:5001/test4',              
            type: 'GET',              
            xhrFields: {                  
                withCredentials: true              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test4-get").html("test4 get:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test4-get").html("test4 get:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test4',              
            type: 'POST',              
            xhrFields: {                  
                withCredentials: true              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test4-post").html("test4 post:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test4-post").html("test4 post:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test4',              
            type: 'PUT',              
            xhrFields: {                  
                withCredentials: true              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test4-put").html("test4 put:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test4-put").html("test4 put:"+textStatus)          
        });

        $.ajax({              
            url: 'http://localhost:5001/test4',              
            type: 'DELETE',              
            xhrFields: {                  
                withCredentials: true              
            }          
        }).done(function( data, textStatus, jqXHR ) {              
            $("#test4-delete").html("test4 delete:"+data)          
        }).fail(function( jqXHR, textStatus, errorThrown) {              
            $("#test4-delete").html("test4 delete:"+textStatus)          
        });
    });
</script>}
```

実行結果：

![代替テキスト](https://mmbiz.qpic.cn/mmbiz_png/4CAA63nvHRDC5YkU2W5hb5yYKtaT0fcG2Mk2Ihre0vF7GtQLbM6LPiakaibnyPN60HdnjcXXvjuicW7ulb7l1TCxA/640?wx_fmt=png)

APIプロジェクト内のクレデンシャルを除くと：

```C#
options.AddPolicy(name: "Policy4",            
                  builder => {                                
                      builder.WithOrigins("http://localhost:5280")                                        
                              .AllowAnyMethod();            
                  });
```

実行結果：

![代替テキスト](https://mmbiz.qpic.cn/mmbiz_png/4CAA63nvHRDC5YkU2W5hb5yYKtaT0fcGj61JnKOP8AldSLZaspxgITC5CHpze58r0VWseiaCjb1c3WKJJxNGMDw/640?wx_fmt=png)

(Translated by GPT)

元のリンク：https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247484911&idx=1&sn=3deb28ffe7d998f89d563d0615f84a08&chksm=9f005ac5a877d3d3057165b4db77301ecbc0378acd94e7350e8a637dbfd8ddc8d5681d194109&token=541757603&lang=zh_CN#rd