#102 Prevent Algo confusion attack

This commit is contained in:
arunmu-nx 2025-04-22 18:49:16 +05:30
parent 4a970bc302
commit b9528b32eb
7 changed files with 82 additions and 2 deletions

View file

@ -98,6 +98,8 @@ enum class VerificationErrc
InvalidSignature,
// Invalid value type used for known claims
TypeConversionError,
// Algorithm confusion attack detected
AlgoConfusionAttack,
};
/**

View file

@ -25,6 +25,31 @@ SOFTWARE.
namespace jwt {
verify_result_t is_secret_a_public_key(const jwt::string_view secret)
{
std::error_code ec{};
BIO_uptr bufkey{
BIO_new_mem_buf((void*)secret.data(), static_cast<int>(secret.length())),
bio_deletor
};
if (!bufkey) {
throw MemoryAllocationException("BIO_new_mem_buf failed");
}
EC_PKEY_uptr pkey{
PEM_read_bio_PUBKEY(bufkey.get(), nullptr, nullptr, nullptr),
ev_pkey_deletor
};
if (!pkey) {
ec = AlgorithmErrc::InvalidKeyErr;
return { false, ec };
}
return {true, ec};
}
template <typename Hasher>
verify_result_t HMACSign<Hasher>::verify(
const jwt::string_view key,

View file

@ -124,6 +124,8 @@ struct VerificationErrorCategory: std::error_category
return "invalid signature";
case VerificationErrc::TypeConversionError:
return "type conversion error";
case VerificationErrc::AlgoConfusionAttack:
return "algo confusion attack possibility";
};
return "unknown verification error";
}

View file

@ -195,10 +195,30 @@ inline verify_result_t jwt_signature::verify(const jwt_header& header,
const jwt::string_view hdr_pld_sign,
const jwt::string_view jwt_sign)
{
auto check_res = check_for_algo_confusion_attack(header);
if (check_res.first) {
return {false, VerificationErrc::AlgoConfusionAttack};
}
verify_func_t verify_fn = get_verify_algorithm_impl(header);
return verify_fn(key_, hdr_pld_sign, jwt_sign);
}
inline verify_result_t jwt_signature::check_for_algo_confusion_attack(
const jwt_header& hdr) const
{
switch (hdr.algo()) {
case algorithm::RS256:
case algorithm::RS384:
case algorithm::RS512:
return {false, std::error_code{}};
default:
// For all other cases make sure that the secret provided
// is not the public key.
return is_secret_a_public_key(key_);
}
}
inline sign_func_t
jwt_signature::get_sign_algorithm_impl(const jwt_header& hdr) const noexcept

View file

@ -829,6 +829,10 @@ private: // Private implementation
*/
verify_func_t get_verify_algorithm_impl(const jwt_header& hdr) const noexcept;
/*!
*/
verify_result_t check_for_algo_confusion_attack(const jwt_header& hdr) const;
private: // Data members;
/// The key for creating the JWS

View file

@ -186,3 +186,31 @@ TEST (DecodeTest, TypHeaderMiss)
EXPECT_FALSE (ec);
}
TEST (DecodeTest, AlgoConfusionAttack)
{
using namespace jwt::params;
using namespace jwt::params;
const char* enc_str =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0."
"jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk";
std::string pub_key = R"(-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh
uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+
iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM
ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g
6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe
K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t
BwIDAQAB
-----END PUBLIC KEY-----)";
std::error_code ec;
auto obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, verify(true), secret(pub_key));
ASSERT_TRUE (ec);
EXPECT_EQ (ec.value(), static_cast<int>(jwt::VerificationErrc::AlgoConfusionAttack));
}

View file

@ -142,4 +142,3 @@ TEST (RSAAlgo, NoSpecificAlgo)
EXPECT_THROW (jwt::decode(enc_str, algorithms({"none", "HS384", "RS384"}), verify(true), secret(key)),
jwt::InvalidAlgorithmError);
}