Cara memeriksa string yang dikodekan Base64 yang valid


127

Apakah ada cara di C # untuk melihat apakah sebuah string dikodekan Base 64 selain hanya mencoba mengubahnya dan melihat apakah ada kesalahan? Saya memiliki kode kode seperti ini:

// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);

Saya ingin menghindari pengecualian "Karakter tidak valid dalam string Base-64" yang terjadi jika nilainya bukan string base 64 yang valid. Saya hanya ingin memeriksa dan mengembalikan false daripada menangani pengecualian karena saya berharap terkadang nilai ini tidak akan menjadi string basis 64. Apakah ada cara untuk memeriksa sebelum menggunakan fungsi Convert.FromBase64String?

Terima kasih!

Pembaruan:
Terima kasih atas semua jawaban Anda. Berikut adalah metode ekstensi yang dapat Anda gunakan sejauh ini untuk memastikan string Anda akan melewati Convert.FromBase64String tanpa pengecualian. .NET tampaknya mengabaikan semua spasi tambahan dan akhir saat mengonversi ke basis 64 sehingga "1234" valid dan begitu juga "1234"

public static bool IsBase64String(this string s)
{
    s = s.Trim();
    return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

}

Bagi mereka yang bertanya-tanya tentang kinerja pengujian vs penangkapan dan pengecualian, dalam banyak kasus untuk 64 basis ini lebih cepat untuk memeriksa daripada menangkap pengecualian sampai Anda mencapai panjang tertentu. Semakin kecil panjangnya, semakin cepat

Dalam pengujian saya yang sangat tidak ilmiah: Untuk 10.000 iterasi untuk panjang karakter 100.000 - 110000, itu 2,7 kali lebih cepat untuk diuji terlebih dahulu.

Untuk 1000 iterasi untuk panjang karakter 1 - 16 karakter dengan total 16.000 tes, itu 10,9 kali lebih cepat.

Saya yakin ada titik di mana akan lebih baik untuk menguji dengan metode berbasis pengecualian. Saya hanya tidak tahu pada poin apa itu.


1
Ini tergantung pada seberapa "menyeluruh" Anda menginginkan cek tersebut. Anda dapat menggunakan beberapa pra-validasi menggunakan regex seperti yang telah dijawab orang lain, tetapi itu bukan satu-satunya indikator. encoding base64 membutuhkan padding dalam beberapa kasus menggunakan =tanda. Jika padding salah, akan memberikan error meskipun inputnya cocok dengan ekspresi.
vcsjones

1
Kondisi Anda tidak secara eksklusif memenuhi string base64. Pertimbangkan stringnya \n\fLE16- metode Anda akan menghasilkan positif palsu untuk ini. Untuk siapa saja yang membaca dan mencari metode yang sangat mudah; Saya akan merekomendasikan untuk menangkap FormatException atau menggunakan RegEx yang sesuai dengan spesifikasi, lihat stackoverflow.com/questions/475074/… .
batal

jika metode di atas mengembalikan nilai false, bagaimana cara memasukkan string ke panjang yang benar?
Paul Alexander

3
Saya percaya bahwa RegEx harus@"^[a-zA-Z0-9\+/]*={0,2}$"
azatar

Solusi ini tidak dapat diandalkan. Gagal jika Anda menambahkan 4 karakter string yang sama.
Bettimms

Jawaban:


49

Sangat mudah untuk mengenali string Base64, karena hanya akan terdiri dari karakter 'A'..'Z', 'a'..'z', '0'..'9', '+', '/'dan sering kali diisi di akhir hingga tiga '=', untuk membuat panjangnya kelipatan 4. Tetapi alih-alih membandingkan ini, Anda ' d lebih baik mengabaikan pengecualian, jika itu terjadi.


1
Saya pikir Anda berada di jalur yang benar. Saya melakukan beberapa pengujian dan tampaknya itu adalah kelipatan 4, bukan 3.
Chris Mullins

1
Panjangnya harus kelipatan 3, pada saat encoding, agar encoding berhasil! Maaf tentang itu ... dan ya, Anda benar ... String yang disandikan memiliki panjang yang merupakan kelipatan 4. Itu sebabnya kami akan memasukkan hingga 3 '='.
Anirudh Ramanathan

4
Ditandai Benar karena Anda yang pertama menyebutkan beberapa hal. Saya memperbarui pertanyaan saya dengan implementasi solusi, beri tahu saya jika Anda melihat ada masalah dengannya.
Chris Mullins

48

Gunakan Convert.TryFromBase64String dari C # 7.2

public static bool IsBase64String(string base64)
{
   Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
   return Convert.TryFromBase64String(base64, buffer , out int bytesParsed);
}

1
Saya tidak tahu bahwa itu adalah sesuatu. Saya pikir ini harus menjadi jawaban baru, jika menggunakan c # 7.2
Chris Mullins

4
Hanya bekerja di .NET Core 2.1+ atau .NET Standard 2.1+
Cyrus

C # adalah kompiler dan TryFromBase64String adalah API dari .NET framework :)
user960567

Ini akan kembali palsu untuk string non-empuk, di sini adalah memperbaiki: Convert.TryFromBase64String(base64.PadRight(base64.Length / 4 * 4 + (base64.Length % 4 == 0 ? 0 : 4), '='), new Span<byte>(new byte[base64.Length]), out _). Terima kasih.
rvnlord

44

Saya tahu Anda mengatakan Anda tidak ingin menangkap pengecualian. Tetapi, karena menangkap pengecualian lebih dapat diandalkan, saya akan melanjutkan dan memposting jawaban ini.

public static bool IsBase64(this string base64String) {
     // Credit: oybek https://stackoverflow.com/users/794764/oybek
     if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
        || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
        return false;

     try{
         Convert.FromBase64String(base64String);
         return true;
     }
     catch(Exception exception){
     // Handle the exception
     }
     return false;
}

Pembaruan: Saya telah memperbarui kondisi berkat oybek untuk lebih meningkatkan keandalan.


1
memanggil base64String.Containsbeberapa kali dapat mengakibatkan kinerja yang buruk base64Stringdalam hal string yang besar.
NucS

@NucS Anda benar, kita dapat menggunakan regex terkompilasi di sini.
harsimranb

1
Anda dapat memeriksanya base64String== null || base64String.Length == 0denganstring.IsNullOrEmpty(base64String)
Daniël Tulp

Perhatikan bahwa Base64 dapat berisi spasi (misalnya jeda baris) tanpa masalah. Mereka diabaikan oleh parser.
Timotius

2
Karena kita memiliki akses ke kode sumber .NET sekarang kita dapat melihat fungsi FromBase64String () melakukan semua pemeriksaan ini. Referenceource.microsoft.com/#mscorlib/system/… Jika itu adalah string base64 yang valid, maka Anda memeriksanya dua kali. Mungkin lebih baik untuk mencoba / menangkap pengecualian.
iheartcsharp

16

Saya yakin regex seharusnya:

    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")

Hanya mencocokkan satu atau dua tanda '=', bukan tiga.

sharus menjadi string yang akan diperiksa. Regexadalah bagian dari System.Text.RegularExpressionsnamespace.


2
tidak memeriksa apakah panjang string adalah mod dari 4 = 0
calingasan

7

Mengapa tidak menangkap pengecualian, dan mengembalikan False?

Ini untuk menghindari biaya tambahan dalam kasus umum.


1
Ini adalah kasus yang tidak biasa, saya kira di mana saya akan menggunakan nilainya lebih cenderung bukan basis 64 jadi saya lebih suka menghindari overhead dari pengecualian. Jauh lebih cepat untuk memeriksa sebelumnya. Saya mencoba mengubah sistem lama yang saya warisi dari kata sandi teks biasa menjadi nilai hash.
Chris Mullins

2
Ekspresi reguler tidak pernah lebih cepat dari yang disarankan Tyler.
Vincent Koeman

Lihat komentar di bagian bawah posting saya. Saya pikir tergantung pada panjang string yang Anda kerjakan, bisa lebih cepat untuk menguji terlebih dahulu, terutama untuk string kecil seperti kata sandi yang di-hash. String tersebut harus kelipatan 4 untuk bisa mencapai regex, dan regex pada string kecil lebih cepat daripada string yang sangat besar.
Chris Mullins

2
Di dunia yang sempurna, seseorang tidak boleh menulis kode yang logika bisnisnya dirancang atau diketahui memiliki pengecualian. Blok percobaan / tangkap pengecualian terlalu mahal untuk digunakan sebagai blok keputusan.
Ismail Hawayel

7

Hanya demi kelengkapan saya ingin memberikan beberapa implementasi. Secara umum, Regex adalah pendekatan yang mahal, terutama jika stringnya besar (yang terjadi saat mentransfer file besar). Pendekatan berikut mencoba cara deteksi tercepat terlebih dahulu.

public static class HelperExtensions {
    // Characters that are used in base64 strings.
    private static Char[] Base64Chars = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    /// <summary>
    /// Extension method to test whether the value is a base64 string
    /// </summary>
    /// <param name="value">Value to test</param>
    /// <returns>Boolean value, true if the string is base64, otherwise false</returns>
    public static Boolean IsBase64String(this String value) {

        // The quickest test. If the value is null or is equal to 0 it is not base64
        // Base64 string's length is always divisible by four, i.e. 8, 16, 20 etc. 
        // If it is not you can return false. Quite effective
        // Further, if it meets the above criterias, then test for spaces.
        // If it contains spaces, it is not base64
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;

        // 98% of all non base64 values are invalidated by this time.
        var index = value.Length - 1;

        // if there is padding step back
        if (value[index] == '=')
            index--;

        // if there are two padding chars step back a second time
        if (value[index] == '=')
            index--;

        // Now traverse over characters
        // You should note that I'm not creating any copy of the existing strings, 
        // assuming that they may be quite large
        for (var i = 0; i <= index; i++) 
            // If any of the character is not from the allowed list
            if (!Base64Chars.Contains(value[i]))
                // return false
                return false;

        // If we got here, then the value is a valid base64 string
        return true;
    }
}

EDIT

Seperti yang disarankan oleh Sam , Anda juga dapat sedikit mengubah kode sumbernya. Dia memberikan pendekatan yang berkinerja lebih baik untuk langkah terakhir pengujian. Rutinitas

    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;

        // 1 - 9
        if (intValue >= 48 && intValue <= 57) 
            return false;

        // A - Z
        if (intValue >= 65 && intValue <= 90) 
            return false;

        // a - z
        if (intValue >= 97 && intValue <= 122) 
            return false;

        // + or /
        return intValue != 43 && intValue != 47;
    } 

dapat digunakan untuk mengganti if (!Base64Chars.Contains(value[i]))baris denganif (IsInvalid(value[i]))

Kode sumber lengkap dengan peningkatan dari Sam akan terlihat seperti ini (komentar dihapus untuk kejelasan)

public static class HelperExtensions {
    public static Boolean IsBase64String(this String value) {
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;
        var index = value.Length - 1;
        if (value[index] == '=')
            index--;
        if (value[index] == '=')
            index--;
        for (var i = 0; i <= index; i++)
            if (IsInvalid(value[i]))
                return false;
        return true;
    }
    // Make it private as there is the name makes no sense for an outside caller
    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;
        if (intValue >= 48 && intValue <= 57)
            return false;
        if (intValue >= 65 && intValue <= 90)
            return false;
        if (intValue >= 97 && intValue <= 122)
            return false;
        return intValue != 43 && intValue != 47;
    }
}

4

Jawabannya harus bergantung pada penggunaan string. Ada banyak string yang mungkin "valid base64" menurut sintaks yang disarankan oleh beberapa poster, tetapi mungkin "dengan benar" mendekode, tanpa kecuali, ke junk. Contoh: string 8char Portlandadalah Base64 yang valid. Apa gunanya menyatakan bahwa ini adalah Base64 yang valid? Saya kira pada titik tertentu Anda ingin tahu bahwa string ini harus atau tidak boleh didekode Base64.

Dalam kasus saya, saya memiliki string koneksi Oracle yang mungkin dalam teks biasa seperti:

Data source=mydb/DBNAME;User Id=Roland;Password=.....`

atau di base64 seperti

VXNlciBJZD1sa.....................................==

Saya hanya perlu memeriksa keberadaan titik koma, karena itu membuktikan bahwa BUKAN base64, yang tentu saja lebih cepat daripada metode di atas.


Setuju, kasus spesifik juga memberlakukan pemeriksaan cepat tambahan tertentu. Sama seperti string koneksi teks biasa vs base64 yang dikodekan.
Oybek

2

Peraturan sepakbola tinggi Knibb!

Ini seharusnya relatif cepat dan akurat tetapi saya akui saya tidak mengujinya secara menyeluruh, hanya beberapa.

Ini menghindari pengecualian mahal, regex, dan juga menghindari perulangan melalui kumpulan karakter, alih-alih menggunakan rentang ascii untuk validasi.

public static bool IsBase64String(string s)
    {
        s = s.Trim();
        int mod4 = s.Length % 4;
        if(mod4!=0){
            return false;
        }
        int i=0;
        bool checkPadding = false;
        int paddingCount = 1;//only applies when the first is encountered.
        for(i=0;i<s.Length;i++){
            char c = s[i];
            if (checkPadding)
            {
                if (c != '=')
                {
                    return false;
                }
                paddingCount++;
                if (paddingCount > 3)
                {
                    return false;
                }
                continue;
            }
            if(c>='A' && c<='z' || c>='0' && c<='9'){
                continue;
            }
            switch(c){ 
              case '+':
              case '/':
                 continue;
              case '=': 
                 checkPadding = true;
                 continue;
            }
            return false;
        }
        //if here
        //, length was correct
        //, there were no invalid characters
        //, padding was correct
        return true;
    }

2
public static bool IsBase64String1(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            try
            {
                Convert.FromBase64String(value);
                if (value.EndsWith("="))
                {
                    value = value.Trim();
                    int mod4 = value.Length % 4;
                    if (mod4 != 0)
                    {
                        return false;
                    }
                    return true;
                }
                else
                {

                    return false;
                }
            }
            catch (FormatException)
            {
                return false;
            }
        }

mengapa Anda pertama kali mencoba untuk mengubah kemudian mengontrol hal-hal lain
Snr

@Snr Anda benar. Saya pikir inilah yang perlu dia ubah: if (value.EndsWith ("=")) {value = value.Trim (); int mod4 = value.Length% 4; if (mod4! = 0) {return false; } Convert.FromBase64String (nilai); kembali benar; } lain {return false; }
Wajid khan

2

Saya akan menggunakan seperti ini sehingga saya tidak perlu memanggil metode konversi lagi

   public static bool IsBase64(this string base64String,out byte[] bytes)
    {
        bytes = null;
        // Credit: oybek http://stackoverflow.com/users/794764/oybek
        if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
           || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
            return false;

        try
        {
             bytes=Convert.FromBase64String(base64String);
            return true;
        }
        catch (Exception)
        {
            // Handle the exception
        }

        return false;
    }

2

Lakukan decode, encode ulang dan bandingkan hasilnya dengan string asli

public static Boolean IsBase64(this String str)
{
    if ((str.Length % 4) != 0)
    {
        return false;
    }

    //decode - encode and compare
    try
    {
        string decoded = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(str));
        string encoded = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(decoded));
        if (str.Equals(encoded, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    catch { }
    return false;
}

1

Imho ini sangat tidak mungkin. Semua solusi yang diposting gagal untuk string seperti "test" dan seterusnya. Jika dapat dibagi menjadi 4, tidak kosong atau kosong, dan jika merupakan karakter base64 yang valid, mereka akan lulus semua pengujian. Itu bisa banyak string ...

Jadi tidak ada solusi nyata selain mengetahui bahwa ini adalah string yang dikodekan berbasis 64 . Apa yang saya dapatkan adalah ini:

if (base64DecodedString.StartsWith("<xml>")
{
    // This was really a base64 encoded string I was expecting. Yippie!
}
else
{
    // This is gibberish.
}

Saya berharap string yang didekodekan dimulai dengan struktur tertentu, jadi saya memeriksanya.


0

Tentu. Pastikan masing-masing karakter dalam a-z, A-Z, 0-9, /, atau +, dan string berakhir dengan ==. (Setidaknya, itulah implementasi Base64 yang paling umum. Anda mungkin menemukan beberapa implementasi yang menggunakan karakter yang berbeda dari /atau +untuk dua karakter terakhir.)


Jika saya mengerti, karakter akhir tergantung pada panjang akhir teks yang dikodekan. Jadi jika teks yang disandikan tidak memiliki panjang% 4, maka '=' disertakan.
Rafael Diego Nicoletti

0

Ya, sejak Base64 menyandikan data biner ke dalam string ASCII menggunakan sekumpulan karakter terbatas, Anda cukup memeriksanya dengan ekspresi reguler ini:

/ ^ [A-Za-z0-9 \ = \ + \ / \ s \ n] + $ / s

yang akan memastikan bahwa string hanya berisi AZ, az, 0-9, '+', '/', '=', dan spasi.


Itu tidak selalu merupakan cara yang pasti untuk mengatakannya. Base64 melakukan beberapa padding untuk Anda menggunakan =karakter di bagian akhir. Jika padding itu tidak valid, itu bukan encoding base64 yang benar, meskipun cocok dengan regex Anda. Anda dapat mendemonstrasikan ini dengan menemukan string basis 64 dengan 1 atau 2 =di akhir, menghapusnya, dan mencoba memecahkan kodenya.
vcsjones

Saya yakin OP meminta untuk menjebak karakter ilegal, bukan jika str legal Base64. Jika yang terakhir, Anda benar, meskipun kesalahan pengisi di Base64 lebih mudah dijebak menggunakan pengecualian.
Rob Raisch

Tidak benar, setidaknya parser base64 versi Net mengabaikan padding sepenuhnya.
Jay

0

Saya menyarankan membuat regex untuk melakukan pekerjaan itu. Anda harus memeriksa sesuatu seperti ini: [a-zA-Z0-9 + / =] Anda juga harus memeriksa panjang senar. Saya tidak yakin yang satu ini, tapi saya cukup yakin jika ada sesuatu yang dipangkas (selain bantalan "=") itu akan meledak.

Atau lebih baik lagi periksa pertanyaan stackoverflow ini


0

Saya baru saja memiliki persyaratan yang sangat mirip di mana saya membiarkan pengguna melakukan manipulasi gambar dalam sebuah <canvas>elemen dan kemudian mengirim gambar yang dihasilkan diambil .toDataURL()ke backend. Saya ingin melakukan validasi server sebelum menyimpan gambar dan telah menerapkan ValidationAttributemenggunakan beberapa kode dari jawaban lain:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class Bae64PngImageAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null || string.IsNullOrWhiteSpace(value as string))
            return true; // not concerned with whether or not this field is required
        var base64string = (value as string).Trim();

        // we are expecting a URL type string
        if (!base64string.StartsWith("data:image/png;base64,"))
            return false;

        base64string = base64string.Substring("data:image/png;base64,".Length);

        // match length and regular expression
        if (base64string.Length % 4 != 0 || !Regex.IsMatch(base64string, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None))
            return false;

        // finally, try to convert it to a byte array and catch exceptions
        try
        {
            byte[] converted = Convert.FromBase64String(base64string);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}

Seperti yang Anda lihat, saya mengharapkan string jenis gambar / png, yang dikembalikan default <canvas>saat menggunakan .toDataURL().


0

Periksa Base64 atau string normal

public bool IsBase64Encoded (String str)

{

try

{
    // If no exception is caught, then it is possibly a base64 encoded string
    byte[] data = Convert.FromBase64String(str);
    // The part that checks if the string was properly padded to the
    // correct length was borrowed from d@anish's solution
    return (str.Replace(" ","").Length % 4 == 0);
}
catch
{
    // If exception is caught, then it is not a base64 encoded string
   return false;
}

}


0

Semua jawaban telah dicerna menjadi 1 fungsi yang memastikan 100% bahwa hasilnya akan akurat.


1) Gunakan fungsi seperti di bawah ini:

    string encoded = "WW91ckJhc2U2NHN0cmluZw==";
    msgbox("Is string base64=" + IsBase64(encoded));

2) Di bawah ini fungsinya:

  public bool IsBase64(string base64String)
    {
        try
        {
            if (!base64String.Length < 1)
            {
                if (!base64String.Equals(Convert.ToBase64String(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(Convert.FromBase64String(base64String)))), StringComparison.InvariantCultureIgnoreCase) & !System.Text.RegularExpressions.Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,2}$"))
                {
                    return false;
                    return;
                }
                if ((base64String.Length % 4) != 0 || string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0 || base64String.Contains(" ") || base64String.Contains(Constants.vbTab) || base64String.Contains(Constants.vbCr) || base64String.Contains(Constants.vbLf))
                {
                    return false;
                    return;
                }
            }
            else
            {
                return false;
                return;
            }

            return true;
            return;
        }
        catch (FormatException ex)
        {
            return false;
            return;
        }
    }

-1

Saya suka Ide cek Ekspresi Reguler. Ekspresi Reguler bisa cepat, dan sesekali menghemat biaya pengkodean. Penyelidikan asli, memiliki pembaruan yang melakukan hal ini. Saya menemukan, bahwa saya tidak pernah bisa berasumsi bahwa string tidak akan nol. Saya akan memperluas fungsi Ekstensi untuk memeriksa string sumber untuk karakter null, atau hanya spasi.

    public static bool IsBase64String(this string s)
    {
        if (string.IsNullOrWhiteSpace(s))
            return false;

        s = s.Trim();
        return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

    }

Ini gagal. Cobalah untuk melewatkan string yang memiliki 4 karakter yang sama seperti 'aaaa'.
Bettimms
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.