|
1 | 1 | --- |
2 | 2 | title: Client IP safelist for ASP.NET Core |
3 | 3 | author: damienbod |
4 | | -description: Learn how to write Middleware or action filters to validate remote IP addresses against a list of approved IP addresses. |
| 4 | +description: Learn how to write middleware or action filters to validate remote IP addresses against a list of approved IP addresses. |
| 5 | +monikerRange: '>= aspnetcore-2.1' |
5 | 6 | ms.author: riande |
6 | 7 | ms.custom: mvc |
7 | | -ms.date: 08/31/2018 |
| 8 | +ms.date: 03/12/2020 |
8 | 9 | uid: security/ip-safelist |
9 | 10 | --- |
10 | 11 | # Client IP safelist for ASP.NET Core |
11 | 12 |
|
12 | 13 | By [Damien Bowden](https://twitter.com/damien_bod) and [Tom Dykstra](https://github.com/tdykstra) |
13 | 14 |
|
14 | | -This article shows three ways to implement an IP safelist (also known as a whitelist) in an ASP.NET Core app. You can use: |
| 15 | +This article shows three ways to implement an IP address safelist (also known as an allow list) in an ASP.NET Core app. An accompanying sample app demonstrates all three approaches. You can use: |
15 | 16 |
|
16 | 17 | * Middleware to check the remote IP address of every request. |
17 | | -* Action filters to check the remote IP address of requests for specific controllers or action methods. |
| 18 | +* MVC action filters to check the remote IP address of requests for specific controllers or action methods. |
18 | 19 | * Razor Pages filters to check the remote IP address of requests for Razor pages. |
19 | 20 |
|
20 | | -In each case, a string containing approved client IP addresses is stored in an app setting. The middleware or filter parses the string into a list and checks if the remote IP is in the list. If not, an HTTP 403 Forbidden status code is returned. |
| 21 | +In each case, a string containing approved client IP addresses is stored in an app setting. The middleware or filter: |
21 | 22 |
|
22 | | -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/security/ip-safelist/samples/2.x/ClientIpAspNetCore) ([how to download](xref:index#how-to-download-a-sample)) |
| 23 | +* Parses the string into an array. |
| 24 | +* Checks if the remote IP address exists in the array. |
23 | 25 |
|
24 | | -## The safelist |
| 26 | +Access is allowed if the array contains the IP address. Otherwise, an HTTP 403 Forbidden status code is returned. |
25 | 27 |
|
26 | | -The list is configured in the *appsettings.json* file. It's a semicolon-delimited list and can contain IPv4 and IPv6 addresses. |
| 28 | +[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/security/ip-safelist/samples) ([how to download](xref:index#how-to-download-a-sample)) |
27 | 29 |
|
28 | | -[!code-json[](ip-safelist/samples/2.x/ClientIpAspNetCore/appsettings.json?highlight=2)] |
| 30 | +## IP address safelist |
| 31 | + |
| 32 | +In the sample app, the IP address safelist is: |
| 33 | + |
| 34 | +* Defined by the `AdminSafeList` property in the *appsettings.json* file. |
| 35 | +* A semicolon-delimited string that may contain both [Internet Protocol version 4 (IPv4)](https://wikipedia.org/wiki/IPv4) and [Internet Protocol version 6 (IPv6)](https://wikipedia.org/wiki/IPv6) addresses. |
| 36 | + |
| 37 | +[!code-json[](ip-safelist/samples/3.x/ClientIpAspNetCore/appsettings.json?range=1-3&highlight=2)] |
| 38 | + |
| 39 | +In the preceding example, the IPv4 addresses of `127.0.0.1` and `192.168.1.5` and the IPv6 loopback address of `::1` (compressed format for `0:0:0:0:0:0:0:1`) are allowed. |
29 | 40 |
|
30 | 41 | ## Middleware |
31 | 42 |
|
32 | | -The `Configure` method adds the middleware and passes the safelist string to it in a constructor parameter. |
| 43 | +The `Startup.Configure` method adds the custom `AdminSafeListMiddleware` middleware type to the app's request pipeline. The safelist is retrieved with the .NET Core configuration provider and is passed as a constructor parameter. |
33 | 44 |
|
34 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Startup.cs?name=snippet_Configure&highlight=10)] |
| 45 | +[!code-csharp[](ip-safelist/samples/3.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureAddMiddleware)] |
35 | 46 |
|
36 | | -The middleware parses the string into an array and looks for the remote IP address in the array. If the remote IP address is not found, the middleware returns HTTP 401 Forbidden. This validation process is bypassed for HTTP Get requests. |
| 47 | +The middleware parses the string into an array and searches for the remote IP address in the array. If the remote IP address isn't found, the middleware returns HTTP 403 Forbidden. This validation process is bypassed for HTTP GET requests. |
37 | 48 |
|
38 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/AdminSafeListMiddleware.cs?name=snippet_ClassOnly)] |
| 49 | +[!code-csharp[](ip-safelist/samples/Shared/ClientIpSafelistComponents/Middlewares/AdminSafeListMiddleware.cs?name=snippet_ClassOnly)] |
39 | 50 |
|
40 | 51 | ## Action filter |
41 | 52 |
|
42 | | -If you want a safelist only for specific controllers or action methods, use an action filter. Here's an example: |
| 53 | +If you want safelist-driven access control for specific MVC controllers or action methods, use an action filter. For example: |
| 54 | + |
| 55 | +[!code-csharp[](ip-safelist/samples/Shared/ClientIpSafelistComponents/Filters/ClientIpCheckActionFilter.cs?name=snippet_ClassOnly)] |
| 56 | + |
| 57 | +In `Startup.ConfigureServices`, add the action filter to the MVC filters collection. In the following example, a `ClientIpCheckActionFilter` action filter is added. A safelist and a console logger instance are passed as constructor parameters. |
| 58 | + |
| 59 | +::: moniker range=">= aspnetcore-3.0" |
| 60 | + |
| 61 | +[!code-csharp[](ip-safelist/samples/3.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServicesActionFilter)] |
| 62 | + |
| 63 | +::: moniker-end |
| 64 | + |
| 65 | +::: moniker range="<= aspnetcore-2.2" |
| 66 | + |
| 67 | +[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServicesActionFilter)] |
| 68 | + |
| 69 | +::: moniker-end |
| 70 | + |
| 71 | +The action filter can then be applied to a controller or action method with the [[ServiceFilter]](xref:Microsoft.AspNetCore.Mvc.ServiceFilterAttribute) attribute: |
| 72 | + |
| 73 | +[!code-csharp[](ip-safelist/samples/3.x/ClientIpAspNetCore/Controllers/ValuesController.cs?name=snippet_ActionFilter&highlight=1)] |
| 74 | + |
| 75 | +In the sample app, the action filter is applied to the controller's `Get` action method. When you test the app by sending: |
| 76 | + |
| 77 | +* An HTTP GET request, the `[ServiceFilter]` attribute validates the client IP address. If access is allowed to the `Get` action method, a variation of the following console output is produced by the action filter and action method: |
| 78 | + |
| 79 | + ``` |
| 80 | + dbug: ClientIpSafelistComponents.Filters.ClientIpCheckActionFilter[0] |
| 81 | + Remote IpAddress: ::1 |
| 82 | + dbug: ClientIpAspNetCore.Controllers.ValuesController[0] |
| 83 | + successful HTTP GET |
| 84 | + ``` |
| 85 | +
|
| 86 | +* An HTTP request verb other than GET, the `AdminSafeListMiddleware` middleware validates the client IP address. |
43 | 87 |
|
44 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Filters/ClientIpCheckFilter.cs)] |
| 88 | +## Razor Pages filter |
45 | 89 |
|
46 | | -The action filter is added to the services container. |
| 90 | +If you want safelist-driven access control for a Razor Pages app, use a Razor Pages filter. For example: |
47 | 91 |
|
48 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServices&highlight=3)] |
| 92 | +[!code-csharp[](ip-safelist/samples/Shared/ClientIpSafelistComponents/Filters/ClientIpCheckPageFilter.cs?name=snippet_ClassOnly)] |
49 | 93 |
|
50 | | -The filter can then be used on a controller or action method. |
| 94 | +In `Startup.ConfigureServices`, enable the Razor Pages filter by adding it to the MVC filters collection. In the following example, a `ClientIpCheckPageFilter` Razor Pages filter is added. A safelist and a console logger instance are passed as constructor parameters. |
51 | 95 |
|
52 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Controllers/ValuesController.cs?name=snippet_Filter&highlight=1)] |
| 96 | +::: moniker range=">= aspnetcore-3.0" |
53 | 97 |
|
54 | | -In the sample app, the filter is applied to the `Get` method. So when you test the app by sending a `Get` API request, the attribute is validating the client IP address. When you test by calling the API with any other HTTP method, the middleware is validating the client IP. |
| 98 | +[!code-csharp[](ip-safelist/samples/3.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServicesPageFilter)] |
55 | 99 |
|
56 | | -## Razor Pages filter |
| 100 | +::: moniker-end |
57 | 101 |
|
58 | | -If you want a safelist for a Razor Pages app, use a Razor Pages filter. Here's an example: |
| 102 | +::: moniker range="<= aspnetcore-2.2" |
59 | 103 |
|
60 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Filters/ClientIpCheckPageFilter.cs)] |
| 104 | +[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServicesPageFilter)] |
61 | 105 |
|
62 | | -This filter is enabled by adding it to the MVC Filters collection. |
| 106 | +::: moniker-end |
63 | 107 |
|
64 | | -[!code-csharp[](ip-safelist/samples/2.x/ClientIpAspNetCore/Startup.cs?name=snippet_ConfigureServices&highlight=7-9)] |
| 108 | +When the sample app's *Index* Razor page is requested, the Razor Pages filter validates the client IP address. The filter produces a variation of the following console output: |
65 | 109 |
|
66 | | -When you run the app and request a Razor page, the Razor Pages filter is validating the client IP. |
| 110 | +``` |
| 111 | +dbug: ClientIpSafelistComponents.Filters.ClientIpCheckPageFilter[0] |
| 112 | + Remote IpAddress: ::1 |
| 113 | +``` |
67 | 114 |
|
68 | | -## Next steps |
| 115 | +## Additional resources |
69 | 116 |
|
70 | | -[Learn more about ASP.NET Core Middleware](xref:fundamentals/middleware/index). |
| 117 | +* <xref:fundamentals/middleware/index> |
| 118 | +* [Action filters](xref:mvc/controllers/filters#action-filters) |
| 119 | +* <xref:razor-pages/filter> |
0 commit comments