So far I have mainly been using a single asp.net app metaphor where the razor pages are served together with the data from the same app, so I can protect certain controller actions for the ui and data controller actions using the same security.
My new app has a completely independent api web site (using servicestack) and a different asp.net UI app that consumes the api. Right now the two apps sit on the same server, but I want to support the ability for anybody to write a UI app that consumes my data, so the app could sit anywhere and could be a php app all I care.
The new UI uses razor and MVC but it is really a fully client side app, which requests data from the api site.
So, the problem is right there. I am used to automatically redirecting a page from the server side to the login when someone hasn't logged in yet. My UI's server side has no concept of the login situation of the api web site.
The best I can do right now is, at the begging of ANY UI page's load, to do a lightweight ajax call to the api web site, to get the current user info. If it's null, I do a client side document.location.href = .
This works, but has issues, primarily it causes a lot of client side busy ui stuff. An initial version of the UI loads empty (no data), then an awkward redirect to the login page happens. The client side app becomes chatty - every page that loads does an additional call to the api site. The server side redirect is clean because the first page you see is either the UI page that you already have access to or the login page.
My first question is, what is the best practice to do this kind of stuff? My second question is, is there a client side cookie on my UI page that deterministically tells me I am logged in to the api site? I inspected the cookies on a UI page before and after the login that sets the security to the api site and the cookies seem to be the same. My third question is - is there some kind of security actionfilter I can write for my UI mvc site, which somehow magically determines from the cookies of the request, whether the UI is currently logged in to the api site and if it is, it lets the request serve the page, if not, it does the sever side redirect to the login page.
Thanks
Edit:
Scott - thanks so much for the detailed explanation. One clarification - i use mvc only to really serve my client side html. I use all knockoutjs and Ajax calls to servicestack to render the data. My question is really about how to react to Ajax calls that return security exceptions and how to avoid presenting an empty html ui because the user is not logged in. My login page authenticates directly from html to ss bypassing the mvc part. It's not an spa where I can keep a single client side login state that applies to all views of the spa. There are multiple cshtml pages that all need to probe for login in order to not load empty and redirect to the login page...
So the MVC just serves the blank template, that includes the knockout js that will call the API to populate it? I believe this flow shows how your current pages are testing for a session using a lightweight ajax call to the api.
The problem with that approach as you have noted is that it has overhead, and a noticeable delay if there isn't a session.
Solution:
You can't test for the ss-id cookie in your JavaScript client application because of the origin difference. Knowing this cookie exists would give you an indication of whether a user might have a valid session. But seeing you can't access it, you have to work around this. When your login page successfully creates a session by calling the API, you should have the success method create a cookie that denotes that you have a session. Such as a hasSession cookie.
You can check for this existence of this cookie on each page load. It doesn't involve a server trip to verify it. If that cookie has the same expiration policy as the ServiceStack API cookie, then it should stay in sync.
The initial cshtml page state should hide the unpopulated page form contact using CSS, and show a loading indicator until the data is loaded from the API.
When the page first loads it should check if the hasSession cookie exists? If it doesn't then it shouldn't make any API calls, and should redirect immediately login.
How would I know that I can invoke ajax calls and succeed without making a test call?
You should just assume you have a session ss-id cookie if you have the hasSession cookie as you must have logged in successfully to get it. So make you call for the page data. If you get data back from the call and not a 401 exception then populate the form, and display it by altering the CSS.
If you got a 401 redirect to the login screen, and delete the hasSession cookie. The user won't have seen a blank unpopulated form because the CSS prevented this. They get a loading indicator while waiting, a perfectly reasonable state.
The 401 Authorization error should only occur once, and redirect to login, and that shouldn't even happen if your hasSession and the ss-id cookie expiration remain in sync.
I am just confused why you are trying to change the ServiceStack attributes now, subclassing [Authorize]. You shouldn't need to change the behaviour of the API.
Related
So I have few things to say I don't want to use cookies so things like express-session doesn't come as option.
I use nodejs with express with no front-end JavaScript and mysql as database. I don't really know how to do it so I would like to hear your opinion.
I already tried to search on internet.
When dealing with regular web pages, there are only four places in a request to store information that would identify a session.
Cookie sent with each request
Custom header on each request
Query parameter with each request
In the path of the URL
You've ruled out the cookie.
The custom header could work for programmatic requests and is regularly used by Javascript code with various types of tokens. But, if you need a web browser to maintain or send the session on its own, then custom headers are out too.
That leaves query parameters or in the path of the URL. These both have the same issues. You would create a sessionID and then attach something like ?sessionID=92347987 to every single request that your web page makes to your server. There are some server-side frameworks that do sessions this way (most have been retired in favor of cookies). This has all sorts of issues (which is why it isn't used very often any more). Here are some of the downsides:
You have to dynamically generate every single link in a web page so that it will include the right sessionID as part of the link so if the user clicks on it, the resulting http request will have the right sessionID included.
All browser caching has to be disabled or bypassed because you don't want the browser to use cached web pages that might contain the wrong sessionID.
User bookmarks basically don't work because they end up bookmarking a URL with a sessionID in it that won't last forever.
The user sees sessionID=xxxx in all their URLs.
Network infrastructure that log the URLs of requests will include the sessionID (because it's in the URL). This is considered a security risk.
All that said and with those tradeoffs, it can be made to work, but it is not considered the "safest" way to do it.
I am working on the logout part of my website and I am using JWT for authentication and using cookies to send the JWT for client side.
For logging out I am passing some dummy token value with the same token name so that it over rides the previous token. But when I log out of the portal, I am still able to access my dashboard. There is some glitch in the logout functionality. I guess it is due to the browser cache.
I have few questions regarding Express.
Are there any ways to reload the current page using Express and delete the browser cache while doing so?
I need to disable the browser forward option of chrome once the user is logged out, how can I achieve this using express?
How to redirect the user to his dashboard when he tries to hit '/login' or '/signup' route when he is already logged in? I am using the JWT authentication for login
Thanks in advance
Are there any ways to reload the current page using Express and delete the browser cache while doing so?
The server can't, on its own, tell the browser what to do. The browser has to initiate communications and then act on that communications.
You could have the web page in the browser reload its own page using Javascript with window.location.reload(true) at any time. If you want the web page Javascript to be told when to do this by the server, it could either send regular Ajax calls to the server and, based on the response, decide when to reload the page. Or, it could have a webSocket connection to the server and the server could send the web page some data that, when the web page received that data, it would see that it should reload its page.
We could help you better if you told us what the real problem was here. Web pages can use Javascript and/or webSocket connections to dynamically update themselves rather than just reload all the time. That's a more modern design.
I need to disable the browser forward option of chrome once the user is logged out, how can I achieve this using express?
There's a discussion of disabling the forward button here: HTML5 history disabling forward button. You will probably find this is a brute force approach (it involves getting rid of browser history) and there is likely a much better way to solve whatever real problem you're trying to solve. It also sounds like you may also want to manage browser cache expiration too.
How to redirect the user to his dashboard when he tries to hit '/login' or '/signup' route when he is already logged in? I am using the JWT authentication for login
When you detect a request to '/login' or '/signup' in Express from a user who is already logged in, you just respond with a res.redirect("/dashboard") from your server. FYI, there are lots of questions about whether this is the proper user experience. A user going to '/login' or '/signup' when they are already signed in could have any one of these use cases:
They don't realize they are already signed in or they don't know if they are signed in as the desired user.
They want to sign in as a different user.
They want to create a new account (different from what is currently logged in).
They are trying to figure out how to log out.
You should make sure that blind redirecting (and not taking the user to the page they asked to go to) still makes all these use cases entirely clear. If not, you will just frustrate the user by not taking them where they asked to go.
No matter how I reason about it, it seems as if there is no secure way of implementing a client side rendered single-page-application that uses/accesses information on sessions for authentication, either via cookies, without severe compromise in security. I was mainly looking to building a React app, but it seems as if I will need to build it with SSR for a relatively secure version of authentication.
The use case that I'm especially thinking of is where the user logs in or registers and then gets a cookie with the session id. From there, in a server side implementation, I can simply set up conditional rendering depending on whether the server stored session has an associated user id or not and then pull the user information from there and display it.
However, I can't think of a client-side rendered solution where the user can use the session id alone on the cookie that isn't easily spoofable. Some of the insecure implementations would include using browser storage (local/session). Thanks.
I think the major issue here is that you are mixing the two parts of a web page (at least according to what HTML set out achieve) and treating them both as sensitive information.
You have two major parts in a web page - the first being the display format and the second being the data. The presumption in client side rendering / single page applications is that the format itself is not sensitive, and only the data needs to be protected.
If that's the case you should treat your client-side redirect to login behavior as a quality of life feature. The data endpoints on your server would still be protected - meaning that in theory an unauthenticated user could muck about the static HTML he is being served and extract page layouts and templates - but those would be meaningless without the data to fill them - which is the protected part.
In practice - your end product would be a single page application that makes requests to various API endpoints to fetch data and fill in the requested page templates. You wouldn't even need to go as far as storing complex session states - a simple flag notifying the client if it is authenticated or not would suffice (that is beyond what you would normally use for server-side authentication such as cookies or tokens)
Now let's say I'm a malicious user who is up to no good - I could "spoof" - or really just open the browser dev tools and set the isAuthenticated flag to true letting me skip past the login screen - now what would I do? I could theoretically navigate to my-service/super-secret without being redirected locally back to the login page on the client side - and then as soon as the relevant page tries to load the data from the server with the nonexistent credentials it would fail - best case displaying an error message, worst case with some internal exception and a view showing a broken template.
So just to emphasize in short:
A. If what you want to protect is your TEMPLATE then there is no way to achieve this clientside.
B. If what you want to protect is your DATA then you should treat gating/preventing users from navigating to protected pages as a quality of life feature and not a security feature, since that will be implemented on the server when serving the data for that specific page.
Suppose I have the following URL route:
app.post('upvote', function(req, res) {
// make a database a call to increase vote count
});
What can I do to prevent others from opening up a console and sending AJAX POST request on www.mysite.com/upvote? I'd like it so that only www.mysite.com is allowed to make that POST request and no one else.
What can I do to prevent others from opening up a console and sending AJAX POST request
Who is "others"?
If others==users of the site... there is nothing you can do to stop them sending whatever requests they like, using the JavaScript console or any other means. You can't trust the client, so you have to have server-side authorisation: requiring that the user be logged into an account, and registering that the account has upvoted so can't vote again.
If others==admins of other sites (by serving script to their users that causes submissions to your site)... it isn't possible for JavaScript on another site to cause an AJAX POST request, or at least not unless you deliberately opt into that using CORS. But it's quite possible for them to cause a POST by simply creating a <form> pointing to your address and submitting it.
This is a classic Cross Site Request Forgery problem. The widely-accepted solution to XSRF issues is to include a secret token as a parameter in each POST (form or AJAX) submission. That secret token is associated with the logged-in state of the user, either by being stored in the server-side session, or replicated in a client-side cookie. Either way an attacker from another site isn't capable of getting hold of a token that is valid for the logged-in user, so they can't fake the request.
You need XSRF protection on all actions that have a direct effect, whether AJAX or form-based POSTs.
I agree with bobince. others is a very general term.
If others belong to other sites (malicious sites on net).
express has csrf middleware to protect from Cross Site Request
Forgery. You can use it to prevent such a scenario. See the API docs
here.
If others are users of your own site
then that is an authentication issue. Every request must be
checked before serving / executing it. You should implement a user
authentication to prevent this situation. I use passport, and
ensure that user is authenticated before I actually run app.post
handler.
Most frameworks I've looked at will insert into forms a hidden input element with the value being a CSRF token. This is designed to prevent user Bob from logging in on my site and then going to http://badsite.com which embeds img tags or JS that tell my site to execute requests using Bob's still logged in session.
What I'm not getting is what stops JS on badsite.com from AJAX requesting a URL with a form on my site, regex-ing the CSRF token from the hidden input element, and then AJAX posting to my site with that valid CSRF token?
It seems to me that you'd want to use JS to insert the CSRF token into the form at runtime, pulling the value from a cookie (which is inaccessible to badsite.com). However, I've not heard this approach mentioned and so many frameworks do the simple hidden input with the CSRF token, I'm wondering if my solution is over-engineered and I'm missing some part of what makes the hidden input method secure.
Can anyone provide some clarity? Thanks!
what stops JS on badsite.com from AJAX requesting a URL with a form on my site
The Same Origin Policy (unless you subvert it with overly liberal CORS headers). JavaScript running on a site can't read data from a site hosted on a different host without permission from that host.
There are workarounds to the SOP, but they all either require the co-operation of the host the data is being read from (JSON-P, CORS), or don't pass any data that identifies a specific user (so can't access anything that requires authorisation).