Python: Base64 kodunu çözerken 'Yanlış doldurma' hatasını yok say


111

İçinde doldurma hatası olsa bile tekrar ikiliye dönüştürmek istediğim, base64 kodlu bazı verilerim var. Kullanırsam

base64.decodestring(b64_string)

"Yanlış dolgu" hatası verir. Başka bir yolu var mı?

GÜNCELLEME: Tüm geri bildirimler için teşekkürler. Dürüst olmak gerekirse, bahsedilen tüm yöntemler kulağa biraz çarptı ve özledim, bu yüzden openssl'yi denemeye karar verdim. Aşağıdaki komut bir muamele yaptı:

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

5
Aslında mı YTL kullanarak base64.b64decode(strg, '-_')? Bu, herhangi bir örnek veri sağlamaya gerek kalmadan, probleminize en olası Python çözümüdür. Önerilen "yöntemler", DEBUG önerileridir, sağlanan bilginin yetersizliği göz önüne alındığında GEREKLİ "isabet ve ıskalama".
John Machin

2
@John Machin: Evet, yöntemini denedim ama işe yaramadı. Veriler şirket gizlidir.
FunLovinCoder 01

3
Deneyinbase64.urlsafe_b64decode(s)
Daniel F

Bunun çıktısını verir misiniz sorted(list(set(b64_string)))lütfen? Şirkete özgü herhangi bir şeyi ifşa etmeden, orijinal verileri kodlamak için hangi karakterlerin kullanıldığını ortaya çıkarması gerekir ve bu da isabet veya ıskalama olmayan bir çözüm sağlamak için yeterli bilgi sağlayabilir.
Brian Carcich

Evet, zaten çözüldüğünü biliyorum, ancak dürüst olmak gerekirse, openssl çözümü de bana vur ya da özledim gibi geliyor.
Brian Carcich

Yanıtlar:


79

Diğer yanıtlarda belirtildiği gibi, base64 verilerinin bozulabileceği çeşitli yollar vardır.

Bununla birlikte, Wikipedia'nın dediği gibi , dolgunun kaldırılması (base64 kodlu verilerin sonundaki '=' karakterleri) "kayıpsız" dır:

Teorik bir bakış açısından, eksik baytların sayısı Base64 basamaklarının sayısından hesaplanabildiğinden, doldurma karakterine gerek yoktur.

Yani bu, base64 verilerinizde gerçekten "yanlış" olan tek şeyse, dolgu sadece geri eklenebilir. WeasyPrint'te bazıları dolgu olmadan base64 olan "veri" URL'lerini ayrıştırabilmek için bunu buldum:

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)

Bu işlev için testler : weasyprint / testing / test_css.py # L68


2
Not: ASCII Unicode değil, bu yüzden güvende olmak isteyebilirsinizstr(data)
MarkHu

4
Bu tek bir uyarı ile iyidir. base64.decodestring kullanımdan kaldırıldı, base64.b64_decode kullanın
ariddell

2
@Ariddell hakkında açıklama yapmak base64.decodestringiçin yorum Py3'te kullanımdan kaldırıldı, base64.decodebytesancak sürüm uyumluluğunun kullanılması daha iyi base64.b64decode.
Cas

base64Modül, girişteki geçersiz base64 olmayan karakterleri göz ardı ettiğinden, önce verileri normalleştirmeniz gerekir. Harf, rakam /veya olmayan her şeyi kaldırın +ve ardından dolguyu ekleyin.
Martijn Pieters

39

Gerektiği gibi dolgu eklemeniz yeterlidir. Ancak Michael'ın uyarısına dikkat edin.

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

1
Kesinlikle 0'a 0'a, 2'ye 1'e ve 1'e 2'yi eşleyen daha basit bir şey var.
badp

2
Neden 4 yerine 3'ün katına genişliyorsunuz?
Michael Mrozek

Base64'teki Wikipedia makalesinin ima ettiği şey bu.
badp

1
@bp: Base64 kodlamada her 24 bitlik (3 bayt) ikili giriş 4 bayt çıktı olarak kodlanır. output_len% 3 mantıklı değil.
John Machin

8
Sadece eklemek ===her zaman işe yarar . Herhangi bir ekstra =karakter Python tarafından görünüşte güvenli bir şekilde atılır.
Acumenus

32

Görünüşe göre kod çözmeden önce baytlarınıza dolgu eklemeniz gerekiyor. Bu sorunun başka birçok cevabı var, ancak belirtmek isterim ki (en azından Python 3.x'te) base64.b64decode, eğer ilk etapta yeterli olması koşuluyla, fazladan dolguyu kesecektir.

Yani, şöyle bir şey: b'abc='aynı b'abc=='(olduğu gibi b'abc=====') iyi çalışır .

Bunun anlamı, ihtiyaç duyabileceğiniz maksimum dolgu karakteri sayısını ekleyebileceğinizdir - bu da üçtür (b'===' )) ve base64, gereksiz olanları kesecektir.

Bu yazmanıza izin verir:

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

hangisi daha basittir:

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

1
Tamam, bu çok "çirkin" değil teşekkürler :) Bu arada sanırım asla 2'den fazla dolgu karakterine ihtiyacın yok. Base64 algoritması, bir seferde 3 karakterlik gruplar üzerinde çalışır ve yalnızca son karakter grubunuz yalnızca 1 veya 2 karakter uzunluğunda olduğunda doldurmaya ihtiyaç duyar.
Otto

@Otto buradaki dolgu, 4 karakterlik gruplar üzerinde çalışan kod çözme içindir. Base64 kodlaması 3 karakterlik gruplar üzerinde çalışır :)
Henry Woody

ancak kodlama sırasında en fazla 2'nin ekleneceğini bilirseniz, bu daha sonra "kaybolabilir" ve sizi kod çözmeden önce yeniden eklemeye zorlarsa, o zaman kod çözme sırasında da en fazla 2 eklemeniz gerektiğini bilirsiniz. #ChristmasTimeArgumentForTheFunOfIt
Otto

@Otto haklı olduğuna inanıyorum. Örneğin 5 uzunluğuna sahip bir base64 kodlu dize 3 dolgu karakteri gerektirse de, 5 uzunluğundaki bir dizi, base64 kodlu bir dizi için geçerli bir uzunluk bile değildir. Şu hatayı alırsınız:binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4 . Bunu işaret ettiğiniz için teşekkürler!
Henry Woody

24

"Yanlış dolgu" yalnızca "eksik dolgu" anlamına gelmez, aynı zamanda (ister inanın ister inanmayın) "yanlış dolgu" anlamına gelebilir.

Önerilen "dolgu ekleme" yöntemleri işe yaramazsa, sondaki bazı baytları kaldırmayı deneyin:

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

Güncelleme: Dolgu ekleme veya olası kötü baytları sondan kaldırma ile ilgili herhangi bir uğraş, herhangi bir boşluk kaldırıldıktan SONRA yapılmalıdır, aksi takdirde uzunluk hesaplamaları altüst olur.

Kurtarmanız gereken verilerin (kısa) bir örneğini bize göstermeniz iyi bir fikir olacaktır. Sorunuzu düzenleyin ve sonucunu kopyalayın / yapıştırın print repr(sample) .

Güncelleme 2: Kodlamanın url güvenli bir şekilde yapılmış olması mümkündür. Böyle bir durumda, verilerinizde eksi ve alt çizgi karakterleri görebileceksiniz ve bunu kullanarak kod çözebilmelisiniz.base64.b64decode(strg, '-_')

Verilerinizde eksi ve alt çizgi karakterleri göremiyorsanız, ancak artı ve eğik çizgi karakterlerini görebiliyorsanız, o zaman başka bir sorununuz var ve eklenti dolgusu veya kaldırma hilelerine ihtiyacınız olabilir.

Verilerinizde eksi, alt çizgi, artı ve eğik çizgi görmüyorsanız, iki alternatif karakteri belirlemeniz gerekir; [A-Za-z0-9] içinde olmayanlar onlar olacak. Ardından, ikinci bağımsız değişkeninde hangi sıranın kullanılması gerektiğini görmek için denemeniz gerekir.base64.b64decode()

Güncelleme 3 : Verileriniz "şirket gizliyse":
(a) bunu önceden söylemelisiniz
(b) sorunu anlamanın başka yollarını keşfedebiliriz ki bu, büyük olasılıkla hangi karakterlerin yerine +ve hangi karakterlerin kullanıldığıyla ilgilidir /. kodlama alfabesi veya diğer biçimlendirme veya yabancı karakterlerle.

Böyle bir yol, verilerinizde hangi "standart" olmayan karakterlerin bulunduğunu incelemek olabilir, örneğin

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

Veriler, standart base64 karakter kümesinden oluşur. Sorunun 1 veya daha fazla karakterin eksik olmasından kaynaklandığından oldukça eminim - dolayısıyla doldurma hatası. Python'da sağlam bir çözüm olmadıkça, openssl'yi çağırma çözümüme gideceğim.
FunLovinCoder

1
Hataları sessizce görmezden gelen bir "çözüm", "sağlam" terimini neredeyse hiç hak etmiyor. Daha önce bahsettiğim gibi, çeşitli Python önerileri, sorunun ne olduğunu bulmak için HATA AYIKLAMA yöntemleriydi, İLKELEN bir çözüme hazırlık ... böyle bir şeyle ilgilenmiyor musun?
John Machin

7
Benim ihtiyacım, base64'ün neden bozuk olduğu sorununu çözmemektir - bu, üzerinde hiçbir kontrolüm olmayan bir kaynaktan geliyor. Benim ihtiyacım, alınan veriler hakkında bozuk olsa bile bilgi sağlamaktır. Bunu yapmanın bir yolu, ikili verileri bozuk base64'ten almaktır, böylece temeldeki ASN.1'den bilgi toplayabilirim. Akış. Orijinal soruyu sordum çünkü bu soruya başka bir sorunun cevabını istemedim - bozuk base64 nasıl hata ayıklanır gibi.
FunLovinCoder

Sadece dizeyi normalleştirin , Base64 karakteri olmayan her şeyi kaldırın. Her yerde, sadece başlangıç ​​veya bitiş değil.
Martijn Pieters

24

Kullanım

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

Kredi burada bir yerde bir yoruma gider.

>>> 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'

>>> 

4
Şu yorumu
kastediyor

22

Bir dolgu hatası varsa, muhtemelen dizinizin bozuk olduğu anlamına gelir; base64 olarak kodlanmış dizelerin dört katı uzunluğa sahip olmalıdır. Dizeyi =dörtten kat yapmak için dolgu karakterini ( ) kendiniz eklemeyi deneyebilirsiniz , ancak bir sorun yoksa zaten buna sahip olmalıdır.


Temel ikili veri ASN.1'dir. Bozulmayla bile ikili programa geri dönmek istiyorum çünkü ASN.1 akışından hala bazı yararlı bilgiler alabiliyorum.
FunLovinCoder

doğru değil, güvenlik kontrolleri için bir jwt kodunu çözmek istiyorsanız buna ihtiyacınız olacak
DAG

4

Kodunu çözmeye çalıştığınız veri kaynağının belgelerine bakın. base64.urlsafe_b64decode(s)Bunun yerine kullanmak istemeniz mümkün mü base64.b64decode(s)? Bu hata mesajını görmüş olmanızın bir nedeni budur.

Dizelerin kodunu, standart Base64 alfabesinde / yerine + ve _ yerine - yerine geçen URL güvenli bir alfabe kullanarak çözün.

Bu, örneğin Google'ın Identity Toolkit ve Gmail yükleri gibi çeşitli Google API'leri için geçerlidir.


1
Bu soruya hiç cevap vermiyor. Ayrıca, urlsafe_b64decodedolgu gerektirir.
rdb

Peki, bu soruyu cevaplamadan önce Google'ın Kimlik Araç Seti ile ilgili olan bir sorun vardı. Yanlış doldurma hatası alıyordum (sunucuda olduğuna inanıyorum), dolgu doğru görünse bile. Kullanmam gerektiği ortaya çıktı base64.urlsafe_b64decode.
Daniel F

Soruyu cevaplamadığına katılıyorum, rdb, ancak tam olarak duymam gereken şey buydu. Cevabı biraz daha hoş bir üslupla yeniden ifade ettim, umarım bu senin için işe yarar Daniel.
Henrik Heimbuerger

Mükemmel derecede iyi. Kulağa biraz kaba geldiğini fark etmedim, yalnızca sorunu çözerse en hızlı düzeltme olacağını düşündüm ve bu nedenle denenecek ilk şey olmalı. Değişikliğiniz için teşekkürler, hoş geldiniz.
Daniel F

Bu yanıt, bir JWT'den türetilen bir Google Erişim Jetonunu çözme sorunumu çözdü. Diğer tüm girişimler "Yanlış doldurma" ile sonuçlandı.
John Hanley

2

Dolgu eklemek oldukça ... zordur. İşte bu başlıktaki yorumların yardımıyla yazdığım işlev ve base64 için wiki sayfası (şaşırtıcı derecede yararlı) 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)

2

base64.urlsafe_b64decode(data)Bir web görüntüsünün kodunu çözmeye çalışıyorsanız, kullanabilirsiniz . Otomatik olarak dolguyla ilgilenecektir.


gerçekten yardımcı oluyor!
Ay

1

Burada açıklanan giriş verilerini düzeltmenin veya daha spesifik olarak ve OP ile uyumlu olarak, Python modülü base64'ün b64decode yönteminin, yakalanmamış bir istisna oluşturmadan girdi verilerini bir şeye işleyebilmesini sağlamak için iki yol vardır :

  1. Giriş verilerinin sonuna == ekleyin ve base64.b64decode (...) öğesini çağırın
  2. Bu bir istisna oluşturuyorsa, o zaman

    ben. Try / exc ile yakalayın,

    ii. (R?) Giriş verilerinden herhangi bir = karakter çıkar (NB, bu gerekli olmayabilir),

    iii. Giriş verilerine A == ekleyin (A == ile P == arası çalışacaktır),

    iv. Base64.b64decode (...) bu A == - eklenen giriş verileriyle çağırın

Yukarıdaki 1. veya 2. Öğeden elde edilen sonuç, istenen sonucu verecektir.

Uyarılar

Bu, kodu çözülen sonucun orijinal olarak kodlanmış olacağını garanti etmez, ancak (bazen?) OP'ye aşağıdakilerle çalışması için yeterince verir:

Bozulmayla bile ikili programa geri dönmek istiyorum çünkü ASN.1 akışından hala bazı yararlı bilgiler alabiliyorum ").

Bkz Bildiklerimiz ve Varsayımlar aşağıda.

TL; DR

Base64.b64decode'un bazı hızlı testlerinden (...)

  1. [A-Za-z0-9 + /] olmayan karakterleri yok sayıyor gibi görünüyor; bu, ayrıştırılmış dörtlü bir gruptaki son karakter (ler) olmadıkça = s'yi yok saymayı içerir; bu durumda = s, kod çözmeyi sonlandırır (a = b = c = d = abc = ile aynı sonucu verir ve a = = b == c ==, ab ==) ile aynı sonucu verir.

  2. Ayrıca , base64.b64decode (...) kodunun çözülmeyi sona erdirdiği noktadan sonra eklenen tüm karakterlerin göz ardı edildiği görülmektedir, örneğin bir grupta dördüncü olarak an = 'den.

Yukarıdaki birkaç yorumda belirtildiği gibi, giriş verilerinin sonunda [o noktaya kadar ayrıştırılan karakter sayısı modulo 4] değeri 0 veya 3 olduğunda gerekli olan sıfır veya bir veya iki = s doldurma vardır, veya sırasıyla 2. Dolayısıyla, yukarıdaki 3. ve 4. maddelerden, giriş verilerine iki veya daha fazla = s eklemek, bu durumlarda [Yanlış doldurma] sorunlarını düzeltecektir.

ANCAK, kod çözme [ayrıştırılan karakterlerin toplam sayısı modülo 4] 1 olduğu durumu idare edemez, çünkü kodu çözülmüş üç bayttan oluşan bir grupta ilk kodu çözülmüş baytı temsil etmek için en az iki kodlanmış karakter gerekir. In un şifreli giriş verileri bozuk, bu = 1 vaka olur hiç [N 4 modulo], ama karakterler eksik olabilir belirtti OP olarak, burada olabilirdi. Bu yüzden = s eklemek her zaman işe yaramayacaktır ve == eklemek işe yaramazken A == eklemek işe yarayacaktır. NB [A] kullanmak keyfi olmaktan başka her şeydir: kodu çözülenlere yalnızca temizlenmiş (sıfır) bitler ekler, bu doğru olabilir veya olmayabilir, ancak o zaman buradaki nesne doğruluk değil, base64.b64decode (...) ile tamamlama istisnaları sans .

OP'den bildiklerimiz ve özellikle sonraki yorumlar

  • Base64 ile kodlanmış giriş verilerinde eksik veri (karakterler) olduğundan şüpheleniliyor
  • Base64 kodlaması, standart 64 basamak değeri artı dolguyu kullanır: AZ; az; 0-9; +; /; = doldurmadır. Bu, openssl enc ...işe yaramasıyla doğrulanır veya en azından önerilir .

Varsayımlar

  • Giriş verileri yalnızca 7 bitlik ASCII verilerini içerir
  • Tek yolsuzluk türü eksik kodlanmış giriş verileridir
  • OP, herhangi bir eksik kodlanmış giriş verisine karşılık geldikten sonraki herhangi bir noktada kodu çözülmüş çıktı verilerini umursamaz.

GitHub

İşte bu çözümü uygulamak için bir sarmalayıcı:

https://github.com/drbitboy/missing_b64


1

Yanlış doldurma hatası, bazen kodlanan dizide meta verilerin de mevcut olmasından kaynaklanır Eğer dizeniz 'data: image / png; base64, ... base 64 stuff ....' gibi görünüyorsa, ilkini kaldırmanız gerekir. kodunu çözmeden önce bölüm.

Diyelim ki resim base64 kodlu dizeniz varsa, aşağıdaki snippet'i deneyin ..

from PIL import Image
from io import BytesIO
from base64 import b64decode
imagestr = 'data:image/png;base64,...base 64 stuff....'
im = Image.open(BytesIO(b64decode(imagestr.split(',')[1])))
im.save("image.png")

0

Hedef dize değerinin kodunu çözmeyi denemeden önce, "=" gibi ek karakterler ekleyin ve 4'ün katı yapın. Gibi bir şey;

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

Bu hatanın bir web sunucusundan gelmesi durumunda: Posta değerinizi URL kodlamayı deneyin. "Curl" aracılığıyla POST yapıyordum ve base64 değerimi url kodlamadığımı keşfettim, bu nedenle "+" gibi karakterler kaçmadı, bu nedenle web sunucusu url-kod çözme mantığı otomatik olarak url-decode çalıştırdı ve + boşluklara dönüştürüldü.

"+" geçerli bir base64 karakteridir ve belki de beklenmedik bir url kod çözme tarafından karıştırılan tek karakterdir.


0

Benim durumumda bir e-postayı ayrıştırırken bu hatayla karşılaştım. Eki base64 string olarak aldım ve re.search ile çıkardım. Sonunda, sonunda garip bir alt dize vardı.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

Sildiğimde --_=ic0008m4wtZ4TqBFd+sXC8-- ve soyduğumda ayrıştırma düzeltildi.

Bu yüzden tavsiyem, doğru bir base64 dizesinin kodunu çözdüğünüzden emin olmanızdır.


0

Kullanmalısın

base64.b64decode(b64_string, ' /')

Varsayılan olarak, sunaklar '+/'.


1
Bu python 3.7'de çalışmaz. assert len ​​(altchars) == 2, repr (altchars)
Dat TT

0

Ben de bu problemle karşılaştım ve hiçbir şey işe yaramadı. Sonunda benim için işe yarayan çözümü bulmayı başardım. Base64'te içeriği sıkıştırdım ve bu bir milyon kayıttan 1'ine oldu ...

Bu, Simon Sapin'in önerdiği çözümün bir versiyonudur.

Dolgunun eksik olması durumunda son 3 karakteri kaldırırım.

"0gA1RD5L / 9AUGtH9MzAwAAA ==" yerine

"0gA1RD5L / 9AUGtH9MzAwAA" alıyoruz

        missing_padding = len(data) % 4
        if missing_padding == 3:
            data = data[0:-3]
        elif missing_padding != 0:
            print ("Missing padding : " + str(missing_padding))
            data += '=' * (4 - missing_padding)
        data_decoded = base64.b64decode(data)   

Bu cevaba göre Sondaki gibi base64'te sebep null'dur. Ama hala kodlayıcının bunu neden bozduğu hakkında hiçbir fikrim yok ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.