Secure token generation and validation
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'dart:convert';
import 'dart:io';
import 'package:jaguar_jwt/jaguar_jwt.dart';
void main() {
var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()).addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080);
}
shelf.Response _echoRequest(shelf.Request request) {
var token = request.headers['authorization'];
if (token == null) {
return new shelf.Response(401);
}
var decodedToken = B64urlEncRfc7515.decodeUtf8(token);
var jsonToken = jsonDecode(decodedToken);
var userId = jsonToken['user_id'];
if (userId == null) {
return new shelf.Response(401);
}
return new shelf.Response.ok('Hello, $userId!');
}
The above code is a simple server application using the Shelf framework in Dart. It listens for requests on localhost at port 8080.
In the
_echoRequest
function, it tries to retrieve the 'authorization' header from the request which is supposed to contain a JWT token. If the token is not present, it responds with a 401 status code.
The vulnerability lies in the way it handles the token. It directly decodes the token using
B64urlEncRfc7515.decodeUtf8(token)
. This means that it does not validate the signature of the token. Hence, an attacker can modify the token, change the algorithm in the header to 'none', and the server would still accept it as a valid token.
Moreover, it retrieves the 'user_id' from the decoded token and if it is present, it sends an OK response with a message 'Hello, user_id!'. This means an attacker can impersonate any user by simply creating a token with the user's id.
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'dart:convert';
import 'dart:io';
import 'package:jaguar_jwt/jaguar_jwt.dart';
const String secretKey = 'your-secure-secret-key'; // Store this securely
void main() {
var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()).addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080);
}
shelf.Response _echoRequest(shelf.Request request) {
var token = request.headers['authorization'];
if (token == null) {
return new shelf.Response(401);
}
try {
var decClaimSet = verifyJwtHS256Signature(token, secretKey);
var userId = decClaimSet.uniqueId;
if (userId == null) {
return new shelf.Response(401);
}
return new shelf.Response.ok('Hello, $userId!');
} catch(e) {
return new shelf.Response(401);
}
}
The updated code introduces a secret key to sign the JWT token. This key is stored securely and not exposed. The
verifyJwtHS256Signature
function from the
jaguar_jwt
library is used to validate the JWT token. This function verifies the signature of the token using the secret key.
The algorithm used to sign the token is HS256, which is a secure algorithm. The 'none' algorithm is not used, preventing the vulnerability where the token could be unsigned.
The claims of the JWT token are checked by the
verifyJwtHS256Signature
function. In this case, it makes sure that the 'user_id' claim is present and valid. The 'user_id' claim is accessed through the
uniqueId
property of the decoded claim set.
If the token is invalid or tampered with, an exception is thrown and caught in the
catch
block. In this case, a 401 Unauthorized response is returned, appropriately handling the error case.