web-dev-qa-db-fra.com

Python: ignore l'erreur 'padding incorrect' lors du décodage en base64

J'ai des données encodées en base64 que je veux reconvertir en binaire même s'il contient une erreur de remplissage. Si j'utilise 

base64.decodestring(b64_string)

il génère une erreur 'Incorrect padding'. Y a-t-il un autre moyen?

MISE À JOUR: Merci pour tous les commentaires. Pour être honnête, toutes les méthodes mentionnées semblaient un peu frapper La commande suivante a fonctionné un régal:

openssl enc -d -base64 -in b64string -out binary_data
75
FunLovinCoder

Comme indiqué dans d'autres réponses, il existe différentes manières de corrompre les données base64.

Cependant, comme Wikipedia dit, supprimer le remplissage (les caractères '=' à la fin des données codées en base64) est "sans perte":

D'un point de vue théorique, le caractère de remplissage n'est pas nécessaire, puisque le nombre d’octets manquants peut être calculé à partir du nombre de base64 chiffres.

Donc, si c’est vraiment la seule chose «fausse» avec vos données base64, le remplissage peut simplement être rajouté. Je suis arrivé à cela pour pouvoir analyser les URL "données" dans WeasyPrint, dont certaines étaient en base64 sans remplissage:

import base64
import re

def decode_base64(data, altchars=b'+/'):
    """Decode base64, padding being optional.

    :param data: Base64 data as an ASCII byte string
    :returns: The decoded byte string.

    """
    data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data)  # normalize
    missing_padding = len(data) % 4
    if missing_padding:
        data += b'='* (4 - missing_padding)
    return base64.b64decode(data, altchars)

Tests pour cette fonction: weasyprint/tests/test_css.py # L68

67
Simon Sapin

Ajoutez simplement un rembourrage si nécessaire. Suivez l'avertissement de Michael, cependant.

b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh
27
badp

S'il y a une erreur de remplissage, cela signifie probablement que votre chaîne est corrompue; Les chaînes codées en base64 doivent avoir un multiple de quatre. Vous pouvez essayer d'ajouter vous-même le caractère de remplissage (=) pour que la chaîne soit un multiple de quatre, mais vous devriez déjà l'avoir à moins que quelque chose ne va pas.

23
Michael Mrozek

"Rembourrage incorrect" peut signifier non seulement "remplissage manquant" mais aussi (croyez-le ou non) "remplissage incorrect".

Si les méthodes suggérées "d'ajout de remplissage" ne fonctionnent pas, essayez de supprimer certains octets de fin:

lens = len(strg)
lenx = lens - (lens % 4 if lens % 4 else 4)
try:
    result = base64.decodestring(strg[:lenx])
except etc

Mise à jour: il est préférable d’ajouter du remplissage à la fin ou de supprimer éventuellement des octets incorrects, APRÈS la suppression des espaces, sinon les calculs de longueur risquent d’être perturbés.

Ce serait une bonne idée si vous nous montriez un échantillon (court) des données que vous avez besoin de récupérer. Modifiez votre question et copiez/collez le résultat de print repr(sample).

Mise à jour 2: Il est possible que l'encodage ait été effectué de manière sécurisée. Si tel est le cas, vous pourrez voir des caractères moins et des traits de soulignement dans vos données et vous devriez pouvoir les décoder à l'aide de base64.b64decode(strg, '-_').

Si vous ne pouvez pas voir les caractères moins et souligné dans vos données, mais que vous pouvez voir les caractères plus et les barres obliques, vous avez un autre problème et vous devrez peut-être utiliser les astuces add-padding ou remove-cruft.

Si vous ne voyez rien de moins, de soulignement, de plus ou de barre oblique dans vos données, vous devez déterminer les deux caractères de remplacement; ils seront ceux qui ne sont pas dans [A-Za-z0-9]. Ensuite, vous devrez expérimenter pour voir dans quel ordre ils doivent être utilisés dans le 2ème argument de base64.b64decode()

Mise à jour 3 : Si vos données sont "confidentielles":
(a) vous devriez le dire dès le départ
(b) nous pouvons explorer d’autres pistes pour comprendre le problème, ce qui est très probablement lié aux caractères utilisés à la place de + et / dans l’alphabet de codage, ou par d’autres caractères de formatage ou étrangers.

Une de ces solutions consisterait à examiner quels caractères non "standard" figurent dans vos données, par exemple.

from collections import defaultdict
d = defaultdict(int)
import string
s = set(string.ascii_letters + string.digits)
for c in your_data:
   if c not in s:
      d[c] += 1
print d
21
John Machin

Utilisation 

string += '=' * (-len(string) % 4)  # restore stripped '='s

Le crédit va à un commentaire quelque part ici.

>>> import base64

>>> enc = base64.b64encode('1')

>>> enc
>>> 'MQ=='

>>> base64.b64decode(enc)
>>> '1'

>>> enc = enc.rstrip('=')

>>> enc
>>> 'MQ'

>>> base64.b64decode(enc)
...
TypeError: Incorrect padding

>>> base64.b64decode(enc + '=' * (-len(enc) % 4))
>>> '1'

>>> 
15
warvariuc

Je n'ai pas le représentant à commenter, mais une bonne chose à noter est que (au moins en Python 3.x) base64.b64decode tronquera tout remplissage supplémentaire à condition qu'il y en ait assez en premier lieu.

Donc, quelque chose comme: b'abc=' fonctionne aussi bien que b'abc=='.

Cela signifie que vous pouvez simplement ajouter le nombre maximal de caractères de remplissage dont vous auriez besoin - qui est de trois (b'===') - et que base64 tronquera tous les caractères inutiles.

Fondamentalement:

base64.b64decode(s + b'===')

est plus propre que

base64.b64decode(s + b'=' * (-len(s) % 4))
10
Henry Woody

Consultez la documentation de la source de données que vous essayez de décoder. Est-il possible que vous ayez voulu utiliser base64.urlsafe_b64decode(s) au lieu de base64.b64decode(s)? C'est l'une des raisons pour lesquelles vous avez peut-être vu ce message d'erreur.

Décoder la chaîne s en utilisant un alphabet sécurisé par URL, qui remplace - à la place de + et _ au lieu de/dans l'alphabet Base64 standard.

C'est par exemple le cas de diverses API Google, telles que les charges utiles Identity Toolkit et Gmail de Google.

4
Daniel F

Ajouter le rembourrage est plutôt ... fastidieux. Voici la fonction que j'ai écrite à l'aide des commentaires de ce fil ainsi que la page wiki pour base64 (c'est étonnamment utile) https://en.wikipedia.org/wiki/Base64#Padding .

import logging
import base64
def base64_decode(s):
    """Add missing padding to string and return the decoded base64 string."""
    log = logging.getLogger()
    s = str(s).strip()
    try:
        return base64.b64decode(s)
    except TypeError:
        padding = len(s) % 4
        if padding == 1:
            log.error("Invalid base64 string: {}".format(s))
            return ''
        Elif padding == 2:
            s += b'=='
        Elif padding == 3:
            s += b'='
        return base64.b64decode(s)
1
Bryan Lott

Il existe deux manières de corriger les données d'entrée décrites ici ou, plus spécifiquement et conformément à l'OP, de rendre la méthode b64decode du module Python en base64 capable de traiter les données d'entrée en quelque chose _ exception:

  1. Ajoutez == à la fin des données d'entrée et appelez base64.b64decode (...)
  2. Si cela soulève une exception, alors

    je. Catch it via try/except,

    ii. (R?) Enlevez tous les caractères = des données d'entrée (NB: cela peut ne pas être nécessaire),

    iii. Ajouter A == aux données d'entrée (A == via P == fonctionnera),

    iv. Appelez base64.b64decode (...) avec ceux A == - données d'entrée ajoutées

Le résultat de la rubrique 1. ou de la rubrique 2. ci-dessus donnera le résultat souhaité.

Mises en garde

Cela ne garantit pas que le résultat décodé correspondra à ce qui a été encodé à l'origine, mais cela donnera (parfois?) Assez de points de contrôle au PO pour qu'il fonctionne avec:

Même avec la corruption je veux revenir au binaire parce que je peux toujours obtenir des informations utiles du flux ASN.1 ").

Voir Ce que nous savons et Hypothèses ci-dessous.

TL; DR

De quelques tests rapides de base64.b64decode (...)

  1. il semble qu’il ignore les caractères non [A-Za-z0-9 + /]; qui inclut ignoring = s sauf si ils sont le dernier caractère d'un groupe de quatre analysés, auquel cas le = s met fin au décodage (a = b = c = d = donne le même résultat que abc = et a == b == c == donne le même résultat que ab ==).

  2. Il apparaît également que tous les caractères ajoutés sont ignorés après le point où la base64.b64decode (...) termine le décodage, par exemple. de an = en tant que quatrième d'un groupe.

Comme indiqué dans plusieurs commentaires ci-dessus, il existe soit zéro, soit un ou deux = s de remplissage requis à la fin des données d'entrée pour lorsque la valeur [nombre de caractères analysés jusqu'à ce point modulo 4] est 0 ou 3, ou 2, respectivement. Ainsi, à partir des éléments 3. et 4. ci-dessus, ajouter deux ou plus = s aux données d’entrée corrigera tout problème de [remplissage incorrect] dans ces cas.

CEPENDANT, le décodage ne peut pas gérer le cas où le [nombre total de caractères analysés modulo 4] est égal à 1, car il faut au moins deux caractères codés pour représenter le premier octet décodé dans un groupe de trois octets décodés. Dans les données d'entrée codées corrompues un, ce cas [N modulo 4] = 1 ne se produit jamais, mais comme l'OP a indiqué que des caractères peuvent être manquants, cela pourrait se produire ici. C’est pourquoi simplement ajouter = s ne fonctionnera pas toujours, et pourquoi ajouter A == ne fonctionnera pas alors que l’ajout de == ne le fera pas. N.B. Utiliser [A] est tout sauf arbitraire: cela n’ajoute que des bits effacés (zéro) au décodé, ce qui peut être correct ou non, mais l’objet ici n’est pas correct mais complet de base64.b64decode (...) sans exceptions.

Ce que nous savons du PO et en particulier des commentaires ultérieurs est

  • On soupçonne des données manquantes (caractères) dans les données d'entrée codées en Base64
  • Le codage Base64 utilise les 64 valeurs de position standard plus le remplissage: A-Z; a-z; 0-9; +; /; = est le remplissage. Ceci est confirmé, ou du moins suggéré, par le fait que openssl enc ... fonctionne.

Hypothèses

  • Les données d'entrée ne contiennent que des données ASCII 7 bits
  • Le seul type de corruption est l'absence de données d'entrée codées.
  • L'OP ne se soucie pas des données de sortie décodées à un moment quelconque après celui correspondant à des données d'entrée codées manquantes

Github

Voici un wrapper pour implémenter cette solution:

https://github.com/drbitboy/missing_b64

0
Brian Carcich

Ajoutez simplement des caractères supplémentaires tels que "=" ou n’importe quel autre caractère et faites-en un multiple de 4 avant de tenter de décoder la valeur de la chaîne cible. Quelque chose comme;

if len(value) % 4 != 0: #check if multiple of 4
    while len(value) % 4 != 0:
        value = value + "="
    req_str = base64.b64decode(value)
else:
    req_str = base64.b64decode(value)
0
Syed Mauze Rehan

Si cette erreur provient d’un serveur Web: essayez d’utiliser l’URL encodant votre valeur de publication. J'avais posté via "curl" et découvert que je ne codais pas ma valeur base64 en url. Des caractères tels que "+" n'étaient pas échappés. La logique de décodage d'URL du serveur Web exécutait automatiquement urod-décodage et était convertie en espaces.

"+" est un caractère base64 valide et peut-être le seul qui soit mutilé par un décodage d'URL inattendu.

0
Curtis Yallop

Dans mon cas, j'ai fait face à cette erreur lors de l'analyse d'un courrier électronique. J'ai obtenu la pièce jointe en tant que chaîne base64 et je l'extrais via re.search. Finalement, il y avait une étrange sous-chaîne supplémentaire à la fin.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

Quand j'ai effacé --_=ic0008m4wtZ4TqBFd+sXC8-- et enlevé la chaîne, l'analyse a été corrigée. 

Donc, mon conseil est de vous assurer que vous décodez une chaîne base64 correcte.

0
Daniil Mashkin

Tu devrais utiliser

base64.b64decode(b64_string, ' /')

Par défaut, les altchars sont '+/'.

0
Quoc