I have a single ASMX web service that has a host of methods that can be called. However, I'm wanting to restrict certain methods so that the caller either needs to be authenticated in order to be able to call them, or even better can only be called from the local server in which the service is running from.
Basically those methods needing to be protected will be called by the Microsoft Windows Scheduler on the local server.
Is either option possible, and what is the best and preferred way of achieving this.
At the top of the web services you want to protect, you can do something like:
if (!Request.IsLocal)
{
Response.StatusCode = 401;
Response.StatusDescription = "Unauthorized";
return null;
}
You could also check if they are authenticated with the IsAuthenticated property.
Of the two methods you mention, by far the easiest is to restrict access to just the local server. You can do this through code (as another comment has noted), or by administration of IIS itself.
If you load up the IIS Manager and select the folder that your asmx file is in, you'll see on the right-hand side a section 'IP Address and Domain Restrictions'. Open this up, add a default deny rule, and then an allow rule for 127.0.0.1.
Be aware that using this method will restrict all services in this folder, so you may need to move this into its own folder if this isn't required or desirable.
Related
I currently have a service which is secured by TransportWithMessageCredential over https. This works great! I now need to add a bit of granularity to some operations on this service.
Lets say I have this method public IEnumerable<Project> GetProjects() now I need to add an additional method which will limit the projection to projects which the current user has access.
Is using code like this:
var uid = System
.ServiceModel
.OperationContext
.Current
.IncomingMessageProperties
.Security
.ServiceSecurityContext
.PrimaryIdentity;
var returnProjects = context.Projects.Where(p => p.ProjectManager.Equals(uid.Name));
going to leave me vulnerable to any type of attack?
I think this should be fine, since WCF will hit my custom UserNamePasswordValidator first and "authenticate" the user, then the code I have above will "authorize" them to get only their projects. Is there a flaw in my thinking here?
No, that is perfectly valid thinking. This is exactly the way that we implement user-specific security (with the minor exception that we use FormsAuthentication for identifying the user).
Before each request is processed, we always check the user and if there is anything suspicious about the request, we throw an exception.
The current situation is as follows:
We have an production .net 3.5 WCF service, used by several applications throughout the organization, over wsHttpBinding or netTcpBinding. User authentication is being done on the Transport level, using Windows integrated security. This service has a method Foo(string parameter), which can only be called by members of given AD groups. The string parameter is obligatory.
A new client application has come into play (.net 3.5, C# console app), which eliminates the necessity of the string parameter. However, only calls from this particular application should be allowed to omit the string parameter. The identity of the caller of the client application should still be known by the server because the AD group limitation still applies (ruling out impersonation on the client side).
I found a way to pass on the "evidence" of the calling (strong-named) assembly in the message headers, but this method is clearly not secure because the "evidence" can easily be spoofed.
Also, CAS (code access security) seems like a possible solution, but I can't seem to figure out how to make use of CAS in this particular scenario.
Does anyone have a suggestion on how to solve this issue?
Edit: I found another thread on this subject; apparently the conclusion there is that it is simply impossible to implement in a secure fashion.
sounds to me like you need to pull the security out into a seperate service ... go down a more federated route this way you can implement a handshake form of encryption using public and private keys to generate a secure session token in both situations.
this way you cna still get both windows a=uthentication and a custom solution in play whilst retaining your attributes on methods for security (I am assuming that you are implementing it this way.)
sounds like a fair bit of work though - I had to do this from scratch and ran into some cross domain / delegation issues. But I am sure the idea is good.
howver you will end up with a nice solid claims based secuirty model
You could get the callers Address:
RemoteEndpointMessageProperty clientAddress =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name]
as RemoteEndpointMessageProperty;
string address = clientAddress.Address;
My applciation works as follows
[user]----username/password/domain----->[WCF service]
then i access the domain server to see to which actual DB the user is associated,
after getting that, i validate the user in his actual DB(DB is per domain)
the problem is that i need a place to store the domain name for the following requests against the db.
for example,if the users calls a WCF service operation:
Test()
first the validation procedure is called, (WCF UserNamePasswordValidator) which validates the user password(which is sent as part of the header for REST or as part of the SOAP), and the next function to be called is the Test, but by then i cant tell the domain of the user(to actually serve the request agains that domain..)
I dont want to change the signature of each domain to
Test(string domain)
I cant simply access the headers since i expose the same methods both as REST and as SOAP and the authentication is different for each of them..(one is with headers as with Amazon S3 and the later is using the SOAP standard)
so basically i'm looking for a global, per call storage.(i want to avoid the Per-Call initiation method)
thanks.
EDIT:
Maybe i should use the ThreadStaticAttribute? will that work?
This will not work. You can't store anything in UserNamePasswordValidator. It even doesn't have access to OperationContext because it runs on different thread.
The way to do this is create custom message inspector and extract the information from custom message header to custom operation context extension as Frank mentioned.
WCF knows a Current OperationContext. You can write your own extensions for it. Unrelated to this issue, I used the same mechanics in this NHibernate Session management here, which may work in its concept for you as well. It accesses the InstanceContext, but the concepts are similar.
Is it possible to have one .NET MVC application, and have it accessible from different domains, in such a way that the content will be domain-dependant?
For example, both www(dot)site1(dot)com and www(dot)site2(dot)com will point to my server's IP, and to the same website in IIS. In that website my .NET MVC application will reside. Now, I want the ability to know which site (domain name) triggered the ControllerAction, and act accordingly (for example, display different content for the homepage in the Index action, or allow/prevent access to specific content assigned to a specific site).
I would appreciate any help on this. I can accept an extra parameter passed to all controller actions (probably using Routing), but if there's a more elegant solution that would be ideal.
Well, you can always get the domain from the Request.RawUrl property.
As Mercer mentioned, deploying these as two separate web apps would be a better solution though. If that isn't possible, I would try to design something relatively generic that would check the domain and return different Views for each domain.
I have written a blog post about how to do this with an example web application for download.
It uses an abstract base Controller that is aware of the site it is being called for - by creating controllers that inherit from this base class you have automatic access to the current "site" for the current request.
It also allows you to load all your sites from a single database - can save you a bit on hosting fees if you're on a shared host, or if you run your own server you don't have to set up a new database for each site you create.
You can easily access the domain name used in the request with something along the lines of the following:
switch(Request.ServerVariables("SERVER_NAME"))
{
case "www.site1.com":
//do something
case "www.site2.com":
//do something else
default:
//????
}
You can do this in anywhere you have access to the Request object.
An elegant solution would be to have 2 deployments for 2 domains, and to separate content.
You could still have common content, but separating the content without hardcoding this inside the application is a win situation.
If you use different databases to keep the data separate, then in the Session Start configure the application to use one of the databases based on the Server Name variable. Then place the working connection string in the session for the user.
protected void Session_Start(Object sender, EventArgs e)
{
NameValueCollection NVCSrvElements = Request.ServerVariables;
switch (NVCSrvElements.Get("SERVER_NAME"))
{
case "www.whatever1.com":
Session["ConnStr"]="db1 connection string";
break;
case "www.whatever2.com":
Session["ConnStr"] = "db2 connection string";
break;
}
}
Then use this connection string in the rest of the application.
I have a scenario in which I'm going to need an arbitrary number of servers to provide the same SOAP web service. I would like to generate one set of proxy classes and be able to supply them with a location to point them at the different servers at runtime. Unfortunately, it looks as though the wsdl:port node (child of wsdl:service) requires the address of a specific server to be hardcoded. It appears that due to this the URL will be baked into my proxy classes. I know that I could potentially modify this by hand-editing the generated proxy classes, or modifying the code generation, but I'd really prefer not to resort to that. I feel like there's got to be a better way to solve this problem. I just want to decouple the interface definition from the location that the service will be residing at. I'm using VS2008 and C#.NET if that's of any help though best would be a language-agnostic (SOAP or WSDL specific) general solution to this problem.
Why don't you load balance the web servers and then create a DNS entry for the load balanced IP address....essentially creating a web farm. This will allow you to reference the hostname rather than the static IP addresses and if you ever need to change the IP address of the load balancer or the web servers it is a one time change. Plus you then have redundancy and performance control.
If you're using a WebReference (pre-WCF) to get to the web service, you can simply set the Url property on the web service proxy class after you create it.
For WCF, you can provide a different endpoint address to the proxy class constructor, rather than using the default (among other possible solutions).
No, in .NET you can change the URL at runtime.
Service svc = new Service ();
svc.url = "Value read from config. file or some such"
output = svc.method (input);
When you add a web reference to your project, it places the address of the web service into the .config file of your application / web application. You can then simply change this setting in the config file to point to a different web service location, assuming of course that the services are identical.
The easiest solution would be to use a software load balancer such as HAProxy. At more cost, you could use a hardware solution such as Big-IP.
Here's a hint on how to decide the URL of WSDL. I´m just changing the port but it´s of course possible to make it more advanced.
public class PortChangeReflector : SoapExtensionReflector
{
public override void ReflectDescription()
{
ServiceDescription description = ReflectionContext.ServiceDescription;
foreach (Service service in description.Services)
{
foreach (Port port in service.Ports)
{
foreach (ServiceDescriptionFormatExtension extension in port.Extensions)
{
SoapAddressBinding binding = extension as SoapAddressBinding;
if (binding != null && !binding.Location.Contains("8092"))
{
binding.Location = binding.Location.Replace("92", "8092");
}
}
}
}
}
}
Put that in your Add_Code and add the following reference to your web.config.
<webServices>
<soapExtensionReflectorTypes>
<add type="Dev.PortChangeReflector,App_Code"/>
</soapExtensionReflectorTypes>
</webServices>
I hope you can get new ideas of this.
Client proxies have URL property you can set at runtime. To make it simpler, wsdl.exe utility has /appsettingurlkey key. When you generate a client proxy, it's constructor will check the key in appSettings and set the service URL accordingly. I believe WCF has this feature as well.
However, I would agree with #Matt and suggest you consider load balancing as the best solution in the long run.
Is this for scaling (each server provides the same data) or
for same API different data on each server?
For 2, then you can do as above, just change the service URL in code.
For 1, you could use round-robin DNS (e.g. you see multiple servers with at the command line type nslookup www.google.com).