# Bug Blog: “LastPosted” reveals non-public data about users, exploited to gain advantages

**URL:** <https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280>\
**Category:** Feature\
**Created:** [November 18, 2018, 8:44pm UTC](https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280 "2018-11-18T20:44:42Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Wintermute](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/wintermute/32/127023_2.png) [@Wintermute](https://meta.discourse.org/u/Wintermute)\
**Post date:** [November 18, 2018, 8:44pm UTC](https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280/1 "2018-11-18T20:44:43Z")

</div>

### In Brief:

> The “Last Posted” field on the user profile updates when a user sends a private message or posts in a private subforum. By comparing this field to the timestamp of the latest post in a user’s activity feed, the public gains access to private information which they should not have access to. We can make this comparison programmatically with API calls and automation to collect data about when and how often people are PMing. This is very bad.

### Proposed Solution

LastPosted should not be updated when a user sends a private message.

### The Exploit

1. Open a user’s [public profile](https://meta.discourse.org/u/Wintermute/summary). Make note of the field **Last Posted.**
2. Open the same user’s [activity feed](https://meta.discourse.org/u/Wintermute/activity). Note the timestamp of the latest post.
3. If the two are different (LastPosted within a couple minutes, activity feed shows latest post hours ago), you have gained access to information (the existence of a private message) which you should not have access to.

### Developing the Exploit: API Calls and Automation

On the profile, this information is rounded and it is sometimes hard to draw precise conclusions. However, by using the public Discourse API, we can grab the real timestamps and compare them for exact information about when and how often a user is PMing.

We will use two different API routes for this.

1. At [https://meta.discourse.org/u/Wintermute.json](https://meta.discourse.org/u/Wintermute.json), find `user.last_posted_at`.
2. At [https://meta.discourse.org/user\_actions.json?username=Wintermute](https://meta.discourse.org/user_actions.json?username=Wintermute), find `user_actions.created_at` of the most recent post (disclaimer: some additional work here to filter out likes, replies to the user etc. We will omit it for brevity).

Provided we have an API key, we can make these calls programmatically and automate them.

I have a working setup which runs a CloudWatch rule every 2 minutes to execute a Lambda function. It makes these calls and then writes to a DynamoDB table if it finds the timestamps are different. I can then scan the dynamo table to produce data on when and how often people are PMing.

I can share this code if there’s interest.

### Why? What's the Impact?

I belong to a community that uses a Discourse website to play [_Werewolf_](https://en.wikipedia.org/wiki/Mafia_(party_game)), a common online forum and party game. If you’ve ever played [Town Of Salem](http://www.blankmediagames.com/), [Trouble in Terrorist Town](https://en.wikipedia.org/wiki/Trouble_in_Terrorist_Town), [MafiaUniverse](https://www.mafiauniverse.com/), [EpicMafia](https://epicmafia.com), or played any of the various [live-action spinoffs](https://www.playwerewolf.co/) at a Summer Camp with your friends, the rules should be familiar.

1. There’s a group of people who are “the Town.”
2. A second group hidden within the town are “Wolves.”
3. Game rotates through “Day” and “Night” phases.
4. Town tries to find the wolves during the day and kick them out.
5. Wolves eat one town per night until one group is eliminated.
6. The wolves typically have a private chat to plan their moves and make decisions at night.
7. There can optionally be a bunch of other characters and roles which may also periodically PM the host for special actions or information about the game.

Until recently we were using the Discourse private messaging feature to facilitate these discussions. Since the discovery of this bug we’ve moved those chats to off-site third-party chat clients, but we’d like to move them back for future games. We’re hoping you all would agree that this is not desired behavior for the “LastPosted” stat and that this bug might be a quick & easy fix 🙂 Let me know if you have any questions.

Thanks for reading and have a nice Thanksgiving.

---

<div class="post-metadata">

**Author:** ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)\
**Post date:** [November 18, 2018, 10:33pm UTC](https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280/2 "2018-11-18T22:33:29Z")

</div>

> [@Wintermute](#):
>
> I belong to a community that uses a Discourse website to play [_Werewolf_](https://en.wikipedia.org/wiki/Mafia_(party_game)), a common online forum and party game

That’s a cool use for Discourse that I’ve never considered!

> [@Wintermute](#):
>
> The “Last Posted” field on the user profile updates when a user sends a private message or posts in a private subforum

A lot of communities use personal messages and private subforums a lot, and so updating `last_posted_at` would be expected behaviour, and wouldn’t really be considered an exploit.

In your very specific case, you could make use of the new “Hide my public profile and presence features” setting. If users enable that, then their “last\_posted\_at” date will be hidden from other users. You could disable the setting again as soon as the werewolves have been revealed 🐺

You’ll find that setting under the “interface” section of your user preferences.

---

<div class="post-metadata">

**Author:** ![Wintermute](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/wintermute/32/127023_2.png) [@Wintermute](https://meta.discourse.org/u/Wintermute)\
**Post date:** [November 19, 2018, 12:10am UTC](https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280/3 "2018-11-19T00:10:27Z")

</div>

Good suggestion which we had actually considered; Unfortunately, doing so disables a couple other site features, including the ability to “ISO” a user (look at their posts in isolation, which discourse enables with the nifty little button pictured below).

 ![posts_in_topic](https://global.discourse-cdn.com/meta/original/3X/e/c/ec4ed2ca408ab48dd86cdd82c3fc1153eaada5f2.png)

This is an important tool in the investigative aspect of our game, so we cannot disable it for the time being.

Additionally, we like the profile features discourse provides and wouldn’t want our users to have to hide them 😃

Should I take your post to mean that you guys prefer to update `last_posted_at` after a private message? Is there perhaps a site change we could make on our end that would disable that field? I know we can write CSS that hides that item on the user’s profile but I’d imagine the API data would still be an issue.

---

<div class="post-metadata">

**Author:** ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)\
**Post date:** [November 19, 2018, 2:03am UTC](https://meta.discourse.org/t/bug-blog-lastposted-reveals-non-public-data-about-users-exploited-to-gain-advantages/102280/4 "2018-11-19T02:03:40Z")

</div>

> [@Wintermute](#):
>
> Should I take your post to mean that you guys prefer to update `last_posted_at` after a private message?

Yes we prefer the default to be “last time you posted anything to anyone in the forum”, we don’t think it should imply “last time you posted something in a public category visible to all people on the forum”

I worry changing this will cause wide spread confusion, I do not really want to touch this in core. Private categories mean that going down this rats nest means we will need different visible last\_posted\_at per user, this is just way too complex for almost no gain.

To amend this you are going to need a plugin that strategically disables:

> <https://github.com/discourse/discourse/blob/c10bc4012a1205e82b13db5501df68dee76d1bcc/lib/post_creator.rb#L508-L508>

I guess a plugin with this source will do the trick for you

```plaintext
require_dependency 'post_creator'
module MyHack
   def update_user_counts
      super
      @user.update_attributes(last_posted_at: Time.new('2000-01-01'))
   end
end

class PostCreator
   prepend MyHack
end

```
