Secured way to communicate between servers - node.js

We are passing secret keys to authenticate the GET requests between https enabled websites. Which of the following ways are more secured:
GET /auth?secret=8727n2i752gns982jsn'
Only 2 servers know that secret keys.
Or should we set headers as follows:
request({
url: '/auth',
headers: {
'secretKey': 's87ehwdiw8y3dhj'
}
});
Which method is more secured and why?

Ideally sending secret key isn't a good option. But if there is utmost need I would suggest you to send the key in the headers like:
request({
url: '/auth',
headers: {
'secretKey': 's87ehwdiw8y3dhj'
}
});
If you give a "secret key" to a browser, it's not secret anymore. Javascript in the browser is just too open to really keep a key secret.
As it is less visible, yet anyone can sniff it as it's just javascript.
Here are few links to enlighten you more:
1
2
3
4

request({
url: '/auth',
headers: {
'Authorization': 'Bearer s87ehwdiw8y3dhj'
}
});
This is a standardized way of passing tokens through header. The security part is really more about your token creation.
'Bearer' is the type of Authorization you are using. It could be 'Basic', etc
ie
'Authorization': 'Basic s87ehwdiw8y3dhj'

Related

Safest way to persist logged in state when forced to use basic auth

Whats the best/safest way to store credentials for subsequent calls to an API using Basic Auth which I dont have control over?
We're apparently only using basic auth for our beta testing phase thankfully, but I have lived long enough to know that sometimes security details get lost on product people and beta users reuse passwords, so want to make it as secure as I can just in case despite it being a temporary thing.
Currently I am using session storage to store credentials in base64 form. Is that really the best that can be done?
Note that in order to get rid of the ugly browser login prompt, the WWW-Authenticate header has been removed from the server response. This means that the browser no longer 'magically' caches auth info for subsequent calls, so I need to do it manually somehow.
This what I am currently using. While it technically works, are there ways I can decrease the security risks?
const baseUrl = getServerBaseUrl()
const authenticationService = {
authenticate: (username, password) => {
const token = btoa(`${username}:${password}`)
sessionStorage.setItem('credentials', token)
return axios.get(baseUrl, {
headers: {
Authorization: `Basic ${token}`,
},
})
},
checkAuthentication: () => {
return axios.get(baseUrl, {
headers: {
Authorization: `Basic ${sessionStorage.getItem('credentials')}`,
},
})
},
}

Switching YouTube user with OAuth2 shows previous user's data in Chrome

I have a Chrome Packaged App that uses oauth2 to authenticate to YouTube. I'm using YouTube to determine the user's channel (via the channel endpoint).
It works for the first user authenticated. But if I switch to a different user, the same YouTube call returns the previous user's data (i.e. channel).
Here are the steps I'm going through.
I get my auth token via a call to getAuthToken:
chrome.identity.getAuthToken({ interactive: true, scopes: ['https://www.googleapis.com/auth/youtube'] })
I get their channel information. I make a call to the channels endpoint like so:
const url = 'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true';
const headers = { Authorization: `Bearer ${token}` };
const config = {
url,
method: 'get',
headers,
responseType: 'json',
};
return axios(config);
This works! The result of the call gives me the channel information.
The user removes their account. I use the same sequence of calls that the Google Demo uses (like this example):
a. call chrome.identiy.removeCachedAuthToken({ token }) with the token
b. call https://accounts.google.com/o/oauth2/revoke?token=${token} to revoke it
Everything is cleared out, I think.
If I look at chrome://identity-internals/ I still see the token, but the Token Status is set to Not Found
The issue:
I repeat from step 1, but I chose a different user.
I confirm that I get a new token that is different than the one I had previously
The call to the YouTube channels api returns the previous user's channel.
It turns out it was a caching issue with youtube.
I had to add 'Cache-Control': 'no-cache' to my headers.
Here is the full headers line:
const headers = {
Authorization: `Bearer ${token}`,
'Cache-Control': 'no-cache',
};

Autodesk Forge Error When Accessing API in Node

I have a 3-legged authentication token, and for some reason when I access the API, I get the following error:
"reason":"Only 2 legged service tokens are allowed to access this api."
This is how I access the API (using the npm package curlrequest, and replacing with my token):
var options = {
url: 'https://developer.api.autodesk.com/project/v1/hubs',
method: 'GET',
headers: {
'Authorization': 'Bearer <Token>'
}
};
curl.request(options, function (err, parts) {
parts = parts.split('\r\n');
var data = parts.pop()
, head = parts.pop();
console.log(data);
});
Am I doing something wrong? Is it only possible to access the Data Management API with a 2-legged token?
Thanks.
So, yes, you can only use 2-legged tokens on the Data Management API. You cannot use 3-legged tokens instead of 2-legged ones, they are meant to do different things.

Error with signature when creating an outbound shipment on Amazon MWS API

I'm trying to create an outbound fulfillment (CreateFulfillmentOrder) on the Amazon MWS API. I'm using the node.js package mws-api to make the request, which I have modified myself since it didn't support this operation correctly. In any case, this is the exact request that I'm sending to the API (with sensitive information changed):
{
uri: 'https://mws.amazonservices.co.uk/FulfillmentOutboundShipment/2010-10-01',
method: 'POST',
headers:
{ Host: 'mws.amazonservices.co.uk',
'User-Agent': 'mws-api/0.1.0 (Language=JavaScript)',
'content-type': 'application/x-www-form-urlencoded',
'content-length': 1095 },
body: 'SellerFulfillmentOrderId=403-9883411-3564317&ShippingSpeedCategory=Standard&DisplayableOrderId=403-9883411-3564317&DisplayableOrderDateTime=2013-12-29&DisplayableOrderComment=Order&NotificationEmailList.member.1=3yv4at7rtl3blsy%40marketplace.amazon.co.uk&DestinationAddress.Name=Amazon%20Taro&DestinationAddress.Line1=COMPANY%NAME&DestinationAddress.City=GORING&DestinationAddress.StateOrProvinceCode=Oxon&DestinationAddress.PostalCode=RG8%209AQ&DestinationAddress.CountryCode=GB&DestinationAddress.PhoneNumber=01575375219&Items.member.1.DisplayableComment=item&Items.member.1.GiftMessage=gift&Items.member.1.PerUnitDeclaredValue.Value=4.29&Items.member.1.PerUnitDeclaredValue.CurrencyCode=GBP&Items.member.1.Quantity=1&Items.member.1.SellerFulfillmentOrderItemId=CW0298&Items.member.1.SellerSKU=CW0298&Action=CreateFulfillmentOrder&Version=2010-10-01&Timestamp=2017-04-12T14%3A39%3A22.707Z&AWSAccessKeyId=LYIAJ83AORVSI6WLBTQA&SellerId=7RVB74LQNDFDB1&SignatureMethod=HmacSHA256&SignatureVersion=2&Signature=nlB5UTDUXexkurlsc7e%2F4tqLO8vcMRy4X%6Oa0rPIapZN8%3D' }
But all I'm getting in response is this error:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
I don't there's any problem with the signature, it has worked correctly while doing other operations. I think that's just the error Amazon gives you when something's wrong on the request, but I cannot find the problem. Does anyone know what could be the problem?
Thanks.
You need to use Amazon Host mws.amazonaws.com.
Ex:
{
uri: 'https://mws.amazonaws.com/FulfillmentOutboundShipment/2010-10-01',
method: 'POST',
headers:
{ Host: 'mws.amazonaws.com',
'User-Agent': 'mws-api/0.1.0 (Language=JavaScript)',
'content-type': 'application/x-www-form-urlencoded',
'content-length': 1095
},
body:'SellerFulfillmentOrderId=403-9883411-3564317&ShippingSpeedCategory=Standard&DisplayableOrderId=403-9883411-3564317&DisplayableOrderDateTime=2013-12-29&DisplayableOrderComment=Order&NotificationEmailList.member.1=3yv4at7rtl3blsy%40marketplace.amazon.co.uk&DestinationAddress.Name=Amazon%20Taro&DestinationAddress.Line1=COMPANY%NAME&DestinationAddress.City=GORING&DestinationAddress.StateOrProvinceCode=Oxon&DestinationAddress.PostalCode=RG8%209AQ&DestinationAddress.CountryCode=GB&DestinationAddress.PhoneNumber=01575375219&Items.member.1.DisplayableComment=item&Items.member.1.GiftMessage=gift&Items.member.1.PerUnitDeclaredValue.Value=4.29&Items.member.1.PerUnitDeclaredValue.CurrencyCode=GBP&Items.member.1.Quantity=1&Items.member.1.SellerFulfillmentOrderItemId=CW0298&Items.member.1.SellerSKU=CW0298&Action=CreateFulfillmentOrder&Version=2010-10-01&Timestamp=2017-04-12T14%3A39%3A22.707Z&AWSAccessKeyId=LYIAJ83AORVSI6WLBTQA&SellerId=7RVB74LQNDFDB1&SignatureMethod=HmacSHA256&SignatureVersion=2&Signature=nlB5UTDUXexkurlsc7e%2F4tqLO8vcMRy4X%6Oa0rPIapZN8%3D'
}
This may help you.

How do I use the node.js request module to make an SSL call with my own certificate?

I'm using node.js and this request module to make HTTP calls to another server.
https://github.com/mikeal/request
It works great. I now need to modify this code to make the calls over SSL, using my company's SSL certificate. In the request module's docs, it says this about the strictSSL option:
"strictSSL - Set to true to require that SSL certificates be valid. Note: to use your own certificate authority, you need to specify an agent that was created with that ca as an option."
This sounds like what I need to do, but I don't understand this phrase: "specify an agent that was created with that ca as an option.".
1) What do they mean by "an agent"?
2) How do I "specify an agent"
3) How do I create the agent "with that ca as an option"?
A code example would be amazing, but any leads would be helpful. Thanks.
This largely elaborates on Peter Lyons' answer, providing an example.
I am assuming that you are requesting a domain running over HTTPS with a certificate signed by your own certificate authority (ca).
When using the request library, as you do, there is no need to actually instantiate the agent yourself, you can simply provide some agentOptions to the request you are making. The following is an example:
request({
method: "POST",
uri: "https://localhost/entries",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "someEntry"
}),
agentOptions: {
ca: fs.readFileSync("certs/ca.cert.pem")
}
}, function(error, httpResponse, body) {
//handle response
});
The important thing here is the agentOptions, which you provide the certificate of a ca. All domains using certificates signed by the ca are now accepted. Imagine a ca CA1 has signed three domains, D1, D2, D3. Setting the ca to CA1 results in allowing requests to all of the domains D1, D2, D3 (but not D4 signed by a different ca).
Point being: the "certs/ca.cert.pem" must be the certificate of the signing certificate authority.
"an agent" means an instance of http.Agent from the node standard http module
The docs indicate this agent instance would be passed to request in the pool option I believe, although I haven't done it myself and the docs are indeed sparse on details here. Based on skimming the code, I think you might just need options.ca
request seems to directly support options.ca and uses it here in getAgent
So my guess is maybe just pass in options.ca as a string that is the public key of your company's certificate authority and see if request does the right thing from there.
const request = require('request');
request.post({
url: strRSAUrl,
agentOptions: {
ca: fs.readFileSync('path-to-cacert.pem')
},
form: {
some_key: some_value,
}
}, function (error, response, body) {
objResponse.send(body);
});
For more details,you can refer from nodejs#request
perhaps I'm misunderstanding the problem, but in my experience you don't need to do anything special at all if you require('https'), the call automatically goes out over SSL.
I just tested this with my google maps api call and indeed if I require('http') Google complains that it wants the call to come in over SSL, but when I add the s everything works as expected.
We can use agentOptions property to attach the certificates and also we can add rejectUnauthorized: true to ensure invalid request to be rejected.
var request = require("request");
var fs = require("fs");
request(
{
url: requestURL.url,
method: requestURL.method,
headers: custom_headers
? custom_headers
: {
"Content-Type": "text/plain ; charset=utf-8"
},
body: requestParam,
agentOptions: {
ca: [
fs.readFileSync("./ROOT.pem"),
fs.readFileSync("./Intermediate.pem"),
],
},
rejectUnauthorized: true,
},
function (error, response, body) {
console.log(error, "error");
console.log(response, response);
console.log(body, "body");
}
);

Resources