matching and verifying Express 3/Connect 2 session keys from socket.io connection - express

I have a good start on a technique similar to this in Express 3
http://notjustburritos.tumblr.com/post/22682186189/socket-io-and-express-3
the idea being to let me grab the session object from within a socket.io connection callback, storing sessions via connect-redis in this case.
So, in app.configure we have
var db = require('connect-redis')(express)
....
app.configure(function(){
....
app.use(express.cookieParser(SITE_SECRET));
app.use(express.session({ store: new db }));
And in the app code there is
var redis_client = require('redis').createClient()
io.set('authorization', function(data, accept) {
if (!data.headers.cookie) {
return accept('Sesssion cookie required.', false)
}
data.cookie = require('cookie').parse(data.headers.cookie);
/* verify the signature of the session cookie. */
//data.cookie = require('cookie').parse(data.cookie, SITE_SECRET);
data.sessionID = data.cookie['connect.sid']
redis_client.get(data.sessionID, function(err, session) {
if (err) {
return accept('Error in session store.', false)
} else if (!session) {
return accept('Session not found.', false)
}
// success! we're authenticated with a known session.
data.session = session
return accept(null, true)
})
})
The sessions are being saved to redis, the keys look like this:
redis 127.0.0.1:6379> KEYS *
1) "sess:lpeNPnHmQ2f442rE87Y6X28C"
2) "sess:qsWvzubzparNHNoPyNN/CdVw"
and the values are unencrypted JSON. So far so good.
The cookie header, however, contains something like
{ 'connect.sid': 's:lpeNPnHmQ2f442rE87Y6X28C.obCv2x2NT05ieqkmzHnE0VZKDNnqGkcxeQAEVoeoeiU' }
So now the SessionStore and the connect.sid don't match, because the signature part (after the .) is stripped from the SessionStore version.
Question is, is is safe to just truncate out the SID part of the cookie (lpeNPnHmQ2f442rE87Y6X28C) and match based on that, or should the signature part be verified? If so, how?

rather than hacking around with private methods and internals of Connect, that were NOT meant to be used this way, this NPM does a good job of wrapping socket.on in a method that pulls in the session, and parses and verifies
https://github.com/functioncallback/session.socket.io

Just use cookie-signature module, as recommended by the comment lines in Connect's utils.js.
var cookie = require('cookie-signature');
//assuming you already put the session id from the client in a var called "sid"
var sid = cookies['connect.sid'];
sid = cookie.unsign(sid.slice(2),yourSecret);
if (sid == "false") {
//cookie validation failure
//uh oh. Handle this error
} else {
sid = "sess:" + sid;
//proceed to retrieve from store
}

Related

Paypal Php Sdk - NotifyUrl is not a fully qualified URL Error

I have this code
$product_info = array();
if(isset($cms['site']['url_data']['product_id'])){
$product_info = $cms['class']['product']->get($cms['site']['url_data']['product_id']);
}
if(!isset($product_info['id'])){
/*
echo 'No product info.';
exit();
*/
header_url(SITE_URL.'?subpage=user_subscription#xl_xr_page_my%20account');
}
$fee = $product_info['yearly_price_end'] / 100 * $product_info['fee'];
$yearly_price_end = $product_info['yearly_price_end'] + $fee;
$fee = ($product_info['setup_price_end'] / 100) * $product_info['fee'];
$setup_price_end = $product_info['setup_price_end'] + $fee;
if(isset($_SESSION['discountcode_amount'])){
$setup_price_end = $setup_price_end - $_SESSION['discountcode_amount'];
unset($_SESSION['discountcode_amount']);
}
$error = false;
$plan_id = '';
$approvalUrl = '';
$ReturnUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=process_agreement';
$CancelUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=cancel_agreement';
$now = $cms['date'];
$now->modify('+5 minutes');
$apiContext = new \PayPal\Rest\ApiContext(
new \PayPal\Auth\OAuthTokenCredential(
$cms['options']['plugin_paypal_clientid'], // ClientID
$cms['options']['plugin_paypal_clientsecret'] // ClientSecret
)
);
use PayPal\Api\ChargeModel;
use PayPal\Api\Currency;
use PayPal\Api\MerchantPreferences;
use PayPal\Api\PaymentDefinition;
use PayPal\Api\Plan;
use PayPal\Api\Patch;
use PayPal\Api\PatchRequest;
use PayPal\Common\PayPalModel;
use PayPal\Api\Agreement;
use PayPal\Api\Payer;
use PayPal\Api\ShippingAddress;
// Create a new instance of Plan object
$plan = new Plan();
// # Basic Information
// Fill up the basic information that is required for the plan
$plan->setName($product_info['name'])
->setDescription($product_info['desc_text'])
->setType('fixed');
// # Payment definitions for this billing plan.
$paymentDefinition = new PaymentDefinition();
// The possible values for such setters are mentioned in the setter method documentation.
// Just open the class file. e.g. lib/PayPal/Api/PaymentDefinition.php and look for setFrequency method.
// You should be able to see the acceptable values in the comments.
$setFrequency = 'Year';
//$setFrequency = 'Day';
$paymentDefinition->setName('Regular Payments')
->setType('REGULAR')
->setFrequency($setFrequency)
->setFrequencyInterval("1")
->setCycles("999")
->setAmount(new Currency(array('value' => $yearly_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
// Charge Models
$chargeModel = new ChargeModel();
$chargeModel->setType('SHIPPING')
->setAmount(new Currency(array('value' => 0, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$paymentDefinition->setChargeModels(array($chargeModel));
$merchantPreferences = new MerchantPreferences();
// ReturnURL and CancelURL are not required and used when creating billing agreement with payment_method as "credit_card".
// However, it is generally a good idea to set these values, in case you plan to create billing agreements which accepts "paypal" as payment_method.
// This will keep your plan compatible with both the possible scenarios on how it is being used in agreement.
$merchantPreferences->setReturnUrl($ReturnUrl)
->setCancelUrl($CancelUrl)
->setAutoBillAmount("yes")
->setInitialFailAmountAction("CONTINUE")
->setMaxFailAttempts("0")
->setSetupFee(new Currency(array('value' => $setup_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$plan->setPaymentDefinitions(array($paymentDefinition));
$plan->setMerchantPreferences($merchantPreferences);
// ### Create Plan
try {
$output = $plan->create($apiContext);
} catch (Exception $ex){
die($ex);
}
echo $output->getId().'<br />';
echo $output.'<br />';
Been working with paypal php sdk for some days now and my code stop working.
So i went back to basic and i am still getting the same damn error.
I am trying to create a plan for subscription but getting the following error:
"NotifyUrl is not a fully qualified URL"
I have no idea how to fix this as i dont use NotfifyUrl in my code?
Could be really nice if anyone had an idea how to fix this problem :)
Thanks
PayPal did a update to their API last night which has caused problem within their SDK.
They are sending back null values in their responses.
I MUST stress the error is not on sending the request to PayPal, but on processing their response.
BUG Report : https://github.com/paypal/PayPal-PHP-SDK/issues/1151
Pull Request : https://github.com/paypal/PayPal-PHP-SDK/pull/1152
Hope this helps, but their current SDK is throwing exceptions.
Use below simple fix.
Replace below function in vendor\paypal\rest-api-sdk-php\lib\PayPal\Api\MerchantPreferences.php
public function setNotifyUrl($notify_url)
{
if(!empty($notify_url)){
UrlValidator::validate($notify_url, "NotifyUrl");
}
$this->notify_url = $notify_url;
return $this;
}
If you get the same error for return_url/cancel_url, add the if condition as above.
Note: This is not a permanent solution, you can use this until getting the update from PayPal.
From the GitHub repo for the PayPal PHP SDK, I see that the error you mentioned is thrown when MerchantPreferences is not given a valid NotifyUrl. I see you're setting the CancelUrl and ReturnUrl, but not the NotifyUrl. You may simply need to set that as well, i.e.:
$NotifyUrl = (some url goes here)
$obj->setNotifyUrl($NotifyUrl);
Reason behind it!
error comes from.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
line.
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
throw new \InvalidArgumentException("$urlName is not a fully qualified URL");
}
FILTER_VALIDATE_URL: according to this php function.
INVALID URL: "http://cat_n.domain.net.in/"; // IT CONTAIN _ UNDERSCORE.
VALID URL: "http://cat-n.domain.net.in/"; it separated with - dash
here you can dump your url.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
public static function validate($url, $urlName = null)
{
var_dump($url);
}
And then check this here: https://www.w3schools.com/PHP/phptryit.asp?filename=tryphp_func_validate_url
you can check here what character will reason for invalid.

How to keep HTTP/2 connection alive till the request / response session is complete?

I am currently using HttpDeclarePushto exploit the Server Push feature in HTTP/2.
I am able to successfully create all the parameters that this function accepts. But the issue is when HttpDeclarePushexecutes it returns a value of 1229 (ERROR_CONNECTION_INVALID) - https://learn.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1000-1299-.
On further investigation I found that the HttpHeaderConnection in _HTTP_HEADER_ID (https://learn.microsoft.com/en-us/windows/desktop/api/http/ne-http-_http_header_id) is actually passed in the function as 'close'. That implies that on every request response the server closes the connection and that is also happening in my case, I checked it in the log.
Here is the code.
class http2_native_module : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS OnBeginRequest(IN IHttpContext * p_http_context, IN IHttpEventProvider * p_provider)
{
HTTP_REQUEST_ID request_id;
const HTTPAPI_VERSION version = HTTPAPI_VERSION_2;
auto pHttpRequest = p_http_context->GetRequest();
auto phttpRequestRaw = pHttpRequest->GetRawHttpRequest();
HANDLE p_req_queue_handle = nullptr;
auto isHttp2 = phttpRequestRaw->Flags;
try {
const auto request_queue_handle = HttpCreateRequestQueue(version, nullptr, nullptr, NULL, &p_req_queue_handle);
const auto verb = phttpRequestRaw->Verb;
const auto http_path = L"/polyfills.0d74a55d0dbab6b8c32c.js"; //ITEM that I want to PUSH to client
const auto query = nullptr;
request_id = phttpRequestRaw->RequestId;
auto headers = phttpRequestRaw->Headers;
auto connId = phttpRequestRaw->ConnectionId;
WriteEventViewerLog(L"OnBeginRequest - Entering HTTPDECLAREPUSH");
headers.KnownHeaders[1].pRawValue = NULL;
headers.KnownHeaders[1].RawValueLength = 0;
const auto is_success = HttpDeclarePush(p_req_queue_handle, request_id, verb, http_path, query, &headers);
sprintf_s(szBuffer, "%lu", is_success);
Log("is_success value", szBuffer); //ERROR CODE 1229 here
HttpCloseRequestQueue(p_req_queue_handle);
}
catch (std::bad_alloc & e)
{
auto something = e;
}
return RQ_NOTIFICATION_CONTINUE;
}
I even tried to update the header connection value as below but it still gives me 1229.
headers.KnownHeaders[1].pRawValue = NULL;
headers.KnownHeaders[1].RawValueLength = 0;
I understand from https://http2.github.io/http2-spec/ that HTTP/2 actually ignores the content in HTTP HEADERs and uses some other mechanism as part of its FRAME.
This brings us to the next question on how we can keep the connection OPEN and is it something related to the FRAME (similar to HEADER) that HTTP2 uses, if so, how C++ or rather Microsoft helps us to play and exploit with the FRAME in HTTP2?

OpenFire: In an IQHandler, how to get the authenticated user that sent it?

I want to implement an IQHandler, but I want to make sure that only authenticated users can send IQ Packets to it. I want to make sure that the JID I get from Packet.getFrom() is the authenticated user that sent it.
I need this so that no one can just create an IQ Packet and set the "from" attribute to a user id other than their own. Can someone help me with this?
Try this:
ClientSession session = sessionManager.getSession(sender);
if(session.getStatus() == Session.STATUS_AUTHENTICATED) {
//YOUR STUFF HERE
}
UPDATE:
Looking closer at the source. It appears that the IQRouter already does this for you. If you are not authenticated the server response with an error stating just that.
public void route(IQ packet) {
if (packet == null) {
throw new NullPointerException();
}
JID sender = packet.getFrom();
ClientSession session = sessionManager.getSession(sender);
try {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
JID to = packet.getTo();
if (session != null && to != null && session.getStatus() == Session.STATUS_CONNECTED &&
!serverName.equals(to.toString())) {
// User is requesting this server to authenticate for another server. Return
// a bad-request error
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.bad_request);
session.process(reply);
Log.warn("User tried to authenticate with this server using an unknown receipient: " +
packet.toXML());
}
else if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED || (
isLocalServer(to) && (
"jabber:iq:auth".equals(packet.getChildElement().getNamespaceURI()) ||
"jabber:iq:register"
.equals(packet.getChildElement().getNamespaceURI()) ||
"urn:ietf:params:xml:ns:xmpp-bind"
.equals(packet.getChildElement().getNamespaceURI())))) {
handle(packet);
}
else {
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.not_authorized);
session.process(reply);
}
// Invoke the interceptors after we have processed the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, true);
}

Issue with BTC-e API in App Script, method parameter

I am trying to incorporate the BTC-e.com API in to a google docs spreadsheet.
The API documentation is here: https://btc-e.com/api/documentation
The method name is sent via POST parameter method.
As the URLFetchApp requires me to set the type of request as POST by a parameter method and I then have another parameter called method to be set as getInfo.
How can I go about setting the fetch method as POST and have the API parameter method as getInfo.
Below is the function this relates too. Also I am sure there a more issues in my work I am yet to find.
function inventory() {
var nonce=Number(SpreadsheetApp.getActiveSheet().getRange('K2').getValue());
var token=SpreadsheetApp.getActiveSheet().getRange('K1').getValue();
var tokenEndpoint = "https://btc-e.com/tapi";
var sign= 'TEMP'
var head = {
'Content-Type': 'application/x-www-form-urlencoded',
'Key': token,
'Sign': sign
}
var params = {
method : "POST",
method : "getInfo",
headers: head,
contentType: 'application/x-www-form-urlencoded',
method : "getInfo",
nonce: nonce
}
var request = UrlFetchApp.getRequest(tokenEndpoint, params);
var response = UrlFetchApp.fetch(tokenEndpoint, params);
var response2=String(response);
SpreadsheetApp.getActiveSheet().getRange('K2').setValue(nonce+1);
SpreadsheetApp.getActiveSheet().getRange('I16').setValue(response2);
SpreadsheetApp.getActiveSheet().getRange('I17').setValue(nonce);
}
This just yields the error
Attribute provided with invalid value: method
Thanks,
Steve
PS: First time posting, I tried to get the format correct.
I made the following Google JavaScript function to do POST access to BTC-e. You can find this function in action in the example spreadsheet I made to demonstrate the BTC-e API functions.
function btceHttpPost(keyPair, method, params, nonce) {
if (keyPair === undefined) {
return "{'error':'missing key pair'}"
}
if (params === undefined) {
params = '';
}
// Cleanup keypair, remove all \s (any whitespace)
var keyPair = keyPair.replace(/[\s]/g, '');
// Keypair example: "AFE730YV-S9A4FXBJ-NQ12HXS9-CA3S3MPM-CKQLU0PG,96a00f086824ddfddd9085a5c32b8a7b225657ae2fe9c4483b4c109fab6bf1a7"
keyPair = keyPair.split(',');
var pubKey = keyPair[0];
var privKey = keyPair[1];
// As specified on the BTC-e api (https://btc-e.com/api/documentation) the
// nonce POST parameter must be an incrementing integer (>0). The easiest
// implementation is the use of a timestamp (TS), so there is no need
// for persistant storage. Preferable, the resolution of the TS should be
// small enough the handle the desired call-frequency (a sleep of the TS
// resolution can fix this but I don't like such a waste). Another
// consideration is the sizeof the nonce supported by BTC-e. Experiments
// revealed this is a 32 bit unsigned number. The native JavaScript TS,
// stored in a float, can be 53 bits and has a resolution of 1 ms.
if (nonce === undefined)
// This time stamp counts amount of 200ms ticks starting from Jan 1st, 2014 UTC
// On 22 Mar 2041 01:17:39 UTC, it will overflow the 32 bits and will fail
// the nonce key for BTC-e
var nonce = Math.floor((Date.now() - Date.UTC(2014,0)) / 200);
// Construct payload message
var msg = 'nonce=' + nonce + '&method=' + method + params;
var msgSign = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, msg, privKey);
// Convert encoded message from byte[] to hex string
for (var msgSignHex = [], i = 0; i < msgSign.length; i++) {
// Doing it nibble by nibble makes sure we keep leading zero's
msgSignHex.push(((msgSign[i] >>> 4) & 0xF).toString(16));
msgSignHex.push((msgSign[i] & 0xF).toString(16));
}
msgSignHex = msgSignHex.join('');
var httpHeaders = {'Key': pubKey, 'Sign': msgSignHex};
var fetchOptions = {'method': 'post', 'headers': httpHeaders, 'payload': msg};
var reponse = UrlFetchApp.fetch('https://btc-e.com/tapi', fetchOptions);
return reponse.getContentText();
};
The problem looks to be with your params object . You have method set thrice in the same object, which is a source of confusion.
Next, take a look at the documentation for UrlFetchApp.fetch() ( https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetch(String,Object) ) . The method can take a value of post, get, delete, put.
The getInfo should probably be appended to your URL to make it
var tokenEndpoint = "https://btc-e.com/tapi/getInfo"
Per the docs, you also have to put in more parameters to the request, nonce, api key etc. Use this as a starting point, revisit the documentation and get back to SO if you still have trouble

Does ServiceStack RedisClient support Sort command?

my sort command is
"SORT hot_ids by no_keys GET # GET msg:->msg GET msg:->count GET msg:*->comments"
it works fine in redis-cli, but it doesn't return data in RedisClient. the result is a byte[][], length of result is correct, but every element of array is null.
the response of redis is
...
$-1
$-1
...
c# code is
data = redis.Sort("hot_ids ", new SortOptions()
{
GetPattern = "# GET msg:*->msg GET msg:*->count GET msg:*->comments",
Skip = skip,
Take = take,
SortPattern = "not-key"
});
Redis Sort is used in IRedisClient.GetSortedItemsFromList, e.g. from RedisClientListTests.cs:
[Test]
public void Can_AddRangeToList_and_GetSortedItems()
{
Redis.PrependRangeToList(ListId, storeMembers);
var members = Redis.GetSortedItemsFromList(ListId,
new SortOptions { SortAlpha = true, SortDesc = true, Skip = 1, Take = 2 });
AssertAreEqual(members,
storeMembers.OrderByDescending(s => s).Skip(1).Take(2).ToList());
}
You can use the MONITOR command in redis-cli to help diagnose and see what requests the ServiceStack Redis client is sending to redis-server.