I have gone throw few posts about using JWT in ASP.Net MVC, which guides how to issue and consume Signed JSON Web Tokens.
Can anyone please guide how to issue and consume encrypted JWT following the JSON Web Encryption (JWE) specifications in case we need to transmit some sensitive data in the JWT payload.
Understanding JWT
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JavaScript Object Notation (JSON) object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or MACed and/or encrypted.
What JWT?
https://jwt.io/introduction/
Json Web Token Standards
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-25
Anatomy of JWT
https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
Creating JSON Web Token in JavaScript
https://www.jonathan-petitcolas.com/2014/11/27/creating-json-web-token-in-javascript.html
Now, We understand JWT call and how we can serve it from server side.
Here i have HTML page in which I have button and also set some custom parameters.
<script src="//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
<script type="text/javascript">
$(function () {
$("#btnJWTApi").click(function () {
// Defining our token parts
// You can use one of these, as alg
// HS256, HS386, HS512
// Always keep type as JWT
var header = {
"alg": "HS256",
"typ": "JWT"
};
var tNow = KJUR.jws.IntDate.getNow();
var tEnd = KJUR.jws.IntDate.getNow() + 60 * 5;
// dynamically pass these data using a function
var data = {
"appId": "yourAppId",
"iat": tNow,
// iat (issued at time) should be set to time when request has been generated
"exp": tEnd,
// exp (expiration) should not be more than 5 minutes from now, this is to prevent Replay Attacks
"method": "TestMethod",
"Q": "test",
"SecretKey": "MySecretKey"
};
// Secret key is used for calculating and verifying the signature.
// The secret signing key MUST only be accessible by the issuer and the User,
// it should not be accessible outside of these two parties.
// Use the Secret you set during user registration from the Plugin
var secret = btoa('MySecret ');
function base64url(source) {
// Encode in classical base64
encodedSource = CryptoJS.enc.Base64.stringify(source);
// Remove padding equal characters
encodedSource = encodedSource.replace(/=+$/, '');
// Replace characters according to base64url specifications
encodedSource = encodedSource.replace(/\+/g, '-');
encodedSource = encodedSource.replace(/\//g, '_');
return encodedSource;
}
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header));
var encodedHeader = base64url(stringifiedHeader);
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data));
var encodedData = base64url(stringifiedData);
var signature = encodedHeader + "." + encodedData;
signature = CryptoJS.HmacSHA256(signature, secret);
signature = base64url(signature);
var targetEle = $("#data");
$.ajax(
{
type: "POST",
url: "http://localhost:12345/api/v1/MyController/SecureMethod",
data: '{"token":"' + encodedHeader + "." + encodedData + "." + signature + '"}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
targetEle.html("<pre>" + JSON.stringify(data, null, '\t') + "</pre>");
},
error: function () {
alert('error');
}
});
});
});
</script>
This call will generate encrypted token which include appId,secret and our payload data with method name.
(Here create one common method, which call first and then according to passing data in a token further method will be call)
This will call your method SecureMethod instead of direct TestMethod.
And decrypt token.
public string SecureMethod(dynamic tokenObject)
{
//save at a time of user registration.
string applicationID = appSecret get from database;
string secretKey = appSecret get from database;
}
var bytes = Encoding.UTF8.GetBytes(secretKey);
var secret = Convert.ToBase64String(bytes);
var jwtDecryption = JsonWebToken.DecodeToObject(token, secret, true, true);
var jsonObj = JObject.FromObject(jwtDecryption);
string appId = jsonObj["appId"].Value<string>();
if (appId.Equals(applicationID)
{
object restService = new MyController();
var method = restService.GetType().GetMethod(jsonObj["method"].ToString(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (method != null)
{
var parameters = method.GetParameters().Select(p => Convert.ChangeType(jsonObj[p.Name].ToString(), p.ParameterType)).ToArray();
object response = method.Invoke(restService, parameters); //your actual method should
return new JavaScriptSerializer().Serialize(response);
}
method.Invoke(restService, parameters); will have method name and parameter so it'll called your method and pass parameters.
public IHttpActionResult TestMethod([FromBody]Response model)
{
// you will get parameters in a model
return Ok();
}
Any suggestion welcome!
Related
I try to request to yobit API wthin R.
To get access to some of the methods you need to complete authentication:
Each Trade API request should pass authentication.
Authentication is fulfilled by sending the following HTTP-titles:
Key - API-key, example: FAF816D16FFDFBD1D46EEF5D5B10D8A2
Sign - digital signature, POST-parameters (?param0=val0 & ...& nonce=1) signed by secret key through HMAC-SHA512
Parameter nonce (1 minimum to 2147483646 maximum) in succeeding request should exceed that in the previous one. To null nonce it is necessary to generate new key.
My code :
nonce=1
API_KEY = "0B02AD5AF57854184D68D3D1D4D980F9"
API_SECRET = "89b20f882220b5dc6feeb33253c25ba3"
Body=paste('method=getInfo&nonce=',nonce, sep="")
sign = hmac(API_SECRET, Body, algo="sha512")
title=add_headers('Content-type'='application/x-www-form-urlencoded', Key = API_KEY, Sign = sign)
rep=POST('https://yobit.net/tapi', body=Body, headers=title, encode='form')
nonce=nonce+1
Response from server:
"{\"success\":0,\"error\":\"invalid key, sign, method or nonce\"}"
Thanks for help!
this i have done in node js And its working.
const crypto = require("crypto");
var apikey = 'apikey';
var secret = 'secret';
var signature = "method=getInfo&nonce="+ nonce;
console.log(signature);
var hmacsignature = crypto.createHmac('sha512', secret).update( signature ).digest('hex');
var headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Key': apikey,
'Sign': hmacsignature
};
var options = {
url: 'https://yobit.net/tapi',
method: 'POST',
body: signature,
headers: headers
}
console.log(options);
request1(options, function (error, response, body) {
res.send(body);
});
I am getting CSRF issue when scanned with HP fortify .
jQuery.fn.downloadContentUsingServerEcho = function (fileName, contentType, contentEncoding, content) {
//// test
//$.ajax({
// type: 'POST',
// url: 'download/' + fileName,
// contentType: 'application/json; charset=utf-8',
// data: JSON.stringify({ contentType: contentType, contentEncoding: contentEncoding, content: content })
//});
var form = document.createElement('form');
form.id = 'downloadForm';
form.method = 'post';
form.target = 'downloadTarget';
form.action = 'download/' + fileName;
var data = {
contentType: contentType,
contentEncoding: contentEncoding,
content: content
};
for (var propName in data) {
if (!data.hasOwnProperty(propName)) { continue; }
var propValue = data[propName];
var input = document.createElement('textarea');
input.name = propName;
input.value = propValue;
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
};
I am getting in this form.method = 'post';
Appreciate your help to fix this issue.
Thanks,
bk
If you are asking how do you prevent against CSRF attacks, OWASP has some good information on it.
OWASP - Cross Site Request Forgery (CSRF)
Note: It is also hard to read what you are trying to do in your code, you should format it so we can see what you are trying to accomplish.
I don't quite understand what your issue is. If HP fortify is saying that you aren't using a CSRF token to secure your AJAX call then you need to generate a token and pass it up to the server.
For implementation in ASP.NET Microsoft gives a good tutorial on this: https://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages
To automatically add the CSRF token to all ajax post calls, you can simply add a prefilter. include antiforgerytoken in ajax post ASP.NET MVC
Note: Some frameworks such as Telerik require the CSRF token to be in options.data instead of just the header.
I have built a RESTful web service using ASP.NET HttpHandler, when running the web service project im redirected to the default page from which I can choose to download the DOJO code for my Client app.
here is a code snippet from the downloaded file:
function PickrWebService(){ self = this; }
PickrWebService.prototype = {
self: null,
urlString: "http://AYMAN/Handler.ashx",
CreateUser:function(Email,Username,Password,FirstName,Surname,Birth,Gender,Mobile,Picture,Address,successFunction,failFunction,token) {
var data = { 'interface': 'PickrWebService', 'method': 'CreateUser', 'parameters': {'Email':Email,'Username':Username,'Password':Password,'FirstName':FirstName,'Surname':Surname,'Birth':Birth,'Gender':Gender,'Mobile':Mobile,'Picture':Picture,'Address':Address}, 'token': token };
var jsonData = dojo.toJson(data);
var xhrArgs = {
url: self.urlString,
handleAs: 'json',
postData: jsonData,
load: successFunction,
error: failFunction };
var deferred = dojo.xhrPost(xhrArgs);
},
CheckUserExistence:function(Email,successFunction,failFunction,token) {
var data = { 'interface': 'PickrWebService', 'method': 'CheckUserExistence', 'parameters': {'Email':Email}, 'token': token };
var jsonData = dojo.toJson(data);
var xhrArgs = {
url: self.urlString,
handleAs: 'json',
postData: jsonData,
load: successFunction,
error: failFunction };
var deferred = dojo.xhrPost(xhrArgs);
}
}
I need help on how to use this code in my client app, and what does the parameter 'token' refer to?
The code is a javascript object for you service which you can call the webservice, by invoking the methods. token is not the part of dojo.xhrPost, it might be from the ASP.Net for passing authentication token. If you have not setup the security on the service, you could ignore it.
var successFunction = function(args){
//Handle the success response.
}
var failFunction= function(err){
//Handle the failure response.
}
var service = new PickrWebService();
service.createUser(Email,Username,Password,
FirstName,Surname,Birth,Gender,Mobile,Picture,Address,successFunction,failFunction);
Apart from the above code, you need to add the dojo api in you client.
On my .NET Web API 2 server, I am using OWIN for authentication. I have followed Taiseer's tutorial and successfully implemented an access token refresh mechanism.
I would like to know if there are any impacts on anything if clients refresh their access tokens frequently, e.g. refresh once every 5 minutes on average.
I am asking this question because I have a button on my page, when user clicks it, the data on that page is sent to different endpoints. These endpoints are marked with the attribute [Authorize].
Previously, when I send a request to a single protected endpoint, I can check if the response is 401 (unauthorized). If so, I can refresh the user's access token first, then resend the rejected request with the new token. However, I don't know how can the same thing be done this time, as there are so many requests being sent at once. The aforementioned method is implemented in my AngularJS interceptor. It can handle a single but not multiple rejected unauthorized requests.
FYI, here is the code for my interceptor, which is found and modified from a source on GitHub.
app.factory('authInterceptor', function($q, $injector, $location, localStorageService) {
var authInterceptor = {};
var $http;
var request = function(config) {
config.headers = config.headers || {};
var jsonData = localStorageService.get('AuthorizationData');
if (jsonData) {
config.headers.Authorization = 'Bearer ' + jsonData.token;
}
return config;
}
var responseError = function(rejection) {
var deferred = $q.defer();
if (rejection.status === 401) {
var authService = $injector.get('authService');
authService.refreshToken().then(function(response) {
_retryHttpRequest(rejection.config, deferred);
}, function() {
authService.logout();
$location.path('/login');
deferred.reject(rejection);
});
} else {
deferred.reject(rejection);
}
return deferred.promise;
}
var _retryHttpRequest = function(config, deferred) {
$http = $http || $injector.get('$http');
$http(config).then(function(response) {
deferred.resolve(response);
}, function(response) {
deferred.reject(response);
});
}
authInterceptor.request = request;
authInterceptor.responseError = responseError;
return authInterceptor;
});
What sets the return URL for the verification email. Not the link that gets generated and inserted in the email, but when you click the link, it ends up going to a page on your site after its verified. How can I set what page it goes to?
You can set the URL by specifying Accounts.emailTemplates.verifyEmail.text. Here's an example:
Accounts.emailTemplates.siteName = 'MyApp';
Accounts.emailTemplates.from = 'me#example.com';
Accounts.emailTemplates.verifyEmail.subject = function() {
return 'Verify your email address on MyApp';
};
Accounts.emailTemplates.verifyEmail.text = function(user, url) {
var token = url.split('/').pop();
var verifyEmailUrl = Meteor.absoluteUrl("verify-email/" + token);
return verifyEmailEmailBody(verifyEmailUrl);
};
The callback takes a url parameter which is the default URL generated by meteor. You can extract the verification token and then use it to build a custom URL. The function needs to return a body string, which you'll generate by implementing verifyEmailEmailBody.
On the client, you'll need to set up the corresponding route. When the route is run, you can call Accounts.verifyEmail.
You can change the verification url used in the email and then handle that route yourself. Here I'll use /verify and redirect to /wherever if successful.
client
var match = window.location.pathname.match(/^\/verify\/(.*)$/);
var token;
if (match) {
token = match[1];
}
Meteor.startup(function () {
if (token) {
Accounts.verifyEmail(token, function (error) {
if (!error) {
window.location.pathname = '/wherever';
}
});
}
});
server
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify/' + token);
};