web service security in asp.net - c#

i am working on project in which we are creating a web service that is called from desktop application.my problem is that how i provide security to the web method when they called from desktop application so that only authenticated member can access that method.
How I pass user name and password through SOAP header.
But at not all the time when we call a method i want to authenticate the user name and password .i want to authenticate a user only for the first time when he called a web method and for next call a token will generate that will we used for future references.
Please give me solution for all that problems immediatly.
or another way to impliment security to web service.
Please give solution with coding.

You need to set the authentication type in IIS on the webserver. http://technet.microsoft.com/en-us/library/cc733010(WS.10).aspx
The type will depend on your application (in-house app, external etc.) and how important security is to you.
You could store the credentials in an encrypted section in your client app.config file. Then authenticate each time you call the webservice.
Note that Basic Authentication sends the username/password combo in the clear (base64 encoding) so it is recommended to use SSL with this.
You can create credentials to pass to the webservice like so (where ConfigurationUtility is a custom helper class and WebServiceCredentials retrives the credentials from a custom config section in the config file).
CredentialCache credentialCache = new CredentialCache();
CredentialElement credentials = ConfigurationUtility.WebServiceCredentials;
NetworkCredential netCredential = new NetworkCredential(
credentials.UserName,
credentials.Password,
credentials.Domain);
You can programmatically encrypt config sections using
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(sectionName);
if (!section.SectionInformation.IsProtected)
{
// Protecting the specified section with the specified provider.
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
}
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
Note: you don't need to decrypt the section, this is done for you.
You can accept (any and all) SSL certs programmatically
ServicePointManager.ServerCertificateValidationCallback += this.ValidateRemoteCertificate;
private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors)
{
return true;
}

Even if you pass the username and password through the soap headers anybody sniffing the packets will be able to lift this out easily. Even if you encrypt the data then a hacker can easily reuse the encrypted headers.
I would consider doing the following:-
Put a SSL certificate on the webserver (this is a VERY cheap option now)
Create a login service that accepts a username and password and then returns a token as a guid
When user successfully authenticates pass back the token and loginId, log this token guid and login id into the database on the server. Every time the user logs back in then recreate a new token guid
Every time the user calls another webservice pass the token and login and check against the database to make sure user is still logged in
You can every be clever with dates to see when a user last authenticated and expire them if need be.
Because you are using SSL the connection between the server and the client is secure. All data that gets transmitted including GET and POST data is encrypted

Related

How do I setup a valid on-premise ADFS URI?

I have a .NET 4.6.2 Windows client application which needs to get an authentication token from our on-premise ADFS server and use it to call an ASP.NET Core REST API. It's client name, id (GUID) and re-direct URI have been registered with ADFS. I am using the latest ADAL (v3.13) library to facilitate the authentication. I am attempting to get a token as demonstrated in the ADAL sample code like this:
AuthenticationContext authenticationContext = new AuthenticationContext("https://<adfs-sts-server>/<rest-api-host>", false);
var result = authenticationContext.AcquireTokenAsync(<rest-api-resource-uri>, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
The AcquireTokenAsync call returns an error, saying: The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
Can anyone tell me:
Is the "requested URI" refered to in the error the https://<adfs-sts-server>/<rest-api-host> or <rest-api-resource-uri>?
Do I need to register <rest-api-host> or <rest-api-resource-uri> with ADFS in some way, and if so how?
Any other information I need to get this to work?
Thanks!
Peter
Using Active Directory Federation Services (ADFS) to provide authentication for on-premise endpoints from a Windows Client
Configuring ADFS
There are 2 parts to configuring ADFS.
Register the client application with ADFS
ADFS needs to be able to identify the application requesting user authentication, whether it be a service, WPF application, Web client or Office Add-in. I have gone generic and added the following client, which we can use for most of our C# requests; we may need to register a new client with different callback for Web clients.
Use one of the many tools out there to generate a GUID for the client ID.
* CLIENT_ID and APP_NAME should be unique.
* For a web client the redirect URI is where the auth service will redirect your call after authenticating the user. It should be an endpoint where you can process the token and continue with your client application. The redirect URI is not really used with rich clients/services/add-ins.
CLIENT_ID = 26E54EC9-7988-4DAE-A527-483A8A78B1C6
APP_NAME = Investplus
DESCRIPTION = Invest+ rich client suite
REDIRECT_URI = https://server/redirect-adfs.html
Instructions for Client registration
(may be possible in a wizard, but this is what I found on the web and it worked fo us)
Log on to the AD FS server as administrator and open a Windows PowerShell command window.
Enter the following command. In Windows PowerShell
Add-AdfsClient -ClientId <CLIENT_ID> -Name <APP_NAME> -RedirectUri <REDIRECT_URI>
Register the resource to be accessed ('Relying Party' in ADFS speak)
I found this link useful, it takes you through the steps of the wizard for setting up a relying party.
Instructions for Relying Party registration
The administrator on the server team will need to use the ADFS Add Relying Party Trust Wizard, and under the "Select Data Source" step select Enter data about the relying party manually.
Values you need to supply for this wizard:
DISPLAY_NAME = "MyInvestApi" (Unique display name for this Relying party)
PROFILE = "AD FS Profile"
ENABLE_SUPPORT_FOR_WS-FEDERATION_PASSIVE_PROTOCOL = true
URL = "https://server/api" (Unique URL for this RP)
ADD_ONE_OR_MORE_IDENTIFIERS = eg. "urn:myInvestApi" and "https://server/api"
ACCEPT_REMAINING_DEFAULTS
when given the opportunity, Add Claim Rules:
SEND_LDAP_ATTRIBUTES_AS_CLAIMS = true
ATTRIBUTE_STORE = Active Directory
SELECT_USEFUL_ATTRIBUTES = User-Principal-Name; Email; Display-Name
Configuring/Coding the Client application
Microsoft provides Active Directory Authentication Libraries (ADAL) for a range of platforms and languages from C# to Javascript, and from iOS to Cordova to Node.
The API exposed has changed significantly in each major version: I am using the latest C# library, currently 3.13.5.
The library makes the coding very simple, just a few lines; where I had problems was:
I couldn't find an explanation of what URL to use for the ADFS
Secure Token Service (STS)
I couldn't find documentation of the whole process as I am doing here (most documentation focussed on Azure FS), I struggled to work out
how the values provided to ADFS for Client and Relying party mapped
to the values used in the code.
What is the ADFS endpoint/URL to use in code?
Microsoft's best practice is to name your ADFS/STS server URL https://sts.domain.com (some people use https://adfs.domain.com, ask your server admins). However, if you try to hit this from a browser you'll get a 404 - Not found and trying to retrieve a token in the code, the ADAL library reports:
The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
This is how I found the endpoint to use:
ADFS pubishes federation metadata at 'https://sts.domain.com/federationmetadata/2007-06/federationmetadata.xml'
Extract this file and open in a text editor.
When configuring the Relying Party, we specified "Enable Support for WS-Federation Passive Protocol" when specifying our resource endpoint, so search the XML for PassiveRequestorEndpoint.
Use the <Address> from this node - in my case https://sts.domain.com/adfs/ls/. I don't know if this will always be the value, or if it is specified when ADFS is setup and therefore potentially different per site.
What other values to use in the code?
We want our client app to retrieve a JSON Web Token (JWT) from ADFS which we can pass to our protected resource for authentication/authorization purposes.
At its most simple, the access token can be retrieved in 3 lines of code + configuration, and this will show how to translate what we have configured in ADFS to the values required by ADAL:
var stsEndpoint = "https://sts.domain.com/adfs/ls/";
var relyingPartyIdentifier = "urn:myInvestApi"; // Tenant in Azure AD speak, but this is an on-premise service
var authority = stsEndpoint + relyingPartyIdentifier;
var restResourceUrl = "https://server/api";
var redirectUri = "https://server/redirect-adfs.html";
const string CLIENT_ID = "26E54EC9-7988-4DAE-A527-483A8A78B1C6";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var asyncRequest = authenticationContext.AcquireTokenAsync(restResourceUrl, CLIENT_ID, redirectUri, new PlatformParameters(PromptBehavior.Auto));
var accessToken = asyncRequest.Result.AccessToken;
Useful references
ASP.NET Core Token Authentication Guide
ADAL - Native App to REST service - Authentication with ACS via Browser Dialog
Create a line-of-business Azure app with AD FS authentication
OAuth 2 Simplified
To issue the token for the web API, we need to make the ADFS to aware it by creating a relying party trust for the web API. And when we add a replying party we need to specify the identifiers for the replying party like figure below(Windows Server 2012 R2):
Then we can use this identifiers as the resource URI to acquire the token for this replying party. Please ensure that the resource URI is correct as you config like figure above.
And here is an article about developing with ADFS using OAuth:
Developing Modern Applications using OAuth and Active Directory Federation Services
Depending on the version of asdf, you may be able to use 'discovery' to obtain the endpoints to use.
Have a look at this post for more details: http://www.cloudidentity.com/blog/2015/08/21/openid-connect-web-sign-on-with-adfs-in-windows-server-2016-tp3/

Write cookie to client local machine in c#

I have a page A which generates another page B's URL with a query string containing encrypted authorization information to access to that page B.
When a user use that URL to access to the page B, I want to write a cookie with the authorization information into the user's local machine.
I read the MSDN reference and it does mention cookies can be generated and read through request and response, but is there a way to write the cookie to a specific location?
Is there also a way I can check whether there's already an existing cookie in the user's local machine before I generate my cookie.
Thanks in advance.
There is no way to check nor set cookies that are not under the same domain as the site performing the request.
You can only set or read cookies for the Same Origin as the site that is the setting the cookie. This is a security feature built in to HTML and can not be worked around.
For a easy to understand example of why this is this way, would you like www.myEvilSiteThatStealsYourData.com to be able to read or write cookies for www.theBankingWebsiteYouUse.com?
Url in page A should contain encrypted UserID and Timestamp symmetrically encrypted ,(like /B?SecurityToken=blabla), page B has to decrypt the security token, verify Timestamp is not too old (1 minute should be enough) and create cookie based on decrypted security token.
More robust scenario: A (identity provider) send user to B (service provider) without any token whatsoever; B checks if user have authentication cookie in B domain, if he does not, B redirects user to A's single login handler, if user have authentication cookie in A domain, A sends encrypted response to B, providing info about user (like UserID) otherwise A asks user for credentials, and if they're correct A sends encrypted response to B. B decrypt the UserID and creates authentication cookie in B domain. There are also protocols for this (SAML, OAuth) - in similar way you can use facebook or google to do identity provider for your app.

SmartCard security, how do you authenticate the certificate as not fake?

I'm trying to develop an ASP.net site that reads the clientCertificate to ensure a smart card was used to access the website (trying to do away with username/password login).
The process I have in my mind is:
User registers an account and C# records user's clientCertificate (public).
The user can then log in the next time with that same clientCertificate, and they are now an authenticated user if hash valid.
I will use the code below to ensure authenticity of certificate. The browser should deal with private keys and ensure the certificate was NOT faked.
Based on Subject+certificate combination, C# assigns them their role-access.
The following code can be used for authenticity of certificate right?
X509Certificate x509Cert = new X509Certificate(Request.ClientCertificate.Certificate);
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] hashvalue = sha.ComputeHash(Request.ClientCertificate.Certificate);
byte[] x509Hash = x509Cert.GetCertHash();
// compare x509Hash WITH hashvalue to ensure they are a match.
// If not, possibly faked certificate, not a real smartcard???
Is this how SmartCard authentication process should work???
If you just need to authenticate users with client certificates you should do this in IIS. You do not need to add any code at all to your application:
Specify Whether to Use Client Certificates (IIS 7)
Unless you need to link client certificates with database accounts or perform an additional validation step. But still for client certificate authentication I would stick with IIS settings.
Update:
In case you need to manipulate the client certificate you can do:
X509Certificate2 x509Cert2 = new X509Certificate2(Page.Request.ClientCertificate.Certificate);
And then access its properties such as:
x509Cert2.Subject
However, leave the validation piece up to IIS. If the client presents a bad certificate your asp.net code will not even execute since IIS will reject it
See this thread, sir. You do not need to verify authenticity explicitly in your code. IIS will do it for you.
Does IIS do the SSL certificate check or do I have to verify it?
IIS even tries to check revocation lists (however, this is often disabled if the CRL is large). An OCSP responder should be used to validate in cases where the CRL is very large or latency in checking it is high http://www.axway.com/products-solutions/email-identity-security/identity-security/va-suite.
Client-certificate authentication is done during the SSL/TLS handshake.
It is usually done using a Public Key Infrastructure, whereby the server has a (fixed) list of trusted CA certificates which it uses to verify the client certificate (in the same way as clients to it for the server). Once the certificate is presented to your application after this stage, you will know that:
the client has the private key for that certificate (guaranteed by the Certificate Verify message in the TLS handhsake (the SSL/TLS stack will verify this for you, no need to implement anything);
the client has the identity described in the certificate, because you will have verified it against your trusted CA.
The verification against a trusted CA requires the user to be registered with that CA in advance. You can't just authenticate any certificate if it hasn't been issued by a CA you trust. (Mapping the certificate's subject to a local user ID is another matter: you could do this upon first connection if needed: have your own database or directory service to map the Subject DN to another kind of user ID in your application, for example.)
User registers an account and C# records user's clientCertificate
(public). The user can then log in the next time with that same
clientCertificate, and they are now an authenticated user if hash
valid.
It sounds like you want to allow any certificate to be presented and use it for the initial registration, without necessarily resorting to a commonly trusted CA.
This is possible in principle, and I've done this to explore alternatives to PKI in Java.
To do this, you need to let any certificate through as far as the SSL/TLS handshake is concerned, and verify the certificate itself later. (You do need to use some form of verification.) You are still guaranteed with this that the client has the private key for the public key certificate it has presented.
Doing this requires two steps:
You need to be able to advertise the fact that you're going to accept any certificate, by sending an empty list of certification authorities in the Certificate Request TLS message (explicitly allowed by TLS 1.1).
Configure the SSL/TLS stack to trust any certificate (once again, when you do this, do not forget to implement your own verification system within your application, otherwise anything will really get through).
In .Net, while it should be possible to address the second point using a remote certificate validation callback, I have never found a way to alter the first point (this was also asked in this question).
In Java, the JSSE's X509TrustManager allows you to address both points.

Authentication Service using WCF

I have a custom MembershipProvider as shown below. It validate user name and password against Active Directory. I would like to make this as an “authentication service”. This should work even if the client uses forms authentication or windows authentication.
There is a WCF “HR Service” which is providing employee information. The “HR UI” website is using “HR Service” WCF service. Now we need to ensure that any client using the “HR Service” should be authenticated using “authentication service” before accessing the operation of “HR Service”. If the client application is authenticated once, next time onwards it should not be validated again (till the application is closed). When a new instance of the client application is opened it need to be authenticated from beginning.
How do we achive it? Do we have any code samples for the end to end flow demonstration?
Note: I should be able to test it using self hosted services.
Note: The client can be of any platform (e.g. Java).
namespace LijosCustomValidation
{
public sealed class LijoMembershipProvider : MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
bool isValid = true;
//my logic to validate the user name and password
return isValid;
}
//other implementations of Abstract Methods from MembershipProvider
}
Your auth service should return a token if the auth is successful. This token in turn should then be presented to the HR service.
You have a couple of options as to what the HR service does at this point. It can either know the secret to validate the token, or it needs to call the auth service to validate the token.
The token should be some value that can be validated if you know the secret, so it could something, say the users id, that is symmetrically encrypted. Ideally it should have a time component in it to prevent replay attacks.
I'd suggest some something like
<hash value>|<token issue time>|<user id>
The hash value should be hash (sha1, md5, etc) of everything after the first pipe. You can then base64 encode the result and pass it around. Validating the token could then check the issue date was within a certain time-frame.
You also have the option of storing the token in the client in a cookie and passing as a cookie to the services, or making it a parameter on your services. There may be other options, depending on your client architecture & how you want to structure your services.

Hiding a queryString in an ASP.NET Webapplication

I have two webapplication, one is a simple authenticationsite which can authenticate the logged in user and redirects him then to another site.
Therefore I have to pass ther userId (GUID) to the second application. Currently this is done via the URL but i would like to hide this id.
Has anybody an idea how to do this properly?
[EDIT]: I can't use the Session because of the ApplicationBoundaries (2 different Servers)
This sounds like a tricky situation.
There are however several options you can use but it all depends on what your application does.
Let's call WebApp1 your authenticate site, and WebApp2 your distination site once authenticated.
Can WebApp2 not call WebApp1 behind the scenes? (Services)
THe problem with passing this Guid between applications is it's going through clear text, and considering it's a user id, if anyone manages to intercept this they will have access to WebApp2 for life. Whether you pass it in a querystring or form variable, it's still vulnerable.
If you can't use WebApp2 to query WebApp1, you should consider WebApp1 creating a temporary Guid that expires. That would be much safer long term, but as it's clear text is still susceptible to attack. The 2 web apps will also need access to the same data store.
Ultimately, i think the AUthentication Site should be a service which WebApp2 can consume.
Users should login through WebApp2, which will call WebApp1 securely for authentication.
WebApp2 can then manage it's own session.
If you can't use cookies because it's cross domain then encrypt it, with a nonce.
Setup a shared secret/key between the two servers; send the encrypted GUID and nonce combination to the second server. Unencrypt, check the nonce hasn't already been used (to stop reply attacks), then use the unencrypted GUID.
If you want to be extra tricky have a web service on app1 where it can check the nonce was actually issued (at this point you're heading towards WSTrust and a single sign-on solution, which generally solve what you're trying to do)
Even with cookies, as they're easily edited/faked, you should have some form of checking.
You have two ASP.NET web applications, and one application does nothing but authenticate a user?
this sounds like a job for....
Web Services!
Create a new web service on the authentication app (They are the .asmx extension), and add a single method that takes in the user and password etc, and returns authentication info.
Then import the WSDL on your 2nd app, and call the 1st app like it was a method. It will simplify your code, and fix your issue.
An Example:
AuthenticateUserService.asmx goes on the Authentication app:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AuthenticateUserService : System.Web.Services.WebService
{
[WebMethod]
public bool AuthenticateUser(string username, string passhash)
{
// Fake authentication for the example
return (username == "jon" && passhash == "SomeHashedValueOfFoobar");
}
}
Once this is setup, fire up your main app, and right click the project and click "Add Web Reference".
Enter the url to the asmx on the authentication app, and Visual Studio will discover it and create a proxy class.
Once that is done, we can call that method like it was a local method in our main app:
protected void Page_Load(object sender, EventArgs e)
{
// Now we can easily authenticate user in our code
AuthenticateUserService authenticationProxy =
new AuthenticateUserService();
bool isUserAuthenticated =
authenticationProxy.AuthenticateUser("jon", SomeHashMethod("foobar"));
}
So, what does this really do?
It eliminates the client from the authentication process.
Your current process:
Client Enters credentials to AppA
AppA redirects the client to AppB
AppB redirects the client back to AppA if the credentials match.
Is replaced with a server side SOAP call between AppA and AppB. Now its like this:
Client enters credentials in AppA
AppA asks AppB if they are good
AppA serves proper content to the client.
Pass the GUID through a session, best way.
http://www.w3schools.com/ASP/asp_sessions.asp
OR, since it's 2 different servers, pass the information by POST method:
http://www.w3schools.com/aspnet/aspnet_forms.asp
The other possibility is to store the session state in a database on the local server, and remotely access that database from the other server to see if the user has successfully logged in and within session timelimit.
With that in mind, you can do the entire authentication remotely as well. Remotely connect to the local database from the remote server and check the login credentials from there...that way you will be able to store the session and/or cookie on the remote server.
I would recommend AGAINST the hidden field proposition, as it completely counteracts what you are trying to do! You are trying to hide the GUID in the URL but posting the same information in your HTML code! This is not the way to do it.
Best choice is the database option, or if not possible, then use HTTP POST.
Use session variables or HTTP POST instead of HTTP GET.
Instead of passing it via a query string you should create a hidden form field with its value and then post to your 2nd page, which can then grab the posted value and it will be hidden from the user.
If the servers have a common domain name, you can use a cookie.
EDIT: Cookies will just hide the ID visually, it is still accessible. Same with hidden fields or using POST rather than GET. So if the ID is confidental and you want to avoid to send it over the network unencrypted, you need a different approach.
A solution could be to encrypt the ID on the auth server with a key which is shared by the servers. Another solution could be to generate a random GUID on the auth server, and then let the auth server directly inform the other server (over SSL) which ID the GUID corresponds to.
go for session mangement or use a HTTP Post as said in the above post.

Categories

Resources