Soru Geçersiz UTF-8 karakterlerinin kullanıcı girişi nasıl kullanılır?


Genel kullanıcılardan geçersiz UTF-8 girdilerinin nasıl işleneceğine ilişkin bir strateji / tavsiye arıyorum.

Webapp'ım UTF-8 kullanıyor olsa da, bir şekilde bazı kullanıcılar geçersiz karakterler giriyor. Bu PHP'nin hatalarına neden olur json_encode () ve genel olarak etrafta olmak kötü bir fikir gibi görünüyor.

W3C I18N SSS: Çok Dilli Formlar "Eğer UTF-8 olmayan veri alınırsa, bir hata mesajı geri gönderilmelidir."

  • Verilerin girilebileceği düzinelerce farklı yere sahip bir site boyunca, bu pratik olarak nasıl yapılmalıdır?
  • Hatayı kullanıcı için yararlı bir şekilde nasıl sunuyorsunuz?
  • Kullanıcı tüm metinlerini kaybetmemek için kötü form verilerini nasıl geçici olarak depolar ve görüntüler? Kötü karakterleri soymak mı? Yeni bir karakter kullanın ve nasıl?
  • Veritabanındaki mevcut veriler için, geçersiz UTF-8 verileri algılandığında, onu dönüştürmeyi ve kaydetmeyi denemeliyim (nasıl? utf8_encode()? mb_convert_encoding ()?), veya veritabanında olduğu gibi bırakın ama json_encode () önce bir şey (ne?) yapıyor?

EDIT: mbstring uzantısına çok aşinayım ve "UTF-8 PHP'de nasıl çalışır" diye sormuyorum. Gerçek dünyadaki deneyimleri olan insanlardan, bunu nasıl ele aldıklarını tavsiye ederim. 

EDIT2: Çözümün bir parçası olarak, gerçekten bir hızlı geçersiz karakterleri U + FFFD'ye dönüştürme yöntemi


35
2017-09-15 06:42


Menşei


Bağladığınız yönergelere uymuyor, ancak geçersiz bayt dizilerini U +, FFFD böylece onunla yapılabilir. - zildjohn01
@ zildjohn01, Bunu yapmanın en iyi yolu nedir (hangi PHP fonksiyonları?). Yaklaşımınızla ayrıntılı bir cevap bırakabilir misiniz? - philfreo
Dürüst olmak gerekirse, bu çok heyecan verici değil. Ben sadece bir UTF-8 ayrıştırıcısını C'den PHP'ye çevirdim. Dize baytını bayt ile tarar ve geçersiz bir bayt dizisi bulunursa, dizeyi el ile yeniden yazar. Yavaş, ama taşınabilir. - zildjohn01
Paylaşmayı önemsiyorsanız yine de onu görmek isterdim - philfreo
Gerçekten görmek isterdim hızlı geçersiz karakterleri U + FFFD'ye çevirme yöntemi. :) - philfreo


Cevaplar:


accept-charset="UTF-8" özniteliği sadece tarayıcıların takip etmesi gereken bir kılavuzdur, bu şekilde göndermeye zorlanmazlar, berbat bir form gönderme botları iyi bir örnektir ...

Genelde yaptığım şey ya kötü karakterleri görmezden gelmek. iconv() ya da daha az güvenilir utf8_encode() / utf8_decode() kullanırsanız iconv Ayrıca kötü karakterleri transliterate etme seçeneğiniz de var.

İşte bir örnek iconv():

$str_ignore = iconv('UTF-8', 'UTF-8//IGNORE', $str);
$str_translit = iconv('UTF-8', 'UTF-8//TRANSLIT', $str);

Kullanıcılarınıza bir hata mesajı görüntülemek istiyorsanız, bunu muhtemelen alınan bir değer yerine global bir şekilde yapardım, bunun gibi bir şey muhtemelen iyi olur:

function utf8_clean($str)
{
    return iconv('UTF-8', 'UTF-8//IGNORE', $str);
}

$clean_GET = array_map('utf8_clean', $_GET);

if (serialize($_GET) != serialize($clean_GET))
{
    $_GET = $clean_GET;
    $error_msg = 'Your data is not valid UTF-8 and has been stripped.';
}

// $_GET is clean!

Ayrıca, yeni satırları ve şerit (görünmeyen) görünür kontrol karakterlerini normalize etmek isteyebilirsiniz:

function Clean($string, $control = true)
{
    $string = iconv('UTF-8', 'UTF-8//IGNORE', $string);

    if ($control === true)
    {
            return preg_replace('~\p{C}+~u', '', $string);
    }

    return preg_replace(array('~\r\n?~', '~[^\P{C}\t\n]+~u'), array("\n", ''), $string);
}

UTF-8'den Unicode kod noktalarına dönüştürülecek kod:

function Codepoint($char)
{
    $result = null;
    $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

    if (is_array($codepoint) && array_key_exists(1, $codepoint))
    {
        $result = sprintf('U+%04X', $codepoint[1]);
    }

    return $result;
}

echo Codepoint('à'); // U+00E0
echo Codepoint('ひ'); // U+3072

Muhtemelen Diğer bir alternatiften daha hızlı, ancak kapsamlı bir şekilde test etmediniz.


Örnek:

$string = 'hello world�';

// U+FFFEhello worldU+FFFD
echo preg_replace_callback('/[\p{So}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/u', 'Bad_Codepoint', $string);

function Bad_Codepoint($string)
{
    $result = array();

    foreach ((array) $string as $char)
    {
        $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

        if (is_array($codepoint) && array_key_exists(1, $codepoint))
        {
            $result[] = sprintf('U+%04X', $codepoint[1]);
        }
    }

    return implode('', $result);
}

Aradığın şey bu mu?


57
2017-09-18 18:16



Bu yöntem, geçersiz karakterleri yalnızca sıyırma yerine U + FFFD ile değiştirmenize izin verir mi? Görünüşe göre, bu daha çok yardımcı olacaktır, böylece kullanıcı hangi karakterlerin sorun olduğunu tam olarak görmektedir. - philfreo
@philfreo: Tamam, bu bir okumalıdır: webcollab.sourceforge.net/unicode.html. - Alix Axel
@philfreo: Unicode kod noktalarının çıktısını almak için bir kod gönderdim, sanırım resmin tamamını nereye sığdırılacağını biliyorsunuzdur. Senin hakkında utf8_encode soru, manuel sayfa her şeyi söylüyor: "kodları bir ISO-8859-1 dizesi UTF-8'e ", her zaman çöp atıyor. iconv Öte yandan, PHP'ye özgü olmayan olgun bir C kütüphanesi, dolayısıyla daha güvenilirdir. - Alix Axel
Bu nedenle, geçersiz veriler bulunduğunda, yalnızca soymadan (//IGNORE), kullanıcı hangi karakterin geçersiz olduğunu görür. - philfreo
@Yzmir: Bu ifadeyi genişletmek ister misiniz? Eğer harika bir örnek olan sahte dizeleri paylaşırsanız, her zaman yaptığım testlerde işe yaradığı için. - Alix Axel


Web uygulamanızdan geçersiz karakterler almak, HTML formları için varsayılan karakter kümeleriyle ilgili olabilir. Formlar için hangi karakter kümesinin kullanılacağını belirtebilirsiniz. accept-charset nitelik:

<form action="..." accept-charset="UTF-8">

Ayrıca, geçersiz karakterlerin nasıl işleneceği hakkında işaretçiler için StackOverflow'da benzer sorulara da göz atmak isteyebilirsiniz. Sağdaki sütundakiler, ancak kullanıcı için bir hatayı işaret etmenin, önemli verilerin beklenmedik şekilde kaybolmasına veya kullanıcının girişlerinin beklenmedik şekilde değişmesine neden olan geçersiz karakterleri temizlemeye çalışmaktan daha iyi olduğunu düşünüyorum.


4
2017-09-15 06:56



Sunucu tarafından kabul edilen karakter kümelerini belirtir. Sadece sayfa için UTF-8 kodlaması belirtmenin yeterli olup olmadığından emin değilim - tarayıcı, ISO-8859-1 veya başka bir şeyde form verilerini gönderirken UTF-8'i görüntüleyebilir. - Archimedix
Nedir accept-charset gerçekten - bir kullanıcının geçersiz karakterler veya sadece bir öneri sunması imkansız mıdır? Hala sunucu tarafına ulaşırsam kötü verileri nasıl ele almalıyım? - philfreo
Göre stackoverflow.com/questions/3719974/... bu gereksiz olurdu - philfreo
Bu özniteliği kendim kullanmıyorum ve şu ana kadar test ettiğim UTF-8 karakterleriyle ilgili bir sorunum yok. Ancak Pekka'nın bu soruya verdiği yoruma değinerek, W3C spesifikasyonu gerçekten Bu öznitelik için varsayılan değer ayrılmış "UNKNOWN" dizesidir. Kullanıcı aracıları Mayıs ayı Bu değeri, belgeyi iletmek için kullanılan karakter kodlaması olarak yorumlamaBu yüzden tarayıcıların bunu nasıl hallettiğinden emin değilim. stackoverflow.com/questions/3719974/#comment-3926382 - Archimedix
Kötü verilerle karşılaştığınızda, benim düşüncem, kullanıcıyı bu konuda bilgilendirmeli ve ona girişini gözden geçirme fırsatı vermelisiniz. Böylelikle karışıklığı önlersiniz ve kullanıcı bu konuda çalışabilir. Bununla birlikte, ilk etapta geçersiz veri almanıza neden olan durumları tanımlamak ilginç olacaktır. Bu, belirli tarayıcıların neden olduğu, istemci ve sunucu tarafından hangi başlıkların gönderildiği, tarayıcıda formun sayfadan sonra hangi kodlamanın ayarlandığı yüklü vb - Archimedix


Girdinin UTF-8 olup olmadığını kontrol etmek ve üzerinden geçmek için oldukça basit bir sınıf hazırladım. utf8_encode()ihtiyaç olarak:

class utf8
{

    /**
     * @param array $data
     * @param int $options
     * @return array
     */
    public static function encode(array $data)
    {
        foreach ($data as $key=>$val) {
            if (is_array($val)) {
                $data[$key] = self::encode($val, $options);
            } else {
                if (false === self::check($val)) {
                    $data[$key] = utf8_encode($val);
                }
            }
        }

        return $data;
    }

    /**
     * Regular expression to test a string is UTF8 encoded
     * 
     * RFC3629
     * 
     * @param string $string The string to be tested
     * @return bool
     * 
     * @link http://www.w3.org/International/questions/qa-forms-utf-8.en.php
     */
    public static function check($string)
    {
        return preg_match('%^(?:
            [\x09\x0A\x0D\x20-\x7E]              # ASCII
            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
            |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
            |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
            |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
            |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
            )*$%xs',
            $string);
    }
}

// For example
$data = utf8::encode($_POST);

2
2017-09-21 16:03



W3 regex için teşekkürler. - rjha94


PHP için bir multibyte uzantısı var, kontrol et: http://www.php.net/manual/en/book.mbstring.php

Denemelisin mb_check_encoding () işlevi.

İyi şanslar!


1
2017-09-15 06:50



Ben kendi sorusuyla bağlantılı olarak, mb uzantısına çok aşinayım. Bu sayfadaki yorumlar, bu mb_check_encoding () öğesinin kötü bayt dizilerini gerçekten kontrol etmediğini, ayrıca belirli bir bölümün nasıl yapılacağını değil, genel bir strateji sorduğumu gösteriyor. - philfreo
Bu nasıl bir yorum? Kimse görebileceğim bu işle bahsetmiyor. Fonksiyonun amacı tam olarak kötü bayt dizilerini kontrol etmektir. Var fonksiyon için bir açık hataAncak bu sayfadaki bir yorum, kapalı olması gerektiğini söylüyor. - itpastorn


Bu sorunun tamlığı için (mutlaka en iyi cevap değil) ...

function as_utf8($s) {
    return mb_convert_encoding($s, "UTF-8", mb_detect_encoding($s));
}

1
2017-09-25 01:24



Bu iyi, ama dikkatli ol; Hangi kodlamaları kontrol etmesi gerektiğini belirtmezseniz mb_detect_encoding () her zaman% 100 doğru değildir. Ayrıca, bazı kodlamalar hemen hemen aynı şekilde davranır (örneğin, ISO-8859-1 / Latin-1 ve CP-1252 / Windows-1252 - aslında, KOI8-R gibi herhangi bir tek baytlı kodlama, herhangi ISO-8859- *, vb. lezzetleri, çok zeki (ve muhtemelen hesaplama açısından pahalı) sezgisel kullanımda bulunmadıkça, tespit etmek neredeyse imkansızdır).


Çöpün içeri girmesine izin vermemenizi tavsiye ederim. Sisteminizi bozabilecek özel işlevlere güvenmeyin. Gönderdiğiniz verileri, tasarladığınız bir alfabeye göre yapmanız yeterlidir. Kabul edilebilir bir alfabe dizesi oluşturun ve gönderilen verileri bayt olarak bir dizide olduğu gibi yürü. Kabul edilebilir karakterleri yeni bir dizeye itin ve kabul edilemez karakterleri atın. Veritabanınızda sakladığınız veriler, kullanıcı tarafından tetiklenen verilerdir, ancak aslında kullanıcı tarafından sağlanan veriler değildir.

4. DÜZENLE: Kötü karakteri ile değiştirerek:

DÜZENLEME # 3: Güncelleme: 22 Eylül 2010 @ 1:32 pm Nedeni: Şimdi döndürülen dize UTF-8, artı kanıt olarak sağladığınız test dosyasını kullandım.

<?php
// build alphabet
// optionally you can remove characters from this array

$alpha[]= chr(0); // null
$alpha[]= chr(9); // tab
$alpha[]= chr(10); // new line
$alpha[]= chr(11); // tab
$alpha[]= chr(13); // carriage return

for ($i = 32; $i <= 126; $i++) {
$alpha[]= chr($i);
}

/* remove comment to check ascii ordinals */

// /*
// foreach ($alpha as $key=>$val){
//  print ord($val);
//  print '<br/>';
// }
// print '<hr/>';
//*/
// 
// //test case #1
// 
// $str = 'afsjdfhasjhdgljhasdlfy42we875y342q8957y2wkjrgSAHKDJgfcv kzXnxbnSXbcv   '.chr(160).chr(127).chr(126);
// 
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';
// 
// //test case #2
// 
// $str = ''.'????';
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';
// 
// $str = '';
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';

$file = 'http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt';
$testfile = implode(chr(10),file($file));

$string = teststr($alpha,$testfile);
print $string;
print '<hr/>';


function teststr(&$alpha, &$str){
    $strlen = strlen($str);
    $newstr = chr(0); //null
    $x = 0;
    if($strlen >= 2){

        for ($i = 0; $i < $strlen; $i++) {
            $x++;
            if(in_array($str[$i],$alpha)){
                // passed
                $newstr .= $str[$i];
            }else{
                // failed
                print 'Found out of scope character. (ASCII: '.ord($str[$i]).')';
                print '<br/>';
                $newstr .= '&#65533;';
            }
        }
    }elseif($strlen <= 0){
        // failed to qualify for test
        print 'Non-existent.';

    }elseif($strlen === 1){
        $x++;
        if(in_array($str,$alpha)){
            // passed

            $newstr = $str;
        }else{
            // failed
            print 'Total character failed to qualify.';
            $newstr = '&#65533;';
        }
    }else{
        print 'Non-existent (scope).';
        }

if(mb_detect_encoding($newstr, "UTF-8") == "UTF-8"){
// skip
}else{
    $newstr = utf8_encode($newstr);
}


// test encoding:
if(mb_detect_encoding($newstr, "UTF-8")=="UTF-8"){
    print 'UTF-8 :D<br/>';
    }else{
        print 'ENCODED: '.mb_detect_encoding($newstr, "UTF-8").'<br/>';
        }




return $newstr.' (scope: '.$x.', '.$strlen.')';
}

1
2017-09-20 13:49



"Alfabe" geçerli bir UTF-8 karakteri olduğunda bunu yapmayı nasıl öneriyorsunuz? - philfreo
Tamam EDIT # 1 güncellenir ve JSON'a koymak istediğiniz her şeyi arındırmalıdır. JSON hala boğuluyorsa, elbette alfabedeki karakterleri ayarlayabilirsiniz. JSON'ta boğulmakta olan örnek bir veri dosyası gönderebilmeniz için, bunu ince ayar yapmama yardımcı olacaktı. - Geekster
Bu bana UTF-8'i destekliyor gibi görünmüyor ... - philfreo
Şimdi UTF-8 geri döndü, kanıt. - Geekster
Sağladığınız dosyayı kullanmak için güncelledik. URL'nizi file () içine okuduğum için sunucunuzun fopen sarmalayıcılarının etkin olması gerekir. Tabii ki dosyayı indirebilir ve dizininizden okuyabilirsin ama ben LAZY'm. : D - Geekster


Belirtilen alt kümenin dışındaki tüm karakterleri kaldırmaya ne dersin? En azından başvurumun bazı bölümlerinde [a-Z] [0-9 set] dışındaki karakterlerin, örneğin kullanıcı adlarının kullanılmasına izin vermem. Bu dizinin dışındaki tüm karakterleri sessizce soran veya bunları algılarsa bir hata döndüren ve kararı kullanıcıya gönderen bir filtre işlevi oluşturabilirsiniz.


0
2017-09-15 07:07



"sadece hatalı biçimlendirilmiş sekansları veya kullanılamıyor karakterleri göz ardı etmek ISO 10646'ya uymuyor, hata ayıklamayı daha da zorlaştıracak ve kullanıcı karışıklığına neden olabilir." cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt - philfreo
@philifreo: ödevinizi veya referansınızı bağladığınız şey nedir? Eğer bu sadece bir referanssa, bunun nedeni, prof öğrencilere bir ödev atamasıdır ve onlara meydan okumasıdır - kötü kodlamayı saptamanın felsefi önemi yoktur. "Gösteri devam etmeli" ifadesini biliyor musun? Bu, programlama için de geçerlidir ve bu yüzden cevabım size kötü karakterleri soyma veya algılanırsa bir hata döndürme olanağı verir. - Geekster


Rails'in tüm tarayıcıları her zaman UTF-8 verilerini yayınlamaya zorlamak için yaptıklarını deneyin:

<form accept-charset="UTF-8" action="#{action}" method="post"><div
    style="margin:0;padding:0;display:inline">
    <input name="utf8" type="hidden" value="&#x2713;" />
  </div>
  <!-- form fields -->
</form>

Görmek railssnowman.info veya ilk yama bir açıklama için.

  1. Tarayıcının, UTF-8 kodlamasında form gönderim verileri göndermesini sağlamak için, sayfayı "text / html; charset = utf-8" İçerik Türü üstbilgisi ile oluşturun. meta http-equiv etiket).
  2. Tarayıcı, sayfa kodlamasıyla uğraşsa bile, tarayıcının UTF-8 kodlamasında form gönderim verileri göndermesini sağlamak için (tarayıcılar, kullanıcıların bunu yapmasına izin verir) accept-charset="UTF-8" şeklinde.
  3. Tarayıcı, sayfa kodlamasıyla uğraşsa bile (tarayıcılar kullanıcıların bunu yapmasına izin verir) ve tarayıcı tarayıcı IE olsa ve kullanıcı sayfa kodlamasını Korece ve kullanıcı olarak değiştirdiyse bile, tarayıcının UTF-8 kodlamasında form gönderim verileri göndermesini sağlamak form alanlarında Korece karakterler girilir, formda gizli bir girdi gibi bir değer ekleyin. &#x2713; Bu sadece Unicode karakter kümesinden olabilir (ve bu örnekte, Kore karakter kümesi değil).

0
2017-09-15 15:04



does accept-charset gerçekten tarayıcıları UTF8 olmayan veriler göndermemeye zorlar mı? Denerse, ne olur? Bu istemci tarafı doğrulaması atlanırsa, sunucuda nasıl ele almalıyım? - philfreo
Gizli alanı da açıklayabilir misiniz - bu gerekli mi? - philfreo
Göre stackoverflow.com/questions/3719974/... bu gereksiz olurdu - philfreo
Diğer sayfayı doğru okuduğunuzdan emin değilim .... Rails'in yaptığı şeyin açıklamasını da içerecek şekilde cevabımı düzenledim. - yfeldblum
Bu, istemci tarafı olduğu için XSS saldırılarına karşı korunmaya yardımcı olmaz. Buradaki fikrin sisteme gelen verileri arındırmak olduğuna inanıyorum, ancak bunun için HTML işaretlerine güvenemezsiniz. - Geekster


PHP kodunuzdaki tüm başlıklar için karakter kümesi olarak UTF-8'i ayarlayın

Her PHP çıktı başlığında, kodlama olarak UTF-8'i belirtin:

header('Content-Type: text/html; charset=utf-8');

0
2017-07-03 08:26