Get user from device/warden session cookie - ruby

I need to deserialize the session cookie by hand to extract the user from it. The enviroment is rails, though it is not in the http server context, so there is no request for devise to automatically deserialise. There is the Warden::SessionSerializer, though I did not really get how to use it.
Before I wasn't using Devise so I used this method to deserialize the rails session cookie:
def decrypt_cookie(cookie)
cookie = CGI.unescape(cookie)
config = Rails.application.config
encrypted_cookie_salt = config.action_dispatch.encrypted_cookie_salt
encrypted_signed_cookie_salt = config.action_dispatch.encrypted_signed_cookie_salt
key_generator = ActiveSupport::KeyGenerator.new(config.secret_key_base, iterations: 1000)
secret = key_generator.generate_key(encrypted_cookie_salt)
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
encryptor.decrypt_and_verify(cookie)
end
Since some people might be skeptical whether there is a context in which manually deserializing the cookie can ever be proper:
The client (browser) authenticates via HTTP, and the session is of course stored in the cookie. Rails is also running (rails runner) the websocket server that responds to certain privileged actions. For these I need to (re-)authenticate the user. If someone sees a different (better) way of doing that, I'd be glad to read it. :)

The way I describe for deserializing the cookie is just the right way. It is actually the same in Devise, the keys just changed and I jumped to quickly to the conclusion that there must be a different serilization process going on.

Do you have to use Devise? I posted previously on using Warden to roll your own token auth which you have complete control over. If you decide like me to just do the easy thing and implement auth yourself you will see its easy to lookup a user from a token via your models.

You can access the session object in your controller. What key do you use to store the user_id? You should just be able to do session[:key], so if you store the id as user_id, then session[:user_id].

Related

How can I handle the most basic user auth in Spring Boot?

I've implemented really basic user authentication before - generate a session cookie when logging in, and when the user loads a page with authentication just check the cookie.
However, the complexity of Spring Security / Apache Shiro is really confusing me.
All I really want is:
be able to have a user log in once and see their username on every page they visit (like on Stackoverflow)
have this login persist for a reasonable length (cookie expiry time or something like that)
It looks like I have the option of using EhCache or implementing my own subclass of... something... to use something like postgres.
I seem to have gotten the first thing I want working in Apache Shiro:
Subject sub = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(remember);
try {
sub.login(token);
// ...
But I'm super stuck on how to get this session to persist between restarts of the spring webserver. Like, I know Stackoverflow highly recommends a code example but I literally don't even know where to start. Right now my "progress" on trying to figure out how to persist sessions between restarts (bolded to clarify what I'm asking) is literally the single line
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
// ...
dwsm.setCacheManager(new EhCacheManager());
and I don't even know what that actually does.
I would really appreciate any kind of guidance here since I really don't even know where to begin and the documentation I've been able to find is unfortunately not super thorough.
thats one of the problems with just sessions. That they are not persistant over restarts unless you save your sessions.
Now days people usually use a token based approach like oauth2.
The flow in a token based authentication system is:
User sends his/hers credentials to an authorizationserver (oauth2).
If credentials were correct they get a token back
The client (webpage) makes a request to the backend to get the user object and supplies the token in this call as an "Authorization"-header.
The backend takes the token and sends it to the authorizationserver (oauth2) to check its validity.
if the token is valid the backend server fetches the user object and sends this to the client.
The client gets the username from the user object and stores this in its state (react, redux if such an app). In every call to the backend the client must supply the token it got from the first login so that the server always knows that the caller is whom he/she claims to be

Number of external requests when serving a resource

So, I have a web application that makes a lot of requests to the server for data. A project requirement is to have very fast server response times. The server is hosted on a cloud based platform.
The app uses sessions to keep track of user authentication once they've logged in. Since it's hosted on a cloud provider, I'm using a cache to back up session storage (in my case it's Auzre cache, but if you're unfamiliar with that think Redis)
The current flow is like this:
The user accesses a resource
Trying to get the session based on the session ID via the cache. This is a cache request.
User is authenticated via session state (if logged in).
Request for data is sent, usually via cache.
Data is returned to the user.
The problem with this approach is that it's hitting the cache twice. Removing the session altogether caused a significant speed improvement (about 50%).
I was considering hitting the cache once, asking for both the key I need for the user, and the SessionID to save the extra round trip. However, I've never seen this approach before, and it requires 'rolling my own session' as I'd have to generate the session IDs and such. I feel like there might be a simpler, easier way.
So, what would be the most efficient way to serve the user a resource and authenticate them?
Note: I'm using ASP.NET MVC/ WebAPI with C# but I don't find that very relevant to the question, so I left the language and platform out of the question because of it.
You would want to combine the authenticate and resource request steps into one single request. Not only is this faster, it is also safer than your implementation. Consider the following scenario:
User authenticate to the server. Result is success.
Authentication is changed. (e.g. user changes password, admin decides to lock the user account, etc.)
User makes a request using the sessionID in step 1.
To ensure that the user is not granted access to the resource, you'd want to authenticate the user precisely at step 3. But that doesn't make sense, you've already authenticated this user previously in step 1...
HTTP, in is core, is designed exactly to do this. There are various ways to pass authentication information along with the request, such as:
Write the authentication information in the content (stupid, but works)
Include authentication in the url, e.g. example.com/pic/123?sessionID=abc (better, but makes your url ugly and long)
Store session info in cookie (better, but what if the client does not support cookie? what about cookie expiration?)
Authenticate HTTP header (my best personal recommendation)
HTTP itself has an authenticate header meant to be compatible with "basic authentication" (it is well defined, look it up if you're interested). But you can implement your own custom header.
Any cache lookup is bound to be slow (compared to computation), so you should omit the cache for the authentication part all together. Your server should be stateless; i.e. do not keep track of login sessions. How do you know if the sessionID is valid? Put a timestamp on it. And to avoid others faking the sessionID, sign it as well.
So, your HTTP request would look something like this (pseudo code):
var request = new HttpRequest();
request.url = "example.com/pic/123";
request.Headers["CustomAuth"] = "id=abc&t=123456789&s=01de45890a";
Implement your own signing method, sort of like a hash function (you can use HMAC), and keep the key securely on the server. If the signature matches, you know you've signed this previously at the login, and it has to be from your server. A timestamp helps you detect session expiration and also protect against replay attacks.
Now in your server, do something like this:
public void Get(){
var authHeader = Request.Headers["CustomAuth"];
if(!validate(authHeader)){
Response.StatusCode = 404;
Response.End();
}else{
//something else
}
}
If you need to do login + session authenticate + resource request in one request, then there're simply just 2 ways for authentication. Users can either provide a username/password combination, or a session key. Think about this scenario, which comes from an API I worked on:
user register with username/password combo
server responds registration success / failure (e.g. username already taken)
if it was a success, user now logins with username/password combo
server returns a session token
Wouldn't it be simpler (and faster) if we do it this way:
user register with username/password combo
if it is success, respond "reg success" and "sessionToken=xxxxxx". if it is failure, respond "reg failure".
I hope this give you some idea.
Also, you can remove the authentication at the server end by modifying / restricting settings on the server to cater to requests coming only from the ip(s) where your web app is hosted. Your web application will let the request pass to server only if its authenticated and hence all the requests reaching the data server will be served automatically without checking for any authentication. In this case you will just need one cache hit to check if the user is authenticated and straight away hit the server.

RubyCAS with a persistent single ticket & Sinatra

Alright, I'm a huge RubyCAS noob, and this is driving me crazy.
I have installed the rubycas-client gem, and have followed along with the official Sinatra setup at this repo. The way this setup is done, every time I request a page, I receive a one-time ticket that's appended to the URL as a query like so:
http://localhost:9393/?ticket=ST-1373928850... etc.
If I refresh the page, I get a Sinatra error saying the ticket has already been used up!
I've two questions, then.
Is the ticket-per-reload standard behavior?
How do I save my CAS login for a session and still retain single sign-out?
What I've done:
I have gone and tried to implement :sessions in Sinatra, but this causes single-sign-out to fail.
I have gone and done my best to follow the steps in the rubycas-client GitHub Repo (replacing ActiveRecord session storage with Sinatra's :session helper).
The RubyCAS documentation for Sinatra is fairly poor, so I'm looking for a definitive answer to this.
It is doing the correct thing when you try and reload the page with the same ticket. That ticket has already been validated. When you get the validation response you need to then set your own applications cookie or other session option.
I usually add a method that will add a session attribute to the user's cookie like:
session["cas"]["username"] = <user from cas validation response>
Then in future requests the Sinatra application can protect whatever routes you want with a helper method like:
cas = RestClient::Resource.new "#{cas_url}/login", :timeout => 5
checked = cas.get
return true if checked.code == 200
In my configure block for Sinatra I do this:
use Rack::Session::Cookie, :key => "example.com",:secret => "veryrandomhex"
I hope this helps, have any questions let me know.
UPDATE BELOW
While discussing this problem, we've uncovered that RubyCas says to not use a regular cookie session for your ruby application in production, while using CAS. What you'll want to do is:
A. Make sure your cookie expires at the same time or sooner than the CAS cookie
And/Or
B. Make sure your cookie is per browser session, then revalidate the CAS user on next browser session.
For Rack cookie you would specify this extra config for when the cookie is set to expire: :expire_after => 1440, (where 1440 is in minutes)
In case of the ruby CAS there are two kinds of session :
(1). The application session.
(2). The Single sign on (SSO) session.
you can use sinatra-session gem for managing the application session and just use session_end! helper method to destroy the application session. For destroying the SSO session unset the session[:cas_ticket] parameter in log out route.
example:
In case of the Sinatra:
get '/logout' do
session_end! # provided by sinatra-session gem
session[:cas_ticket] = nil # session variable set by CAS server
end
here we are explicitly setting the session[:cas_ticket] to nil, however you can use session.clear in logout route to destroy the current session data .

where should i set the session api and client

Here is the situation, I have setup 2 codeigniter installation.
One will be a client and one will be an api. Further improvement of this will be
The client will no longer be made from CI, since I wasn't using it's functionality. I just wanted to start out from a mvc framework right on.
My question would be where should I be storing sessions? during logins.
Below is how I did it, but I think I did it wrong.
I created a Login from the client. This one sends the login credentials to the api and then validated these information sent by the client and will return a message/response whethere the login credentials were valid or not.
If the login details were valid, the api will set a session in it's controller like this
if(true) {
$this->session->set_userdata($array);
}
This is in the login_controller I created. Is this the proper way of setting sessions for a client of a api?
You've got the concept right. You only want to set session userdata upon verifying the user supplied valid credentials.
That said, make sure you're using encrypted cookies and, if you're handling sensitive data, store your session data in the database. Storing it in the database causes some odd quirks with how sessions work in CodeIgniter (mainly with flashdata), but the added benefit of positive identification might potentially be worth it.
By storing the session data in the database, you can more positively verify a user is who they claim to be (in terms of the session ID, etc). The reason is because the session data is stored only in the database, and not in the session cookie (which only holds session ID and some other info). That way, even if someone does manage to decrypt the cookie, they can't modify their userdata to pretend to be someone else, like you might be able to with the cookies only method.

How to block external http requests? (securing AJAX calls)

I want to use post to update a database and don't want people doing it manually, i.e., it should only be possible through AJAX in a client. Is there some well known cryptographic trick to use in this scenario?
Say I'm issuing a GET request to insert a new user into my database at site.com/adduser/<userid>. Someone could overpopulate my database by issuing fake requests.
There is no way to avoid forged requests in this case, as the client browser already has everything necessary to make the request; it is only a matter of some debugging for a malicious user to figure out how to make arbitrary requests to your backend, and probably even using your own code to make it easier. You don't need "cryptographic tricks", you need only obfuscation, and that will only make forging a bit inconvenient, but still not impossible.
It can be achieved.
Whenever you render a page which is supposed to make such request. Generate a random token and store it in session (for authenticated user) or database (in case this request is publicly allowed).
and instead of calling site.com/adduser/<userid> call site.com/adduser/<userid>/<token>
whenever you receive such request if the token is valid or not (from session or database)
In case token is correct, process the request and remove used token from session / db
In case token is incorrect, reject the request.
I don't really need to restrict access to the server (although that would be great), I'm looking for a cryptographic trick that would allow the server to know when things are coming from the app and not forged by the user using a sniffed token.
You cannot do this. It's almost one of the fundamental problems with client/server applications. Here's why it doesn't work: Say you had a way for your client app to authenticate itself to the server - whether it's a secret password or some other method. The information that the app needs is necessarily accessible to the app (the password is hidden in there somewhere, or whatever). But because it runs on the user's computer, that means they also have access to this information: All they need is to look at the source, or the binary, or the network traffic between your app and the server, and eventually they will figure out the mechanism by which your app authenticates, and replicate it. Maybe they'll even copy it. Maybe they'll write a clever hack to make your app do the heavy lifting (You can always just send fake user input to the app). But no matter how, they've got all the information required, and there is no way to stop them from having it that wouldn't also stop your app from having it.
Prevent Direct Access To File Called By ajax Function seems to address the question.
You can (among other solutions, I'm sure)...
use session management (log in to create a session);
send a unique key to the client which needs to be returned before it expires (can't
be re-used, and can't be stored for use later on);
and/or set headers as in the linked answer.
But anything can be spoofed if people try hard enough. The only completely secure system is one which no-one can access at all.
This is the same problem as CSRF - and the solution is the same: use a token in the AJAX request which you've perviously stored eslewhere (or can regenerate, e.g. by encrypting the parameters using the sessin id as a key). Chriss Shiflett has some sensible notes on this, and there's an OWASP project for detecting CSRF with PHP
This is some authorization issue: only authorized requests should result in the creation of a new user. So when receiving such a request, your sever needs to check whether it’s from a client that is authorized to create new users.
Now the main issue is how to decide what request is authorized. In most cases, this is done via user roles and/or some ticketing system. With user roles, you’ll have additional problems to solve like user identification and user authentication. But if that is already solved, you can easily map the users onto roles like Alice is an admin and Bob is a regular user and only admins are authorized to create new users.
It works like any other web page: login authentication, check the referrer.
The solution is adding the bold line to ajax requests. Also you should look to basic authentication, this will not be the only protector. You can catch the incomes with these code from your ajax page
Ajax Call
function callit()
{
if(window.XMLHttpRequest){xmlhttp=new XMLHttpRequest();}else{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){document.getElementById('alp').innerHTML=xmlhttp.responseText;}}
xmlhttp.open("get", "call.asp", true);
**xmlhttp.setRequestHeader("X-Requested-With","XMLHttpRequest");**
xmlhttp.send();
}
PHP/ASP Requested Page Answer
ASP
If Request.ServerVariables("HTTP_X-Requested-With") = "XMLHttpRequest" Then
'Do stuff
Else
'Kill it
End If
PHP
if( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && ( $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' ) )
{
//Do stuff
} else {
//Kill it
}

Resources