Help generate Facebook API "Sig" in Python - python

I have been struggling with this for over two days and I could use your help. Here's the problem:
Whenever a request is made to the Facebook REST server, we have to send an additional parameter called "sig". This sig is generated using the following algorithm:
<?php
$secret = 'Secret Key'; // where 'Secret Key' is your application secret key
$args = array(
'argument1' => $argument1,
'argument2' => $argument2); // insert the actual arguments for your request in place of these example args
$request_str = '';
foreach ($args as $key => $value) {
$request_str .= $key . '=' . $value; // Note that there is no separator.
}
$sig = $request_str . $secret;
$sig = md5($sig);
?>
More information about this: http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application
I have been trying to reproduce this piece of code in Python, here is my attempt:
def get_signature(facebook_parameter):
sig = ""
for key, value in facebook_parameter.parameters:
sig += key + "=" + value
sig += facebook_parameter.application_secret
return hashlib.md5(sig).hexdigest()
facebook_paremeter.parameters is a list that looks like this:
[('api_key', '...'), ('v', '1.0'), ('format', 'JSON'), ('method', '...')]
and facebook_paremeter.application_secret is a valid app secret.
This code is running on the Google App Engine development platform (if that makes any difference). Python 2.6.4.
Can somebody help me find out where my code is going wrong?
Thanks,
Sri

List had to be sorted.

Related

PHP bcrypt to PYTHON bycrypt not giving same values

Hello I am building an API on python to create a user and insert password in database. The problem is that the application is on Laravel PHP and using bcrypt. For example encrypting "test1234$%" in PYTHON gives "$2b$12$rsGZPtjctbI6bSGzS4P3mOSdrABnJuHfnKxEQwvm4KFu72BN3XNKK" and encrypting same in PHP gives "$2y$10$cO2nvRURLRdlW8j6CbWu8OeVlv7dyeozpBZcxVB2nd8hbyILyg7Xa"
and when trying to login with users created by the api on the app it does not work.
Even if i test with this it does not work the output is invalid:
$hash = '$2b$12$rsGZPtjctbI6bSGzS4P3mOSdrABnJuHfnKxEQwvm4KFu72BN3XNKK';
//validade hash in php
if(password_verify ( "test1234$%", $hash )){
echo "valid";
} else {
echo "invalid";
}
echo("\n".phpversion());
on python side used the following code:
pip install bcrypt
import bcrypt
password = item.password
bpassword = b"password"
hashed = bcrypt.hashpw(bpassword, bcrypt.gensalt())
on PHP side:
if (! function_exists('bcrypt')) {
/**
* Hash the given value against the bcrypt algorithm.
*
* #param string $value
* #param array $options
* #return string
*/
function bcrypt($value, $options = [])
{
return app('hash')->driver('bcrypt')->make($value, $options);
}
}
bcrypt use different salt each runtime that is why its perfect for storing password on database... unless you force it to use the same salt each time it will keep generating different resulting hash
I found a solution in the Python api i call bcrypt in PHP using subprocess
code = """echo password_hash("""'"'+item.password+'"'""",PASSWORD_BCRYPT);"""
hashed_password = await myClass.php(code)
async def php(self, code):
p = subprocess.Popen(["php", "-r", code],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = p.communicate()
if out[1] != b'': raise Exception(out[1].decode('UTF-8'))
return out[0].decode('UTF-8')

How do I pass a variable from my local Python code to a remote PHP script that writes to a MySQL database?

On my local PC I want to use Python to both send and receive data on a remote MySQL database, via a PHP file that is located on the same webserver as the MySQL database.
I can already UPDATE the MySQL database when I run the following PHP script on the webserver:
<?php
$host_name = 'host';
$database = 'db';
$user_name = 'user';
$password = 'pass';
$conn = new mysqli($host_name, $user_name, $password, $database);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "UPDATE test
SET test = 1
WHERE test = 0";
if ($conn->query($sql) === TRUE) {
echo "Record updated successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
I have searched for hours but so far cannot make any decent attempt at Python code that will send a variable to the PHP code that will in turn update the MySQL database.
The PHP file seems to be publicly accessible so I don't imagine webserver credentials are required in my Python?
Thank you in advance SO!
With a local PHP server running using php -S localhost:8881 receive.php
send.py
import requests
url = 'http://localhost:8881'
myobj = {'key1': 'value1'}
x = requests.post(url, data = myobj)
print (x.text)
receive.php
<?php
var_dump($_POST);
Output of running send.py will be:
array(1) {
["key1"]=>
string(6) "value1"
}
For future potential visitors, below is the combination of Python and PHP that finally wrote a value from my local Python file -> to the remote PHP file -> which wrote successfully to a MySQL database.
I write the integer 6 where the value 2 exists in the database table.
Python:
import requests
url = 'https://url/file_name.php'
myobj = {'key1': 6}
x = requests.post(url, data = myobj)
print (x.text)
PHP on server (file_name.php):
<?php
$host_name = 'hostname';
$database = 'db_name';
$user_name = 'user_name';
$password = 'password';
$conn = new mysqli($host_name, $user_name, $password, $database);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Set variable post_data to equal the value of 'key1', which also had to be converted into an integer
$post_data = intval($_POST['key1']);
// SQL Query
$sql = "UPDATE table_name
SET column_name = $post_data
WHERE value = 2";
if ($conn->query($sql) === TRUE) {
echo "Record updated successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
I get the feeling there's probably a lot of useless information in the PHP file, but I'm a PHP newbie and just glad it's working now :)

Pass a file from a PHP server to a Python server (HTTP request)

I have a web application running on a Laravel PHP server. For some needs (Word document processing), I implemented a Python server that does data extraction. I would like to know how to call my Python server from PHP by passing a file to it.
Currently, I save the docx file on the PHP server, accessible via a url. I make an http POST request from the PHP server to the Python server with the URL to download the document. The problem is that I get a deadlock since the PHP server is waiting on the response from the Python server and the Python server is waiting on the PHP server to download the document. Any suggestions on how to get around this problem?
Here the PHP code:
// Send POST REQUEST
$context_options = array(
'http' => array(
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n"
. "Content-Length: " . strlen($data) . "\r\n",
'content' => $data,
'timeout' => 10,
)
);
$context = stream_context_create($context_options);
$result = fopen('http://localhost:5000/api/extraction','r', false, $context);
And here the Python code:
#app.route('/api/extraction', methods=['post'])
def extraction():
data = request.form.to_dict()
url = data['file'] # get url
filename = secure_filename(url.rsplit('/', 1)[-1])
path = os.path.join(app.config['UPLOAD_FILE_FOLDER'], filename)
urllib.request.urlretrieve(url, path)
You should send the file through proper POST (multipart/form) request instead of having Python fetching the data. It's much harder to debug and maintain than your current 2-roundtrip approach.
Approach 1: Normal Form Request
<?php
/**
* A genertor that yields multipart form-data fragments (without the ending EOL).
* Would encode all files with base64 to make the request binary-safe.
*
* #param iterable $vars
* Key-value iterable (e.g. assoc array) of string or integer.
* Keys represents the field name.
* #param iterable $files
* Key-value iterable (e.g. assoc array) of file path string.
* Keys represents the field name of file upload.
*
* #return \Generator
* Generator of multipart form-data fragments (without the ending EOL) in array format,
* always contains 2 values:
* 0 - An array of header for a key-value pair.
* 1 - A value string (can contain binary content) of the key-value pair.
*/
function generate_multipart_data_parts(iterable $vars, iterable $files=[]): Generator {
// handle normal variables
foreach ($vars as $name => $value) {
$name = urlencode($name);
$value = urlencode($value);
yield [
// header
["Content-Disposition: form-data; name=\"{$name}\""],
// value
$value,
];
}
// handle file contents
foreach ($files as $file_fieldname => $file_path) {
$file_fieldname = urlencode($file_fieldname);
$file_data = file_get_contents($file_path);
yield [
// header
[
"Content-Disposition: form-data; name=\"{$file_fieldname}\"; filename=\"".basename($file_path)."\"",
"Content-Type: application/octet-stream", // for binary safety
],
// value
$file_data
];
}
}
/**
* Converts output of generate_multipart_data_parts() into form data.
*
* #param iterable $parts
* An iterator of form fragment arrays. See return data of
* generate_multipart_data_parts().
* #param string|null $boundary
* An optional pre-generated boundary string to use for wrapping data.
* Please reference section 7.2 "The Multipart Content-Type" in RFC1341.
*
* #return array
* An array with 2 items:
* 0 - string boundary
* 1 - string (can container binary data) data
*/
function wrap_multipart_data(iterable $parts, ?string $boundary = null): array {
if (empty($boundary)) {
$boundary = '-----------------------------------------boundary' . time();
}
$data = '';
foreach ($parts as $part) {
list($header, $content) = $part;
// Check content for boundary.
// Note: Won't check header and expect the program makes sense there.
if (strstr($content, "\r\n$boundary") !== false) {
throw new \Exception('Error: data contains the multipart boundary');
}
$data .= "--{$boundary}\r\n";
$data .= implode("\r\n", $header) . "\r\n\r\n" . $content . "\r\n";
}
// signal end of request (note the trailing "--")
$data .= "--{$boundary}--\r\n";
return [$boundary, $data];
}
// build data for a multipart/form-data request
list($boundary, $data) = wrap_multipart_data(generate_multipart_data_parts(
// normal form variables
[
'hello' => 'world',
'foo' => 'bar',
],
// files
[
'upload_file' => 'path/to/your/file.xlsx',
]
));
// Send POST REQUEST
$context_options = array(
'http' => array(
'method' => 'POST',
'header' => "Content-type: multipart/form-data; boundary={$boundary}\r\n"
. "Content-Length: " . strlen($data) . "\r\n",
'content' => $data,
'timeout' => 10,
)
);
$context = stream_context_create($context_options);
$result = fopen('http://localhost:5000/api/extraction','r', false, $context);
Your Python script should receive the file as a normal HTTP form file upload (with the file field named "upload_file"). Use your framework supported method to get the file from the request.
Approach 2: A really long x-www-form-urlencoded value
If you're concern about binary safety, or if it somehow failed, the other approach would be submitting the file as a base64 encoded string:
<?php
$file_data = file_get_contents('/some');
$data = urlencode([
'upload_file' => base64_encode('path/to/your/file.xlsx'),
]);
// Send POST REQUEST
$context_options = array(
'http' => array(
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n"
. "Content-Length: " . strlen($data) . "\r\n",
'content' => $data,
'timeout' => 10,
)
);
$context = stream_context_create($context_options);
$result = fopen('http://localhost:5000/api/extraction','r', false, $context);
You'd get the file data on your Python server in base64 encoded string on the field named "upload_file". You need to decode to get the original binary content.
Approach 3: If you insist...
If you insist on your current 2-roundtrip approach, the simple solution is to have 2 different endpoints:
One for sending POST request to your Python application.
One for serving the xlsx file without any requirement to the Python application.
From your description, your deadlock is there because you're using the same script for these purpose. I don't see a reason why they can't be 2 separated script / route controller.

web3.py return from a get function in strange format

i'm developing a python app. This app is just to get data from the blockchain. In web3.js it all works good, but i need to do it in python (the client wants a python app). It all works almost good; The script does what it needs to do, but when calling the get function i get a strange output (using the get function on remix or web3.js whit a nodeJs api that i wrote works just perfect) :
D:\pytoh\b_get\Scripts\python.exe C:/Users/Alessandro/PycharmProjects/pytoh/b_get/bget.py
True
[(b'\xe9:=\xd87/\x98\x00\xe4\x89\xe3\x8eb[c\x9cj\xc4\xa3\xa2b\x1d\x9a\xf0%\x1d\xdaB\xb7\xd9\xc4\xe3',
b'\xe9:=\xd87/\x98\x00\xe4\x89\xe3\x8eb[c\x9cj\xc4\xa3\xa2b\x1d\x9a\xf0%\x1d\xdaB\xb7\xd9\xc4\xe3')]
Process finished with exit code 0
The output that i need is like this (i don't understand why the py output is like that):[0xe93a3dd8372f9800e489e38e625b639c6ac4a3a2621d9af0251dda42b7d9c4e3,0xe93a3dd8372f9800e489e38e625b639c6ac4a3a2621d9af0251dda42b7d9c4e3]
The python script is simple (i'm still writing it):
import json
from web3 import Web3
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:7545'))
print(w3.isConnected())
with open("ABI.json") as f:
info_json = json.load(f)
abi = info_json["output"]["abi"]
address = "0x1305ef6377fe8cB7C6dD7Eb2B6cAD83A34fC7503"
get = w3.eth.contract(address=address, abi=abi)
result=get.functions.getUser("0xe93a3dd8372f9800e489e38e
625b639c6ac4a3a2621d9af0251dda42b7d9c4e3").call()
print(result)
And this is the smart contract that the client wrote:
pragma solidity ^0.4.26;
pragma experimental ABIEncoderV2;
contract crud{
address owner;
constructor() public{
owner = msg.sender;
}
modifier onlyOwner () {
require(msg.sender == owner);
_;
}
struct user{
bytes32 hash_json;
bytes32 hash_corso;
}
mapping (bytes32 => user[]) public users;
function setUser(bytes32 _hash, bytes32 _hash_json, bytes32 _hash_corso) onlyOwner public {
user memory usr;
usr.hash_json=_hash_json;
usr.hash_corso= _hash_corso;
users[_hash].push(usr);
}
function getUser(bytes32 _hash) view public returns (user[] memory) {
return users[_hash];
}
}
Thank for your help!
The result is a byte array. When you print it, it converts control characters to hex but prints readable characters.
To get a full hex string, try this:
result = bytearray([22,32,7,67,14,87,90]) # for testing
hxstr = "".join(["{:02x}".format(v) for v in result])
print("0x"+hxstr)
Output
0x162007430e575a

Need some help on how to implement an restfull api app based on golang

My coding skills are a bit low :)
Recently i started learning golang and how to handle an Api communication app. Have been having a great time learning it by myself, golang is revealing itself as a challenging language with great rewards in the end (code sense ^^).
Have been trying to create a cryptsy api lib for golang based on their API V2 (BETA) which is a restfull api. They have a python lib on their api website https://github.com/ScriptProdigy/CryptsyPythonV2/blob/master/Cryptsy.py.
So far have been able to get the public access working but am having a really hard time at the private access because of the authentication part.. I find that the info they give on their website on how to implement it is a bit confusing :(
Authorization is performed by sending the following variables into the request header Key
Public API key.
All query data (nonce=blahblah&limit=blahblah) signed by a secret key according to HMAC-SHA512 method. Your secret key and public keys can be generated from your account settings page. Every request requires a unique nonce. (Suggested to use unix timestamp with microseconds)
For this authentication part the python code goes as:
def _query(self, method, id=None, action=None, query=[], get_method="GET"):
query.append(('nonce', time.time()))
queryStr = urllib.urlencode(query)
link = 'https://' + self.domain + route
sign = hmac.new(self.PrivateKey.encode('utf-8'), queryStr, hashlib.sha512).hexdigest()
headers = {'Sign': sign, 'Key': self.PublicKey.encode('utf-8')}
Got this far in golang:
package main
import(
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
const (
API_BASE_CRY = "https://api.cryptsy.com/api/"
API_VERSION_CRY = "v2"
API_KEY_CRY = "xxxxx"
API_SECRET_CRY = "xxxxxxxxxxxx"
DEFAULT_HTTPCLIENT_TIMEOUT = 30 // HTTP client timeout
)
type clientCry struct {
apiKey string
apiSecret string
httpClient *http.Client
}
type Cryptsy struct {
clientCry *clientCry
}
type CryptsyApiRsp struct {
Success bool `json:"success"`
Data json.RawMessage `json:"data"`
}
func NewCry(apiKey, apiSecret string) *Cryptsy {
clientCry := NewClientCry(apiKey, apiSecret)
return &Cryptsy{clientCry}
}
func NewClientCry(apiKey, apiSecret string) (c *clientCry) {
return &clientCry{apiKey, apiSecret, &http.Client{}}
}
func ComputeHmac512Hex(secret, payload string) string {
h := hmac.New(sha512.New, []byte(secret))
h.Write([]byte(payload))
return hex.EncodeToString(h.Sum(nil))
}
func (c *clientCry) doTimeoutRequestCry(timer *time.Timer, req *http.Request) (*http.Response, error) {
type data struct {
resp *http.Response
err error
}
done := make(chan data, 1)
go func() {
resp, err := c.httpClient.Do(req)
done <- data{resp, err}
}()
select {
case r := <-done:
return r.resp, r.err
case <-timer.C:
return nil, errors.New("timeout on reading data from Bittrex API")
}
}
func (c *clientCry) doCry(method string, ressource string, payload string, authNeeded bool) (response []byte, err error) {
connectTimer := time.NewTimer(DEFAULT_HTTPCLIENT_TIMEOUT * time.Second)
var rawurl string
nonce := time.Now().UnixNano()
result := fmt.Sprintf("nonce=%d", nonce)
rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, result )
req, err := http.NewRequest(method, rawurl, strings.NewReader(payload))
sig := ComputeHmac512Hex(API_SECRET_CRY, result)
req.Header.Add("Sign", sig)
req.Header.Add("Key", API_KEY_CRY )
resp, err := c.doTimeoutRequestCry(connectTimer, req)
defer resp.Body.Close()
response, err = ioutil.ReadAll(resp.Body)
fmt.Println(fmt.Sprintf("reponse %s", response), err)
return response, err
}
func main() {
crypsy := NewCry(API_KEY_CRY, API_SECRET_CRY)
r, _ := crypsy.clientCry.doCry("GET", "info", "", true)
fmt.Println(r)
}
and my output is :
response {"success":false,"error":["Must be authenticated"]} <nil>
not getting why :( im passing the public key and the signature in the header, the signature.. i think im doing it right in the hmac-sha512.
I'm quering the user info url https://www.cryptsy.com/pages/apiv2/user, which as stated in the api site doesn't have any extra query variables so the nonce is the only one needed..
Have googled about restfull api's but haven't been able to find any answer :( starting to not let me sleep at night since i think that what im doing is kinda right.. really cant spot the error..
Anyone out there that could try and help me with this?
Thxs a lot :)
I see the issue with result := fmt.Sprintf("%d", nonce). The code that corresponds to the Python code should be something like
result := fmt.Sprintf("nonce=%d", nonce)
Could you please check it with this fix?
I also can observe a major difference in how the request is sending. The Python version is (link):
ret = requests.get(link,
params=query,
headers=headers,
verify=False)
but your code is does not send params with added nonce, etc. I think it should be something like
rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, queryStr)
where queryStr should contain nonce, etc.

Categories

Resources