cpp-jwt accepts malformed JWT :
- with trailing padding characters (
==);
- using standard base64 characters;
- with invalid final characters (containing trailing 1 bits);
Example:
{"crv": "P-256", "x": "b9Sw0PZw8rGPrpjRb0sjK1aZjNurjPeeKGifWIMh2m0", "y": "NMSXejh09vzsrfkwM5SW0znQo72KfJuwXgBBo3ktQ9c", "kty": "EC"}
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb9Sw0PZw8rGPrpjRb0sjK1aZjNur
jPeeKGifWIMh2m00xJd6OHT2/Oyt+TAzlJbTOdCjvYp8m7BeAEGjeS1D1w==
-----END PUBLIC KEY-----
Token = eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYW1lIjoiSm9obn5-fn5-fn5-fn5-fiJ9.DuhRJ5gLzWzlRkvi1GYLCxbZMLhnh1kTEACU4Xpcy6jv9O_CNSTUHeVX8avDslUU1z5faLdgusbYaqsSgkGRVw
{"alg":"ES256","typ":"JWT"}
{"name":"John~~~~~~~~~~~~"}
OK
Token = eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYW1lIjoiSm9obn5-fn5-fn5-fn5-fiJ9.DuhRJ5gLzWzlRkvi1GYLCxbZMLhnh1kTEACU4Xpcy6gQCxA8ytsr4xqoDlQ8Tarq5aibRO-2474bTx-weiGT-g
{"alg":"ES256","typ":"JWT"}
{"name":"John~~~~~~~~~~~~"}
OK
Token = eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYW1lIjoiSm9obn5-fn5-fn5-fn5-fiJ9.DuhRJ5gLzWzlRkvi1GYLCxbZMLhnh1kTEACU4Xpcy6jv9O_CNSTUHeVX8avDslUU1z5faLdgusbYaqsSgkGRVw==
{"alg":"ES256","typ":"JWT"}
{"name":"John~~~~~~~~~~~~"}
OK
Token = eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYW1lIjoiSm9obn5-fn5-fn5-fn5-fiJ9.DuhRJ5gLzWzlRkvi1GYLCxbZMLhnh1kTEACU4Xpcy6jv9O_CNSTUHeVX8avDslUU1z5faLdgusbYaqsSgkGRVx
{"alg":"ES256","typ":"JWT"}
{"name":"John~~~~~~~~~~~~"}
OK
Token = eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYW1lIjoiSm9obn5-fn5-fn5-fn5-fiJ9.DuhRJ5gLzWzlRkvi1GYLCxbZMLhnh1kTEACU4Xpcy6jv9O/CNSTUHeVX8avDslUU1z5faLdgusbYaqsSgkGRVw
{"alg":"ES256","typ":"JWT"}
{"name":"John~~~~~~~~~~~~"}
OK
roduced with:
#include <string>
#include <iostream>
#include <fstream>
#include "jwt/jwt.hpp"
using namespace jwt::params;
int main(int argc, char **argv) {
if (argc != 3)
return 1;
std::string key_filename = argv[1];
std::string token = argv[2];
std::ifstream file(key_filename.c_str());
std::stringstream buffer;
buffer << file.rdbuf();
std::string public_key = buffer.str();
// std::cout << public_key << "\n";
//Decode
auto decoded_token = jwt::decode(token, algorithms({"ES256"}), secret(public_key));
std::cout << decoded_token.header() << std::endl;
std::cout << decoded_token.payload() << std::endl;
std::cout << "OK\n";
return 0;
}
This introduces some malleability of the JWT. An attacker could derive a new (equivalent, malformed) JWT from a valid one. This might be an issue if an application builds a deny list (revocation, anti-replay) based on the raw token or its hash.
Building a deny list this way is probably not a good idea and quite brittle anyway and for this reason. In particular, there is an inherent malleability when using ECDSA signatures caused by the malleability of ECDSA signatures. This implementation error which is quite subtle and people are bound to make this error. This especially true when the JWT is actually considered as an opaque token by the consumer. On the other hand there is no real argument for accepting invalid JWTs. So, I think it makes sense to reject malformed JWTs both for correctness and for hardening (error-proofing).
cpp-jwt accepts malformed JWT :
==);Example:
roduced with:
This introduces some malleability of the JWT. An attacker could derive a new (equivalent, malformed) JWT from a valid one. This might be an issue if an application builds a deny list (revocation, anti-replay) based on the raw token or its hash.
Building a deny list this way is probably not a good idea and quite brittle anyway and for this reason. In particular, there is an inherent malleability when using ECDSA signatures caused by the malleability of ECDSA signatures. This implementation error which is quite subtle and people are bound to make this error. This especially true when the JWT is actually considered as an opaque token by the consumer. On the other hand there is no real argument for accepting invalid JWTs. So, I think it makes sense to reject malformed JWTs both for correctness and for hardening (error-proofing).