How to get current user's session id?


(Maestro Magnifico) #1

Continuing the discussion from How to get current user?:

To be more clear - I’m trying to catch user’s session for my server-side script, placed on example.com when Discourse placed on forum.example.com. So CORS is the only right way to do this.

So we have https://meta.discourse.org/session/current.json end-point. I was playing with it couple days and I found it useful only for outputing user’s info on page, not for checking user’s session to pass it forward on server script, because it’s unsafe to do as I wrote here.

@eviltrout, can you please add user’s session id to this end-point? Or create another one just with session id? If I understand correctly, if I had user’s session id on my server-side script + acces to Discourse db (that I already have, thanks to Sam) then I can get user’s id and check he’s rights safely.


Discourse Auth Proxy
How to get current user?
(Robin Ward) #2

No I believe that would be a security violation. It means any site that wants to could retrieve the session id.

The correct solution is to run your script on the same server and read it from the cookie (as I suggested in the other topic.)


(Maestro Magnifico) #3

The thing is - any site can’t retrieve session id. You can do it only from allowed origins, there’s an option in Discourse for this:

I just tried to do this from my work domain - I got error that request was blocked. You can try yourself to access here via this script:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
    <title></title>
    <meta name="keywords" content="" />
    <meta name="description" content="" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    
    <script type="text/javascript">

$(document).ready(function() {
    
    $.ajax({
        type: 'GET',
        cache: false,
        url: 'http://forum.red-squadron.ru/session/current.json',
        dataType: 'json',
        xhrFields: {
            'withCredentials': true
        },
        success: function(result){
            $('.container').html(result);
            
        },
        error: function(result){
            console.log('Error: '+result['status']+': '+result['statusText']+' '+result['responseText']);
        }
    });
    
}); /* END OF document.ready */
    </script>
</head>

<body>
    <div class="container"></div>
</body>
</html>

And even if you could access here from any server, this wouldn’t give you anything. Because, to be able to get info of my session you must have my cookie in your browser. So CORS is a very safe thing, the only way to hack into someone’s session is to stole user’s cookies.


(Robin Ward) #4

I think there is some confusion here. What do you consider the sessionId if not the cookie?

Your session cookie is an encrypted blob of data. Do you want to return that via CORS? Because I really disagree with that. I don’t think it’s safe to rebroadcast that information.


(Maestro Magnifico) #5

Why it’s unsafe if I already have it in my browser? I can open it and read it.

This CORS request will just send it to server, as it already doing when you send requests to /session/current.json. And will return data that user already have. So what do you afraid here? Sniffing? Sniffing can be done on login form anyway. Although I’m not sure entirely how sniffing works, I’m not a hacker, but when I’m watching this forum, there is cookies with session id in requests and answers. Looks like same deal to me.

If there’s a better way to catch user’s session on example.com when Discourse is on discourse.example.com, please share ideas. For now, I don’t see any ways of doing this. There is one via /session/current.json but it’s extremely unsafe and stupid.


(Maestro Magnifico) #6

Discourse stores in DB pairs of user id = session id, right? I want to get user’s id in my php scripts by checking he’s session id. It’s the only safe way of checking user’s privileges in back-end logic that I see.


(Robin Ward) #7

No it does not. Your cookie is signed by the server with a secure key. The server verifies it wasn’t tampered with and retrieves your user id from that cookie.

If anyone has your cookie, they can impersonate you.


(Sam Saffron) #8

Minor expansion. They can impersonate, however as soon as you log out or change password the impersonator will be logged out.


(Maestro Magnifico) #9

Sounds almost the same. :smile: Anyway, riking sugested another solution here, I will try using it to catch user’s session. I just hope that this plugin will send all data that I need each time some user logs in or logs out.


(Jens Maier) #10

Uuuh… no?

_forum_session contains session data. It’s encrypted and signed alright, but it’s not used to determine the user’s identity. For that purpose, the _t cookie contains a simple token that’s used as a key into the users table.


(Robin Ward) #11

Huh this is really weird.

@sam we don’t use the actual session cookie for auth at all? When/why did we switch to a DB lookup based on _t?

Why have the other session cookie if we aren’t using it?


(Maestro Magnifico) #12

What’s this?
https://meta.discourse.org/session/csrf.json


(Jens Maier) #13

That’s the CSRF ham value. It’s used to validate requests so that foreign sites can’t lure you into clicking a harmless-looking link that actually goes to your Discourse instance and does something nefarious.


(Maestro Magnifico) #14

Uh… :unamused: I’m looking at my DB structure at the moment. If only we had a way to get this thing out via CORS request.

@eviltrout, what would you say? Can you add it to /session/current.json? This value doesn’t expose anything, you can’t use it to access anywhere or impersonate user. This would allow admins to use Discourse users, groups, trust levels and privileges on root domain, when Discourse placed on subdomain. Like this:

  1. Get auth_token via CORS request.
  2. Send it via AJAX to back-end script on root domain.
  3. Back-end script gets user’s id like this:
    SELECT users.id FROM public.users WHERE users.auth_token = 'blablabla'
    Then checks privileges for user’s id and returns html of page / tools / anything.

ADD: Is auth_token safe though? Can anyone get my auth_token here on meta?


(Jens Maier) #15

The auth_token value is stored in the _t cookie. That cookie is set to http-only so that JavaScript cannot access its value. This is done on purpose to mitigate the impact of XSS vulnerabilities because the auth token is considered password equivalent data (if you know it, you can use that account; at least until you or the legitimate account owner log out).

If /session/current.json included the auth token, an attacker who can successfully inject JS code into Discourse can easily and silently steal your auth token. Granted, that’s not much of a benefit considering that a successful XSS attack can basically take control of everything and do everything that your user can do, but still; it’s something.

And yeah, while CORS and similar techniques make it very hard to obtain that data, it would mean that your auth token’s safety depends on your browser to a much greater degree than as it does now. It’s just not a good tradeoff.

On the other hand…

Why don’t you just have a plugin monkey-patch log_on_user in default_current_user_provider.rb and make it set the _t cookie to the root domain?

class CookieShim
  def initialize(cookies); @cookies = cookies; end
  def []=(cookie, value)
    if cookies == Auth::DefaultCurrentUserProvider::TOKEN_COOKIE
      value[:domain] = 'example.com'
    end
    @cookies[cookie] = value
  end
end
um = Auth::DefaultCurrentUserProvider.instance_method(:log_on_user)
Auth::DefaultCurrentUserProvider.send(:define_method, :log_on_user) do |u, s, c|
  um.bind(self).call(u, s, CookieShim.new(c))
end

This is on a new level of hackiness, but it might just work… :grin:


(Maestro Magnifico) #16

I like the idea, but I don’t have skills to do it. I don’t know Ruby or how to monkey-patch. Can you create such plugin? Will be much appreciated. It sucks that community doesn’t have this feature yet. I can’t even add chat to my website that would pick nicknames from Discourse session. And I’m not the first one who asked this.

ADD: It should also delete cookie when user logs out.


(Jens Maier) #17

Take that snippet above, slap the comments on top that make it a valid plugin, wrap the code in an initializer and you’re done. Ruby is really easy to learn, if you’re good with jQuery, you can understand what’s going on above in a weekend. :slight_smile:


(Maestro Magnifico) #18

Oh mighty Lucifer! I think I’ve found it:

I just registered on a website that this plugin made for and here’s my cookie: