What can people do if they steal a Phoenix Token?

  1. Is the uuid, used to sign a token, available for extraction to anyone who manages to steal a token from a user?
  2. If they submit that stolen token to the system, will they assume the victim’s identity?
2 Likes
  1. You can’t get the secret_key_base from the token alone. The payload, however, should be able to be extracted from the token if you can use Erlang serialization for the terms (iirc).

  2. Yes. It pretty much is the same like if you hijack a session cookie. The signing only prevents others tampering with the contained data.

2 Likes

What if the system keeps track of “tokens in use + process id” and rejects a token if it’s submitted by a different process?
1 token per process?

1 Like

What do you mean by “tokens in use” ?
Are you talking about channels or http sessions?
If this is about http sessions then you must consider that the process dies after the request is complete.
If this is about channels then you should probably check socket assigns.

2 Likes

It’s about something that the legitimate user and the server have, but the thief doesn’t.
So far, tokens sound like a really long plain text password stored in a browser’s window.
If it’s too hard for people to crack passwords, they will focus on stealing that token.

The user has a token, a unique process id, serving that connection, the server has the token salt, and secret key.
The thief has a token, a different process id, serving their connection.
The same process id is, maybe, just guessing, the only thing the thief doesn’t have.

If the same token is detected to be submitted by a different process, the system should deny the second process and kill it.
Yes, if a user closes their window, or the server restarts, the process id changes, and they must login again to get a new token, and that’s perfectly fine considering the alternative is absolute impersonation of the victim.

At the moment, if you login with a normal browser tab you get 1 token, if you login with the same user from an incognito tab you get a different token.

I’m not a security expert, but If there’s a “plain text” password stored in a browser’s window, I would focus my attacks to steal that, with JavaScript, or browser extensions or … …
A stolen token is missing:

  1. Process id.
  2. User password.
  3. Secret base key.
  4. Token salt?.

With a stolen token you bypass all these.

If users have to login again in order to prevent this attack, why not?
I have some doubt about scalability of such “token to process id” list, with multiple nodes, but maybe some db can help with that?
The point is, I’m willing to say yes to almost anything that will prevent such an attack.
It’s a plain text password, in a tag in the browser.
If people have to login after they close the window, or they have to wait 10 seconds to login while the system checks lists of all nodes, it’s still better than someone nullifying all security steps just by taking a page tag and sending it to their server.

Again, not a security expert or even a beginner :slight_smile:
6 am reply edits :slight_smile:

1 Like

As I said earlier, this type of attack has been around for so long, called session hijacking. There are methods to prevent cookies being stolen:

  1. Store your cookies with Secure flag, so it is only transmitted on HTTPS connections. HTTP connections will submit cookies in unencrypted form, and can be sniffed;

  2. Store your cookies with HTTPOnly flag, so that Javascript code cannot access it, therefore preventing getting the cookie through XSS attack. If you depend on JS to send API requests then this might not be feasible;

  3. Always sanitize user input from HTML tags, or only whitelist tags if necessary. This will prevent XSS script injection as an attacker couldn’t inject arbitrary JS code to be run on others’ browsers;

  4. Implement CSRF tokens. This will prevent email phishing attacks which could send requests through clicking a button or even no action at all (via image tags);

  5. Whatever you do, always use HTTPS everywhere.

There might be more, but I believe these are the basics.

Because UX. Some people only want to login once, that’s why “remember me” exists. If you believe your users can stand logging in for each use of the app (for example banking apps), then by all means make the token valid only for one session.

Off the top of my head, the process id solution that you propose wouldn’t work for the request-response cycle, since for each request your process id would be different, even if you’re using a single browser. A connection-handler process dies after it sends a response.

You can make it work for Channels, I believe, but that also means that if you ever close the window, or the server restarts for whatever reason, the token would also be invalid. The user would need to login again.

One thing you need to remember when tackling security issues is don’t ever reinvent the wheel :slight_smile:

3 Likes

Thanks for the feedback!

If it could work for Channels, that’s absolutely awesome :slight_smile: epic win :slight_smile:

I’m not trying to reinvent the wheel, but things are different with Elixir and Erlang, we have processes for everything, maybe a little reinvention wouldn’t hurt? Frankly, I’m surprised that it’s even feasible, it was just a guess.

As to UX, I would pay not to have users who can’t be inconvenienced once in a while with a login, to avoid someone impersonating them. I have strong suspicion that such users also have convenient passwords like “myname1235”, then they get hacked on day 3, then they complain and tarnish the reputation products.

1 Like

On second thought, I’m sorry but it might not be feasible, or at least that straightforward, after all.

Assuming you use a dedicated route for logging in (where you issue the tokens after username/password auth), I don’t think you can encode the process id of the channel connection on the token, because the connection (and therefore the process id) wouldn’t exist by then. And as I said, the req/res connection will have different process id than the channel.

And if you can’t encode it in the token, then that means you need to put it somewhere else, and then you have no way of knowing whether it has been tampered with… unless you only issue a one-time-use token, then on first channel connection you send said token through Phoenix channel; the server will then sign another token with the process id and send it back for next messages, but it seems complicated and error prone.

I might be wrong here, I’m just spitting what I have in my head right now :smile:

Most of the time, there’s a form of tradeoff between UX and security. Of course by purposefully “inconveniencing” users, such as with complicated password validations on signup, you can filter out irresponsible users, but they might be the majority of your target users (for example if you’re building something like Facebook which targets almost everyone), which means that your app wouldn’t get any traction. What good is an app without any user?

After all, not everyone shares the same paranoia of security and privacy (whether or not they should is another issue altogether :slight_smile:).

2 Likes

I was thinking something like:

  1. User provides email and password.
  2. Server checks against db and if valid signs a token and exposes it in the template, starts websocket connection.
  3. At the same time sever puts the ws “token+self()” in a database, database is shared with other nodes.
  4. Attacker steals token, logs in, tries to send stolen token.
  5. Server checks if token already issued in the database and compares the “self()” from the attacker with the “self()” in the database.

Or… or… maybe, the system keeps track if a token is assigned to a process that serves a channel, if one is assigned, it keeps track what that token is + self(), if the same process tries to submit a different token it kills the connection.

I really don’t know what the best approach is, and those are probably bad ideas, but it feels like it could be possible?

Facebook can feel free to target and cater to everyone, that’s why it’s such a wonderful place :slight_smile:
I want to make products that target specific group of people, and if no one wants to use them that’s perfectly fine!
For me it’s more important to throw my hat in the ring, I believe everyone should.

1 Like

You described the attackers attempt to connect, but what about the legit client case?
The self pid that you are referring to will be gone after the server responds.
The pid of the process that will handle both the legitimate and the attackers websocket request will be different than the one that handled the post request made to login. So they will both fail.

My earlier response-question was a bit confusing I guess. I actually meant to explain that http is stateless, there are no processes in use.

I was struggling with these concepts in the previous months and @bobbypriambodo was there to help me! :heart_eyes:

I am thinking of a React SPA connecting with a channel on the first load. All the requests are made
through the channel. When a user log’s in, the socket get’s assigned a token (or just the user’s id). Any subsequent request (websocket request) is tied with the token/user AND the actual pid of the channel.
This is the only way I can think of achieving your requirements.
React can be replaced with any js framework.
These are my 2 cents on this.
Caution Elixir beginner here! :slight_smile:

2 Likes

Well, that’s just what I’m saying :slight_smile:
I thought tokens are used only for the ws connections so I never meant it to be for the Controller part of the system.

Legitimate user case:

  1. Provides user and pass with /post
  2. Server validates, signs a token, exposes token on page.
  3. User connects with JS client using the token.
  4. Server validates and lets user use socket connection.

Attacker case:

  1. Provides user and pass.
  2. Server validates, signs a token, exposes token on page.
  3. Attacker connects with JS client using the stolen token instead of the server token.
  4. Attacker becomes the victim.

If the server can check that the attacker’s token is different from the assigned token for his ws process id, it could kill the connection there no? I think?
I’m also a beginner so hopefully this makes some sense :slight_smile:

1 Like

Honestly? When it comes to security best practices reinventing wheels is almost guaranteed to result in something worse. security is hard, stick with known best practices.

You’re trying to secure HTTP, this problem doesn’t care how you handle the http request on the server. Procsses or not doesn’t matter.

2 Likes

I’m trying to bind the thing that keeps the socket struct alive for the duration of a ws connection to the token the system issues for that connection :slight_smile:
Not sure where you see HTTP in there.

1 Like

Would this work?

1 Like