2010-07-21 18 views
16

Pour Facebook FBML Apps Facebook envoie dans un paramètre signed_request expliqué ici:Comment décoder l'URL de base64 en python?

http://developers.facebook.com/docs/authentication/canvas

Ils ont donné la version php de décoder cette signature demande:

http://pastie.org/1054154

Comment faire la même en python?

J'ai essayé module base64 mais je reçois une erreur de remplissage incorrect:

>>> base64.urlsafe_b64decode("eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk3NDYwMDAsIm9hdXRoX3Rva2VuIjoiMjk1NjY2Njk1MDY0fDIuRXpwem5IRVhZWkJVZmhGQ2l4ZzYzUV9fLjM2MDAuMTI3OTc0NjAwMC0xMDAwMDA0ODMyNzI5MjN8LXJ6U1pnRVBJTktaYnJnX1VNUUNhRzlNdEY4LiIsInVzZXJfaWQiOiIxMDAwMDA0ODMyNzI5MjMifQ") 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/base64.py", line 112, in urlsafe_b64decode 
    return b64decode(s, '-_') 
    File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/base64.py", line 76, in b64decode 
    raise TypeError(msg) 
TypeError: Incorrect padding 
+0

merci j'essayées base64, mais je reçois cette erreur: http://pastie.org/1054201 – kevin

+0

S'il vous plaît publier en fait le plus petit code qui montre l'erreur et l'erreur réelle. La plupart d'entre nous n'ont pas les patiences pour suivre les liens partout. –

+0

code et erreur ajouté. – kevin

Répondre

20

Apparemment, vous avez manqué les deux derniers caractères lors de la copie la chaîne codée en base64 d'origine. Suffixe la chaîne d'entrée avec deux signes est-égal (=) et il sera décodé correctement.

+1

Geert, merci pour cela. mais c'est exactement le code que j'ai eu de facebook et il n'a pas eu = à la fin. est-ce prévu? – kevin

+2

Ce n'est pas à prévoir je dirais. Cependant, vous pouvez vérifier la longueur de l'entrée base64 en vérifiant sa longueur: la longueur doit toujours être un multiple de 4 octets (c'est en fait la raison pour laquelle le décodeur a jeté une erreur). Si ce n'est pas le cas, vous pouvez ajouter des signes is-equal jusqu'à ce que ce soit le cas, puis la chaîne sera décodée correctement. – Geert

+2

Semble '=' padding n'est pas toujours requis dans toutes les variantes: http://en.wikipedia.org/wiki/Base64 –

22

J'ai partagé un extrait de code pour analyser signed_request paramètre dans une application toile facebook base python à http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas:

import base64 
import hashlib 
import hmac 
import simplejson as json 

def base64_url_decode(inp): 
    padding_factor = (4 - len(inp) % 4) % 4 
    inp += "="*padding_factor 
    return base64.b64decode(unicode(inp).translate(dict(zip(map(ord, u'-_'), u'+/')))) 

def parse_signed_request(signed_request, secret): 

    l = signed_request.split('.', 2) 
    encoded_sig = l[0] 
    payload = l[1] 

    sig = base64_url_decode(encoded_sig) 
    data = json.loads(base64_url_decode(payload)) 

    if data.get('algorithm').upper() != 'HMAC-SHA256': 
     log.error('Unknown algorithm') 
     return None 
    else: 
     expected_sig = hmac.new(secret, msg=payload, digestmod=hashlib.sha256).digest() 

    if sig != expected_sig: 
     return None 
    else: 
     log.debug('valid signed request received..') 
return data 
+0

fonctionne bien, une belle! – Anentropic

+0

La solution de dae.eklen fait de même et est plus élégante. ('base64.urlsafe_b64decode (s + '=' * (4 - len (s)% 4))') – sax

+1

Merci. C'est un extrait de code assez court - ce serait génial de le voir inclus dans cette réponse. – dgel

1
import base64 
import simplejson as json 

def parse_signed_request(signed_request): 
    encoded_sig, payload = signed_request.split('.',2) 
    data = json.loads(base64.b64decode(payload.replace('-_', '+/'))) 
    return data 
20

essayer

s = 'iEPX-SQWIR3p67lj_0zigSWTKHg' 
base64.urlsafe_b64decode(s + '=' * (4 - len(s) % 4)) 

comme il est écrit here

+2

Assurez-vous que la chaîne avec laquelle vous travaillez est une instance de str-unicode qui échouerait avec une erreur. Si c'est votre cas, utilisez la fonction 'str (s)' pour la conversion. – sax

5

Alternative à @dae La solution de .eklen, vous pouvez ajouter === à elle:

s = 'iEPX-SQWIR3p67lj_0zigSWTKHg' 
base64.urlsafe_b64decode(s + '===') 

Cela fonctionne parce que Python ne se plaint que de manquer de rembourrage, mais pas un rembourrage supplémentaire.

+0

Ok, cela a du sens et fonctionne avec votre exemple. Je suis confus cependant, j'ai une chaîne d'un multiple de longueur de 4 sans padding renvoyant l'erreur de remplissage incorrecte, quand j'avais un '=', j'ai toujours le même problème, mais si j'avais au moins '==' Ça marche. Qu'est-ce qui se passe avec ça? – guival

1

Surprenant, mais la réponse actuellement acceptée n'est pas exactement correcte. Comme d'autres réponses, il s'agit de l'encodage base64url, qui fait partie de RFC7515. Fondamentalement, ils ont respectivement remplacé les caractères '+' et '/' par '-' et '_'; En outre, vous avez supprimé tous les caractères "=" de fin, car vous pouvez toujours indiquer le nombre de caractères manquants, en examinant simplement la longueur de la chaîne codée.

est ici exemple illustratif de RFC7515:

static string base64urlencode(byte [] arg) 
{ 
    string s = Convert.ToBase64String(arg); // Regular base64 encoder 
    s = s.Split('=')[0]; // Remove any trailing '='s 
    s = s.Replace('+', '-'); // 62nd char of encoding 
    s = s.Replace('/', '_'); // 63rd char of encoding 
    return s; 
} 

static byte [] base64urldecode(string arg) 
{ 
    string s = arg; 
    s = s.Replace('-', '+'); // 62nd char of encoding 
    s = s.Replace('_', '/'); // 63rd char of encoding 
    switch (s.Length % 4) // Pad with trailing '='s 
    { 
    case 0: break; // No pad chars in this case 
    case 2: s += "=="; break; // Two pad chars 
    case 3: s += "="; break; // One pad char 
    default: throw new System.Exception(
     "Illegal base64url string!"); 
    } 
    return Convert.FromBase64String(s); // Standard base64 decoder 
}