Always Stay Online and Active on Slack with WebSocket Framejacking

Slack: Always Stay Active

Goal: Find a simple way to always stay active on Slack, even if the browser is minimized or out of focus, without giving away your password to an online service or needing an API key.

Even though Slack is on a dedicated monitor beside you, if you’re not focused on it, you’ll appear “away” and “not at your computer.” Do you subconsciously think team members are sleeping when they appear away at 10 am? Let’s always be active until we close the Slack tab or manually set ourselves as away. This is fair: Slack is open somewhere, so we are active.

Is Eric really away?Eric is usually activeAm I really away or offline when I’m in a different window?

By changing just four characters in Slack, we can always stay active without going inactive, even after hours with a minimized browser.


Are There Existing Solutions?

Let’s see what solutions exist or we can think of before we analyze an extremely simple and effective solution.

Online service that pretends to be you

One solution requires giving away your user name and password to an online service that pretends to be you just enough to convince Slack you are active. Who is to know how safe this is? This doesn’t work for Enterprise Slack that uses SSO authentication.

Slack on an always-open smartphone

Another solution is to open Slack in the foreground on your phone and never let your phone sleep. You’ll need a phone and charger.

Desktop Slack app with a mouse jiggler

Yet another solution is to open the Slack desktop app and use a mouse jiggler to prove to Slack you are active and awake. You’d need hardware or some way to nudge your mouse now and then, plus the desktop app.

Slack API to perform user actions

Others have even written Python, Java, NodeJS, etc. code to generate faux user activity via the Slack API with a personal API key. This seems like a neat idea, but the API changes now and then, and it is unclear how to change the user status. We don’t have Enterprise Slack access to the API.

Self-refreshing frame

You might consider writing a self-refreshing iframe that just loads Slack over and over again. Slack sets the X-Frame-Options header to sameorigin, so Slack will not even load in an iframe. Good thinking, though.

Selenium to click around in Slack

Another interesting solution is to fire up a web-testing framework like Selenium to load a headless browser that “clicks” around Slack now and again. Some steps are needed to enter your credentials (and hope the UI doesn’t change and a Captcha doesn’t appear), plus the programming knowledge required is high. This likely doesn’t work with Enterprise Slack and SSO authentication.


Let’s Arrive at a Neat Solution Together

As a rewarding read, let’s arrive at this solution together by asking ourselves some questions.

Question: How does Slack know we are away? Does the browser signal inactivity or does the Slack server keep track?

Let’s open Chrome DevTools and watch the console, XHR network requests, and WebSocket frames.

Sample WebSocket frames
WebSocket messages
Console shows Slack is aware of window blur and focus
Console messages

We see that when the browser loses focus and then regains focus, some console messages appear. However, there are no corresponding WebSocket messages.

Question: Can we remove browser event listeners related to losing focus?

Let’s try that. We can easily remove all blur event listeners using DevTools like so.

Window event listeners
Window event listeners

When we focus on another window or minimize the Slack window, no blur events fire. So far, so good. Now we wait for thirty minutes to see if we go inactive.

Some time goes by…

Darn. We went inactive. Let’s see what network requests took place.

No unusual XHR requests

There were no unusual XHR requests, but the WebSocket frames are interesting.

WebSocket messages when user goes away

Notice these are response frames. The server told us we went inactive—no browser event triggered it.

Let’s check messages when we return to an inactive Slack window.

Messages when the user becomes active again

The server told us we became active as soon as we entered (focused) the browser tab.

Question: What user actions signal to the server we are active?

Let’s click around Slack channels and type messages while watching the WebSocket.

Activation WebSocket messages

We see a typing message when we start typing, and a tickle message when we blur, refocus, then click. These are the only two benign events that signal activity.

Question: Can we replay these activity messages?

No. Each sent frame has a sequential id, so we can’t replay them via setInterval().

Question: Could we predict the next id to spoof an activity message?

We might track the current id and send one with id + 1, but that would likely invalidate the next genuine frame or clash with the asynchronous Slack Service Worker.

It’s unclear if sequence IDs persist across reconnections or reset per socket. Slack could also change the pattern at any time—say, even-numbered IDs instead of odd. Let’s assume IDs are unpredictable and move on.

Question: Can we framejack and modify ordinary messages?

Reviewing background traffic shows candidates for framejacking—hijacking single-frame WebSocket messages, as most Slack messages fit in one frame.

Common WebSocket messages
Common WebSocket messages

Ping! Every ten seconds or so a ping is sent to the server and a pong is received. A ping message has the same format as the tickle message.

Ping and tickle messages
Ping and tickle messages

Let’s try spoofing a tickle message by replacing the four characters “ping” with “tickle” in framejacked ping messages every few minutes (more on that soon). This approach could be simple and elegant.

Question: Can we proxy the WebSocket constructor and handlers to intercept messages?

Yes and no. Granted, it’s trivial to MITM WebSockets—intercepting them and modifying messages. However, WebSockets close, reopen, or get killed over time, so we must invest effort to always framejack the correct socket.

Many WebSockets come and go
Many WebSockets come and go
Question: Should we run setInterval() for framejacking all sockets?

No. Over the day, many sockets are created—you’d end up with stacked setInterval() timers that misbehave if variables go out of scope.

A better approach is to skip timers entirely and use timestamps: each incoming message triggers an elapsed-time check to send faux activity.

Question: Is there any downside to skipping pings?

Yes. Slack uses a heartbeat protocol: it sends a ping every ten seconds and expects a matching pong. If no pong arrives within ninety seconds, the client closes the socket.

Pong timeout check in Slack
Pong timeout check in Slack
Question: How can we safely framejack a ping then?

We shouldn’t modify Slack’s JavaScript or hack private variables. Could we simply prevent the socket from closing? I tried editing Slack’s cached JS file.

Experiment with large ping timeout
Experiment with large ping timeout

After framejacking one ping, no more pings were sent—and Slack stopped working soon after.

Instead, let’s intercept the ping, convert it to a tickle, and inject a well-crafted pong into the WebSocket layer so the client-side JavaScript remains satisfied.

Fake some pong messages from the console
Fake some pong messages from the console

It’s trivial to spoof a pong from the DevTools console. But, we shouldn’t settle for sending a simple JSON string—we want to forge a trusted EventMessage with all the attributes of a real message received on the wire. That makes our solution future-proof.

Trusted pong EventMessage
Trusted pong EventMessage

You can’t simply forge a trusted EventMessage object (a real WebSocket message) and modify it. You must engineer a way to tamper with a read-only event—making its data attribute writeable so it returns a valid pong.

Trusted EventMessage forgery
Trusted EventMessage forgery

Admit it—forging a trusted message is exciting. We saved a reference to a real pong (which is hard to clone) and made its data attribute writeable.

Question: Does this work with a minimized browser?

Yes. Slack uses a Service Worker running in its own background thread. Service Workers handle alerts, chart updates, data sync, and more.

Slack Service Worker
Slack Service Worker

Below is a console log showing the script working while the browser is minimized and hidden.

Console logs while browser is minimized
Console logs while browser is minimized

The Slack: Always Stay Active Script

We only need Chrome (or Brave) and the popular browser extension Tampermonkey1 to inject JavaScript on Slack pages.

What began as a proof of concept now includes visual feedback and integrity checks to warn us if Slack changes something critical. You can stop the script remotely on all your browsers on all your machines, and you can still set yourself away manually. The script is organized for easy review and verification. Enjoy.

Quick Start

A thin orange line appears while Slack boots. It turns green when active.

Slack: Always Stay Active is running
Slack: Always Stay Active is running
Manually setting yourself away works
Manually setting yourself away works

If it turns red, internal sanity checks failed signalling Slack has changed something major. This design is future-proof. If Slack renames tickle to, say, caress or nudge, you can update the script accordingly.

Advanced Features

Send yourself these messages to control the script across all of your open Slack windows:

  1. sigkill! – close all your open Slack windows
  2. sighup! – refresh all your open Slack windows

Console Commands

Test the script from the DevTools console:

  1. slackasa.ticklenow() – framejack the next pong to signal activity now
  2. slackasa.listsockets() – list all open sockets and their timestamps
  3. slackasa.reload() – reload the page

Installation Steps

Important! (Update Sept 25, 2022) Use a web browser that uses Manifest V2 like FireFox or Chrome before 2023. Chrome, Brave, Edge, and all Chromium-based browsers (most browsers) are moving to declarative network requests which block user scripts and cripple adblockers (Google makes money from ads).

Step 1. Add Tampermonkey to Chrome.

Get Tampermonkey for Chrome or Brave
Get Tampermonkey for Chrome or Brave

Step 2. Create a new empty userscript without saving.

Create a new user script

Step 3. Copy and paste the script at the bottom of the page. Save. Close Tampermonkey.

Paste the script to Tampermonkey
Paste the script to Tampermonkey

Step 4. Visit Slack. Find the Tampermonkey icon, and enable Tempermonkey and the ‘Slack: Always Stay Active’ userscript. Reload Slack.

Activate the Slack: Always Stay Active script
Activate the Slack: Always Stay Active script
Slack: Always Stay Active is running
Slack: Always Stay Active is running

Discussion

This began as a mental exercise to keep Slack active. I use web Slack constantly—open on multiple computers and my phone—so I trigger activity at least every thirty minutes.

Yet sometimes I’m engrossed in my IDE for long stretches. Even with Slack on a dedicated monitor beside me, I appear “away” and “not at my computer”—far from the truth. Does this happen to you, too?

Success: We’ve found a safe, viable, non-intrusive solution to always stay active in Slack and Enterprise Slack unless we close all Slack browser windows—a tiny adjustment to one word in a well-timed WebSocket frame.

Full Script

This was written and developed in vanilla JavaScript in the Tampermonkey script editor in my spare time over the weekend. Encapsulation isn’t perfect as it’s not transpiled from TypeScript. The goal is to make it easy to read and understand. Why not publish this script? To require users to examine the code and verify it is safe as a habit. If you share this script, kindly attribute it. Thank you.

Copy script to clipboard

Notes:

  1. There are over 10 million users of Tampermonkey.