I am building a private API which provides some data, I have already set CORS to only allow requests from my website, and that works, but there is a problem: a user can make the request with the chrome console from my website to the API and that request will succeed since the API has no real way to tell if the request comes from the code i have written.
So my question is: is there any way to tell that? is there any way to prevent users from making that request from my website to call the API and bypass the CORS?
You need to update your request model so that requests to the API come from the web server itself, rather than from a users client.
That way you can add authentication to the API and only allow requests that come directly from your web server. Then web server can then display those results to your users.
CORS is not a method for checking/enforcing authentication or authorisation. It is purely an additional security control to protect against browser-based security vulnerabilities (XSS etc).
Related
I am building a system with an ASP.NET Core web app (incidentally, in Blazor), which let's call "Site", and some domain web services (which might someday be used by other sites), one of which let's call "CustomerService".
Following various guides and articles on how to set up authentication with Open ID Connect and Azure Active Directory for this system, I see the following possible different approaches to authentication and authorization, especially with regard to AJAX requests:
Site-only auth, passthrough: Service trusts the site; site authenticates user.
Service-only auth, passthrough: Service authenticates user; site passes through all AJAX requests.
Service-only auth, CORS Service provides site client data via CORS, with authentication; site doesn't handle AJAX requests at all.
Service and site auth, passthrough: Service and site both authenticate user; site passes through some AJAX requests.
These all seem to have significant practical problems. Is there a fifth approach, or a variation I should be considering?
Here's my elaboration of these approaches.
(1) Service trusts the site; site authenticates user:
(1a) Set up Site.Server to use Open ID Connect for users to authenticate, implement all necessary authorization on Site.Server, pass through web API calls to CustomerService, and set up CustomerService to trust requests that come from Site.Server. This looks like a bad idea because then any user can spoof Site.Server and have full access to operations that should be secured on CustomerService. Also, CustomerService would not be able to enforce authorization; we'd be trusting Site.Server to get it right, which seems suboptimal.
(1b) Same as (1a), but Site.Server would know a secret API key that would be passed to CustomerService, either in headers or the API call's querystring or body. This doesn't seem so great because the API key would never change and then could be discovered and spoofed by any user. Still, this could work, as the API key could stay secret, and we could use our secret server for both sides to retrieve it. But still CustomerService would not be able to enforce authorization; we'd be trusting Site.Server to get it right, which seems suboptimal.
(1c) Same as (1b), but we come up with a mechanism for rotating the API key occasionally. This doesn't seem so great because the API key would change and then could be discovered and spoofed by any user. Still, this could work, as the API key could stay secret, and we could use our secret server for both sides to retrieve it. But still CustomerService would not be able to enforce authorization; we'd be trusting Site.Server to get it right, which seems suboptimal.
(2) Service authenticates user; site passes through all AJAX requests: Avoid any authentication on Site.Server and instead enforce authorization/authentication on CustomerService only through Open ID Connect+Azure AD. Site.Server would have to pass through requests including headers to CustomerService. This has the benefit of putting the security in the right place, but it seems unworkable, as the user has no way to authenticate on CustomerService since the user isn't using CustomerService directly; their AJAX requests still go to Site.Server.
(3) Service provides site client data via CORS, with authentication; site doesn't handle AJAX requests at all: Avoid any authentication on Site.Server and instead use CORS to allow the user's browser to connect directly to CustomerService, requiring authentication only through Open ID Connect+Azure AD. This has the benefit of putting the security in the right place, but how can a user authenticate on a AJAX request without having done so in a human-browsable way first? My AJAX request can't redirect to microsoftonline and prompt the user, can it? Plus CORS seems like a bad idea in general--we want to move away from cross-site anything; to the user, it should appear that Site.Server is serving up both AJAX calls and HTML page requests, right?
(4) Service and site both authenticate user; site passes through some AJAX requests. Put authentication on both Site.Server and CustomerService, with the same app ID, making them appear as one and the same site as far as Azure AD knows. Site.Server could do its own authentication and restrict certain service calls from getting to CustomerService, or it could pass through requests, including headers, to CustomerService, which could then deny or grant access as well. This is what we decided to do, but I question it now, as if I add a
second service, now it has to have again the same app ID to keep this approach.
None of these approaches seem to hit the mark. Am I missing another approach that I should be considering? Or is there a variation I am missing?
Here are my thoughts on what an option 5 is:
WEB UI
Code runs in the browser and interacts with the Authorization Server to authenticate the user. A library such as OIDC Client does the security work for you.
Provides best usability and simplest code. The UI uses access tokens to call cross domain APIs. Renewing tokens is tricky though, and browser security requires some due diligence.
WEB BACK END
Is static content only, deployed around the world close to end users - perhaps via Azure CDN. Must execute zero code. Provides best performance.
WEB UI SAME AS MOBILE UI
Your Web UI in effect operates in an identical manner to a mobile UI and is quite a bit simpler, with less need for cookies + double hops.
ENTRY POINT API
The browser UI interacts with an entry point API tailored to UI consumers. This API validates tokens by downloading Azure AD token signing keys. It is also has first say in authorizing requests.
The entry point API orchestrates calls to Core APIs and Azure APIs such as Graph. The Web UI uses a single token scoped to the entry point API and you can strictly control the UI's privileges. Meanwhile the API can use Azure AD's 'on behalf of' feature to get tokens for downstream APIs so that the UI does not need to deal with this.
DOMAIN APIs
These typically run in a locked down private cloud and are not called directly by the outside world. This allows you closer control over which types of caller can invoke which high privilege operations.
BLOG POSTS OF MINE
My blog's index page has further info on these patterns and the goals behind them. Maybe have a browse of the SPA Goals and API Platform Architecture posts.
There are some working code samples on this page. In my case the hosting uses AWS instead of Azure, though concepts are the same.
I have a Lambda function which triggered by API Gateway service, however this API is accessed by front-end application, this application not requiring the users to login or sign up to use it.
However I would like to secure my API to allow only from my front-end application.
After my research I found that I can use custom authorization in API Gateway, this custom authorization will check the authorization header of the incoming request and validate it.
the question is, can I use Amazon Cognito for something like this(implicit grant type)?
if not what is the thing that the front-end application will send to me to be validated and how can I keep it always changeable, so no one can guess it?
Thank You.
You could check the headers, but if they're always the same, someone can send an HTTP request with those headers - from any client - and trick your Lambda into thinking it's coming from your UI.
Even if you generate a unique token every time your UI is loaded and include it in the headers, someone could take that token and send requests from another client as well.
You could build fancy JavaScript tricks to make headers more dynamic, but it would only make it harder to use your API from another client, not impossible.
I'm writing a web app with a separate frontend and backend. The frontend is written in React, and the backend is a node.js server running an Express endpoint. How do I ensure that only my frontend can access the API, and not anyone else? My API URL is exposed in my frontend client side code, so anyone can see that.
I added JWT authentication to my API, but I still need to have an unprotected /login endpoint in order to generate the JWT token, and in order to login to generate the token, I must post both a username and password from my frontend, which other users can see, since it's done from the client side.
What is the proper way of securing an API that is hosted on a separate backend like this, so that only my frontend can access it, in a way where nobody can see what credentials are being used to access the endpoint?
You can't. Your API is on the internet. Anyone can access it. You can require an account and login credentials for the account before allowing access to the API, but once someone has an account and credentials, they can access the API from their own script rather than via your web page. This is how the web works. Not much you can do about it. And credentials being used by the client cannot be hidden. All data that is EVER on the client can be looked at by a hacker on the client. This is the way of the web.
Larger companies will typically monitor their API usage to look for inappropriate use. This includes rate limiting, detecting behaviors and sequences that are not typical of a regular human user. When they detect inappropriate use, they will often disable that action or ban the offending account, either temporarily or permanently. This is also why some pages use techniques to detect if an actual human is individually causing the operation such as reCaptcha. For example, on stack overflow, when editing comments or posts, I often run into rate limiting where it tells me that I have to wait a bit before it will accept my edit.
There is no absolutely secure way to store credentials in a client. The most common scheme for credentials is to require username and password (securely over https) and then when that is accepted on the server as legit credentials, some sort of token is issued to the client which can be used for future API calls. That token may be in a cookie or may need to be manually included with each subsequent API call (the advantage of a cookie when using APIs from a browser is that the cookie is automatically sent with each subsequent request).
If the token is a cookie, then the cookie is stored in the browser's cookie storage and an expiration can be set for it. The browser's cookie storage is protected from access by web pages from other sites, but can be accessed by someone on the local computer (it's stored in the file system).
If the token is not a cookie, just returned as a token, and the client wishes to store it, there are a few other places that Javascript provides access to in order to store it. Local storage has similar security as cookie storage. It is protected from access by other web sites, but can be accessed by a person on the local computer.
Goal: My server needs to direct non-users to a landing/home page, and logged in users to the actual app. When the app is loaded, it will make authenticated HTTP requests to a RESTful API (via Ajax).
I have a RESTful API that needs authentication. On another server I have my website, which also needs authentication, so I can determine whether to display the landing/home page for non-users or the app for logged in users.
Initially I thought it would be enough to implement HTTP Basic Auth for the RESTful API. However, in order to get authentication running for my website too, I would also need to setup authentication there, which would mean duplicating the low-level code to check the credentials in the database in both the REST API and the website servers.
Alternatively, I wondered if the website could authenticate via the RESTful API. For example, in my request handler for POST /login, I could make a GET request to my API, passing along the user credentials from the request body. If the request returns 200 OK, I could sign the user’s session, thus authenticating them. From there onwards, the Ajax requests to the REST API need to be authenticated with the same credentials, so I could:
set a cookie containing the credentials, thus allowing the JavaScript to retrieve the credentials before doing the request (OK with SSL?)
dump the credentials in the served HTML for the web app thus allowing the JavaScript to retrieve the credentials before doing the request (OK with SSL?)
proxy the API through the web app server, where I could retrieve the credentials from the session and add them to the Authorization header of the proxied request?
Alternatively, I imagine I could just share a session between the two servers, although I’ve heard that’s bad practice for RESTful design.
What would be wrong with doing it like this? Is there a better way to meet my goal?
I recently implemented something similar to this (assuming I understand you correctly), and there seemed to be a few viable options.
Have the server side of your web-app always authenticate with a specific username/password when accessing the REST API, ensuring that your web-app is always trusted and assuming that users are properly logged in on the web-app if a request is authenticated as the app.
Pros: Easy to implement, easy to understand, easy to extend for other applications as well (we had a CLI that accessed the same REST API as well).
Cons: It's impossible for the REST API to know which user is actually accessing it. If a trusted client is compromised the whole system is compromised.
Have the server side of your web-app keep user details in the session and authenticate using the users credentials every time you access the REST API.
Pros: Fairly easy to implement (although some authentication mechanisms make it hard to keep hold of the user password - for good reason). The whole procedure is transparent to the REST API.
Cons: You're now storing (for all intents and purposes in clear-text) the username and password of a user in the session of the web-server - one of the most prime targets for attack in the system.
Create an authentication system on the REST API that authenticates a request with a username/password authorization and returns a token that is valid for a limited time.
Pros: More secure, if your web-app is compromised you're not providing the attacker with your users username/passwords, but instead only allowing them a limited time access.
Cons: Much harder to implement. You might need to deal with token timeouts specifically. For purists it also means that your REST implementation (or at least the authentication system) will be arguably "stateful".
What you should implement would depend on your situation. Personally I'd definitely go with the more secure option (the last one), but due to external constraints we were forced to implement the first option in our specific case (with the promise we'd revisit it and upgrade later - unfortunately later never comes).
I think your approach with using Basic HTTP Authentication in REST service and having your app authenticate with the service is perfectly fine. The only caveat here (which I am sure you are aware of), is that your REST service should run over SSL, as Basic HTTP authentication is not very secure - username and password are just Base64 encoded.
I have a web service built with WebAPI that accepts JSON requests and responds accordingly. The core architecture is built but there isn't any authentication/authorization.
After a lot of googling and poking around sample projects, I'm not sure where to start. I've found a ton of material from 2008 and 2009 but not a whole lot of recent guides/workflows for WebAPI / single page apps. I think the workflow should be as follows:
Check to see if the user is logged in: How can this be done with javascript? Do I send a cookie to my webAPI? If so, do I send that cookie as a parameter in the body of the request?
Let the user log in / register: How is this data encrypted/decrypted? Surely I can't be sending passwords over the wire... is this where SSL comes in?
Provide them with access to what they have rights to access: I think I got this - I can just authorize in the controllers on a per-request basis.
Any info would be awesome.
Basically you need a token based authentication or authorization.
If you are referring to the ASP.NET WebAPI, the following project will be a great place to start:
http://thinktecture.github.com/Thinktecture.IdentityModel.45/
Even if you are not using ASP.NET WebAPI, the following video is a great introduction on how to provide authentication/authorization on RESTful web services:
http://vimeo.com/43603474
To answer some of your questions:
Check to see if the user is logged in: How can this be done with javascript? Do I send a cookie to my webAPI? If so, do I send that cookie as a parameter in the body of the request?
You can use a cookie but I normally use the header in order to avoid common XSRF attacks. Cookies are automatically included whenever a http request is sent from the browser.
is this where SSL comes in?
Yes. If you are going to go ahead with the token based approach, you can use a separate server (Identity Server) to do the authentication for you.
JavaScript clients are unique. Do you have the Web API and the page serving up JavaScript in the same domain? If not, you have same origin policy restrictions. If you have the same Web application hosting the web pages and Web API, you can use forms Authn. In that case, you don't need to send the cookie containing the authentication ticket yourself from JavaScript. Browsers do that for you and that is the cause of XSRF problem. You have to be careful about JavaScript sending credentials that the end user is not supposed to know. If JavaScript knows something, any intelligent end user can get to that knowledge. OAuth 2.0 implicit grant could be a good choice. The end user enters the credentials (password) in the authorization server which issues an access token. JavaScript gets the token and presents it to the web API but it will never have access to the credentials.