fix: Improve validation of Bot Tokens (#1206)
* improve bot token validation by trying to decode user id from token Try to decode the user id from the supplied bot token as a way of validating the token. If this should fail, indicate that the token is invalid. * Update the tokenutils tests to pass the new validation checks * Add test case for CheckBotTokenValidity method * lint: clean up whitespace * Add check for null or whitespace string, lint whitespace * fix userid conversion * Add hint to user to check that token is not an oauth client secret * Catch exception that can be thrown by GetString * Refactor token conversion logic into it's own testable method
This commit is contained in:
committed by
Christopher F
parent
91e0f03bfd
commit
f4b1a5f25b
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
@@ -16,6 +17,65 @@ namespace Discord
|
||||
/// </remarks>
|
||||
internal const int MinBotTokenLength = 58;
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a base 64 encoded string into a ulong value.
|
||||
/// </summary>
|
||||
/// <param name="encoded"> A base 64 encoded string containing a User Id.</param>
|
||||
/// <returns> A ulong containing the decoded value of the string, or null if the value was invalid. </returns>
|
||||
internal static ulong? DecodeBase64UserId(string encoded)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(encoded))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// decode the base64 string
|
||||
var bytes = Convert.FromBase64String(encoded);
|
||||
var idStr = Encoding.UTF8.GetString(bytes);
|
||||
// try to parse a ulong from the resulting string
|
||||
if (ulong.TryParse(idStr, out var id))
|
||||
return id;
|
||||
}
|
||||
catch (DecoderFallbackException)
|
||||
{
|
||||
// ignore exception, can be thrown by GetString
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// ignore exception, can be thrown if base64 string is invalid
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// ignore exception, can be thrown by BitConverter
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the validity of a bot token by attempting to decode a ulong userid
|
||||
/// from the bot token.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// The bot token to validate.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the bot token was valid, false if it was not.
|
||||
/// </returns>
|
||||
internal static bool CheckBotTokenValidity(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return false;
|
||||
|
||||
// split each component of the JWT
|
||||
var segments = message.Split('.');
|
||||
|
||||
// ensure that there are three parts
|
||||
if (segments.Length != 3)
|
||||
return false;
|
||||
// return true if the user id could be determined
|
||||
return DecodeBase64UserId(segments[0]).HasValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the validity of the supplied token of a specific type.
|
||||
/// </summary>
|
||||
@@ -42,13 +102,17 @@ namespace Discord
|
||||
// this value was determined by referencing examples in the discord documentation, and by comparing with
|
||||
// pre-existing tokens
|
||||
if (token.Length < MinBotTokenLength)
|
||||
throw new ArgumentException(message: $"A Bot token must be at least {MinBotTokenLength} characters in length.", paramName: nameof(token));
|
||||
throw new ArgumentException(message: $"A Bot token must be at least {MinBotTokenLength} characters in length. " +
|
||||
"Ensure that the Bot Token provided is not an OAuth client secret.", paramName: nameof(token));
|
||||
// check the validity of the bot token by decoding the ulong userid from the jwt
|
||||
if (!CheckBotTokenValidity(token))
|
||||
throw new ArgumentException(message: "The Bot token was invalid. " +
|
||||
"Ensure that the Bot Token provided is not an OAuth client secret.", paramName: nameof(token));
|
||||
break;
|
||||
default:
|
||||
// All unrecognized TokenTypes (including User tokens) are considered to be invalid.
|
||||
throw new ArgumentException(message: "Unrecognized TokenType.", paramName: nameof(token));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user