Open utterances-bot opened 1 year ago
Seriously ? Let's create public (anonymous) endpoints which expose sensitive data and use security-by-obscurity to fix that ?? That's like the worst advise ever ?
Those endpoints should not be anonymous, but require proper authorization imo
Well, actually they should not even be there - they're the last resort for understanding what's going on.
I totally agree with preferring authentication over obfuscation, but not every project has an auth layer.
So, yes, if it's something that you're planning to leave there, especially on production, you should consider authentication. But if it's something used for quick checks - may be exposed only if a specific flag is set - then pure obfuscation can be ok.
Thanks for this nice post. I often add environment and version number in my web pages but I never though of doing it for an API.
The /conf is more controversial. First there is the security issue. I would never ever expose the settings as such, even with proper authentication. If I need to know what happens, I log it.
But there is also the problem of accuracy: some settings are retrieved as IOptions
Good catch, I haven't thought about that.
I'm used to IOptions, therefore I haven't tested it with IOptionsSnapshot.
Is there a way to catch all the configs regardless of when they were set? My only guess is by wrapping all the application config within a root key, such as
{ "MyApplication":{ "SomeRealConfig": ... }
Now you can use IOptionsSnapshot
Any other ideas?
Disclaimer: for security reason, I do not think an endpoint returning all the settings is a good idea, you should at least use a whitelist for the keys you want to return
If you want an endpojnt to return your configuration, your code is fine. If you want an endpoint to return the options your services are running with, then there are 4 issues
IOptions<TOptions>
is defined with the values of the configuration at the time of its instanciation. IOptionsSnapshot<TOptions>
is instanciated for each request, so it reflects the state of the configuration now.Action<TOptions>
, which is not in the configuration but in the codeIOptions<TOptions>
cannot but IOptionsSnapshot<TOptions>
and IOptionsMonitor<TOptions>
can), and, as far as I now, you cannot enumerate the namesThat being said, there is a way to get all the IOptions<TOptions>
that will be resolved by the service provider, or, to be more precise, there is a way to get all the options's type and then IOptions<TOptions>
can be requested from the service provider.
As you may know, only IOptions<>
is registered in the service collection so you cannot get them by looking for them. Instead we could relying on the fact that the options have to be configured and so an interface with a concrete type is registered.
var query = from d in builder.Services
where d.ServiceType.IsGenericType && !d.ServiceType.IsGenericTypeDefinition
let g = d.ServiceType.GetGenericTypeDefinition()
where g == typeof(IConfigureOptions<>) || g == typeof(IPostConfigureOptions<>)
select d.ServiceType.GenericTypeArguments[0];
var optionTypes = query.Distinct().ToList();
Now, in you endpoint you can retrieve all options like this
var options = optionTypes.Select(t => context.RequestServices.GetService(typeof(IOptions<>).MakeGenericType(t))).ToList();
Keep in mind that the options may not be serializable by defaut. Some use Func<..> as properties.
Also, you still won't know if you are using the singleton value or the snapshot value, and you will not be able to handle named options either.
Most of this is contextual, so logging the options when needed seems easier, safer and more accurate.
For more information on what is registered when you add the options: https://github.com/dotnet/runtime/blob/20d4e309ec4f081e4b1d47a8f937abfbb763a4d4/src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs#L26-L30
Here's a nice comment from David Osolkowski on Twitter:
For the configuration data, if you get IConfigurationRoot instead of IConfiguration you can use the built-in GetDebugView method that not only shows everything but also which config source it came from.
I was not aware of this. Thanks for the information.
I'd suggest using the ASP.NET Core built-in health checks with custom IHealthCheck
.
If there's any sensitive information, it shouldn't appear in your public endpoints. If you need to have an endpoints to launch some actions, they must be secured with query key at least. And also, if you just need to expose some environmental data, for example, to collect status or statistics from group of services, you can use custom healthchecks.
I'd suggest using the ASP.NET Core built-in health checks with custom
IHealthCheck
.
Well, Health Checks have a meaning - they tell you that the system is healthy.
The Env endpoint has a totally different meaning: it tells you which is the current ASPNETCORE_ENVIRONMENT
variable (and you can use it to print whichever info you like)
Well, Health Checks have a meaning - they tell you that the system is healthy.
You're right in general, but environmental data (such as env, build id, commit and so) is usually a part of diagnostic data, which you probably will want to receive together with "healthy" or "unhealthy" status update. So having these values in healthchecks is a natural way how to store and access em.
Hi, I have created AspNetCore.VersionInfo library to expose environment and custom information, pluggable by ad hoc providers. I have read also your interesting comments about authentication issues and in the next version surely I will improve this aspect! Any contribution or suggestion is welcome!
The 2 secret endpoints I create in my .NET APIs - Code4IT
In this article, I will show you two simple tricks that help me understand the deployment status of my .NET APIs
https://www.code4it.dev/blog/my-2-secret-endpoints