Mysql_real_escape_string () etrafında dolaşan SQL enjeksiyonu

Apr 21 2011

mysql_real_escape_string()İşlev kullanılırken bile SQL enjeksiyon olasılığı var mı ?

Bu örnek durumu düşünün. SQL, PHP'de şu şekilde oluşturulur:

$login = mysql_real_escape_string(GetFromPost('login')); $password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

Çok sayıda insanın bana bunun gibi bir kodun hala tehlikeli olduğunu ve mysql_real_escape_string()kullanılan işlevle bile kırılmasının mümkün olduğunu söylediğini duydum . Ama olası bir istismar düşünemiyorum?

Bunun gibi klasik enjeksiyonlar:

aaa' OR 1=1 --

çalışma.

Yukarıdaki PHP kodundan geçebilecek olası bir enjeksiyondan haberiniz var mı?

Yanıtlar

393 WesleyvanOpdorp Apr 21 2011 at 15:05

Aşağıdaki sorguyu düşünün:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()sizi buna karşı korumaz. Sorgunuzda değişkenlerinizin etrafında tek tırnak ( ' ') kullanmanız sizi buna karşı koruyan şeydir. Aşağıdakiler de bir seçenektir:

$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";
652 ircmaxell Aug 25 2012 at 09:08

Kısa cevap evet, evet dolaşmanın bir yolu varmysql_real_escape_string() . # Çok OBSCURE EDGE VAKALARI İÇİN !!!

Uzun cevap o kadar kolay değil. Burada gösterilen bir saldırıya dayanıyor .

Saldırı

Öyleyse, saldırıyı göstererek başlayalım ...

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Belirli durumlarda, bu 1'den fazla satır döndürür. Burada neler olduğunu inceleyelim:

  1. Bir Karakter Seti Seçme

    mysql_query('SET NAMES gbk');
    

    İşe bu saldırı için, sunucunun hem kodlamak için bağlantıda bekliyor o kodlamayı ihtiyaç 'ASCII ie olarak 0x27 ve kimin nihai bayt bir ASCII bazı karakter var \yani 0x5c. Sonradan anlaşıldı ki, varsayılan olarak MySQL 5.6 desteklenen 5 tür kodlamalar vardır: big5, cp932, gb2312, gbkve sjis. Burada seçeceğiz gbk.

    Şimdi, buranın kullanımına dikkat etmek çok önemli SET NAMES. Bu, SUNUCU ÜZERİNDEKİ karakter kümesini ayarlar . C API işlevine yapılan çağrıyı kullanırsak mysql_set_charset()sorun olmaz (2006'dan beri MySQL sürümlerinde). Ama neden bir dakika içinde daha fazlası ...

  2. Yük

    Bu enjeksiyon için kullanacağımız yük, bayt dizisi ile başlar 0xbf27. İçinde gbk, bu geçersiz bir çok baytlı karakterdir; içinde latin1, bu dize ¿'. İçinde olduğunu unutmayın latin1 ve gbk , 0x27kendi hazır bilgi üzerine 'karakteri.

    Biz denilen eğer, çünkü bu yükü seçmiş addslashes()üzerine, biz bir ASCII eklemek istiyorum \ie 0x5cönce, 'karakteri. Böylece 0xbf5c27, gbkiki karakterlik bir dizi olan: 0xbf5cardından gelen ile sonuçlanırdık 0x27. Ya da başka bir deyişle, geçerli bir karakter ve ardından gelen bir kaçış karakteri '. Ama kullanmıyoruz addslashes(). Yani bir sonraki adıma geçelim ...

  3. mysql_real_escape_string ()

    C API çağrısı , bağlantı karakter kümesini bilmesinden mysql_real_escape_string()farklıdır addslashes(). Böylece sunucunun beklediği karakter seti için kaçış işlemini düzgün bir şekilde gerçekleştirebilir. Ancak, bu noktaya kadar müşteri latin1bağlantı için hala kullandığımızı düşünüyor çünkü aksini asla söylemedik. Biz söyledin sunucu Kullandığımız gbkancak istemci hala sanıyor latin1.

    Bu nedenle çağrı mysql_real_escape_string()ters eğik çizgiyi ekler ve '"kaçmış" içeriğimizde serbest asılı bir karakter var ! Biz bakmak için olsaydı Aslında, $variçinde gbkkarakter kümesi, biz görürdük:

    縗 'VEYA 1 = 1 / *

    Saldırının gerektirdiği de tam olarak bu.

  4. Sorgu

    Bu kısım sadece bir formalitedir, ancak işlenen sorgu şu şekildedir:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
    

Tebrikler, az önce bir programa başarıyla saldırdınız mysql_real_escape_string()...

Kötü

Daha da kötüleşiyor. MySQL ile hazırlanmış ifadeleri taklit etmekPDO için varsayılan . Bu, istemci tarafında (C kütüphanesinde) temelde bir sprintf yaptığı anlamına gelir, bu da aşağıdakilerin başarılı bir enjeksiyonla sonuçlanacağı anlamına gelir:mysql_real_escape_string()

$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Şimdi, öykünülmüş hazırlanmış ifadeleri devre dışı bırakarak bunu önleyebileceğinizi belirtmek gerekir:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Bu genellikle gerçek bir hazırlanmış ifadeyle sonuçlanacaktır (yani veriler, sorgudan ayrı bir pakette gönderilir). Bununla birlikte, PDO'nun MySQL'in yerel olarak hazırlayamayacağı öykünme ifadelerine sessizce geri döneceğini unutmayın: kılavuzda listelenenler , ancak uygun sunucu sürümünü seçmeye dikkat edin).

Çirkin

En başta söyledim, mysql_set_charset('gbk')yerine kullansaydık bunların hepsini önleyebilirdik SET NAMES gbk. Ve 2006'dan beri bir MySQL sürümü kullanıyorsanız, bu doğrudur.

Daha eski bir MySQL salınımını, ardından kullanıyorsanız hata içinde mysql_real_escape_string()böyle bizim yükü içinde olanlar gibi geçersiz baytlı karakterler amaçları kaçmak için tek bayt olarak tedavi edildi anlamına geliyordu istemci doğru bağlantı kodlama haberdar olmuştu bile bu yüzden bu saldırı olur ve hala başarılı. Hata MySQL giderilmiştir 4.1.20 , 5.0.22 ve 5.1.11 .

Ama en kötüsü olduğunu PDOC API maruz vermedi mysql_set_charset()böylece önceki sürümlerinde bu 5.3.6 kadar olamaz , mümkün olan her komut için bu saldırıyı önlemek! Artık bir DSN parametresi olarak gösteriliyor .

Kurtarıcı Zarafet

Başlangıçta söylediğimiz gibi, bu saldırının işe yaraması için veritabanı bağlantısının savunmasız bir karakter seti kullanılarak kodlanması gerekir. utf8mb4olduğunu savunmasız değil destekleyebilir henüz ve her MySQL 5.5.3 beri yerine-ama sadece olmuştur kullanılabilir kullanmayı seçebilir böylece: Unicode karakter. Bir alternatif, utf8aynı zamanda savunmasız olmayan ve Unicode Temel Çok Dilli Düzlemin tamamını destekleyebilen bir alternatiftir .

Alternatif olarak, NO_BACKSLASH_ESCAPES(diğer şeylerin yanı sıra) çalışmasını değiştiren SQL modunu etkinleştirebilirsiniz mysql_real_escape_string(). Bu mod etkinleştirildiğinde, 0x27değiştirilecek 0x2727ziyade 0x5c27kaçan süreci dolayısıyla ve olamaz , daha önce var olmayan savunmasız kodlamalarla geçerli karakterleri oluşturmak (yani 0xbf27hala 0xbf27vb.) - Sunucu yüzden hala geçersiz olarak dize reddedecektir . Bununla birlikte, bu SQL modunu kullanmaktan kaynaklanabilecek farklı bir güvenlik açığı için @ eggyal'ın cevabına bakın .

Güvenli Örnekler

Aşağıdaki örnekler güvenlidir:

mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Çünkü sunucu bekliyor utf8...

mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Çünkü karakter setini, istemci ve sunucu eşleşecek şekilde doğru bir şekilde ayarladık.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Çünkü öykünmüş hazırlanmış ifadeleri kapattık.

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Çünkü karakter setini doğru bir şekilde ayarladık.

$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*"; $stmt->bind_param('s', $param); $stmt->execute();

Çünkü MySQLi her zaman doğru hazırlanmış ifadeler yapar.

Sarma

Eğer sen:

  • MySQL'in Modern Sürümlerini (5.1'in sonları, tümü 5.5, 5.6, vb.) VE mysql_set_charset() / $mysqli->set_charset()/ PDO'nun DSN karakter kümesi parametresini (PHP ≥ 5.3.6'da) kullanın

VEYA

  • Bağlantı kodlaması için savunmasız bir karakter kümesi kullanmayın (yalnızca utf8/ latin1/ ascii/ vb. Kullanırsınız)

% 100 güvendesin.

Aksi takdirde, kullanıyor olsanız bilemysql_real_escape_string() savunmasızsınız ...

190 eggyal Apr 25 2014 at 02:15

TL; DR

mysql_real_escape_string()aşağıdaki durumlarda hiçbir koruma sağlamaz (ve ayrıca verilerinizi kırabilir):

  • MySQL'in NO_BACKSLASH_ESCAPESSQL modu etkindir ( her bağlandığınızda açıkça başka bir SQL modu seçmediğiniz sürece bu olabilir ); ve

  • SQL dize değişmezleriniz çift tırnak "karakterleri kullanılarak tırnak içine alınır .

Bu hata # 72458 olarak dosyalanmış ve MySQL v5.7.6'da düzeltilmiştir (aşağıdaki " The Saving Grace " başlıklı bölüme bakın).

Bu başka, (belki daha az mı?) Belirsiz EDGE CASE !!!

@ İrcmaxell'in mükemmel cevabına saygı göstererek (gerçekten, bunun intihal değil, övgü olması gerekiyor!), Onun formatını benimseyeceğim:

Saldırı

Bir gösteri ile başlamak ...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- '); mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

Bu testtablodaki tüm kayıtları döndürecektir . Bir diseksiyon:

  1. Bir SQL Modu Seçme

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
    

    String Literals altında belgelendiği gibi :

    Bir dizeye tırnak işareti eklemenin birkaç yolu vardır:

    • " '" İle tırnak içine alınmış bir dizge içindeki " '", " " olarak yazılabilir ''.

    • " "" İle tırnak içine alınmış bir dizge içindeki " "", " " olarak yazılabilir "".

    • Alıntı karakterinin önüne bir çıkış karakteri (" \") koyun .

    • " '" İle alıntılanan bir dizenin içindeki " "" özel bir işleme gerek yoktur ve iki katına çıkarılması veya kaçılması gerekmez. Aynı şekilde, " "" ile alıntılanan bir dizenin içindeki " '" özel bir işleme ihtiyaç duymaz.

    Sunucunun SQL modu içeriyorsa NO_BACKSLASH_ESCAPES, bu seçeneklerden üçüncüsü (benimsediği genel yaklaşım) mysql_real_escape_string()mevcut değildir: onun yerine ilk iki seçenekten biri kullanılmalıdır. Dördüncü madde işaretinin etkisinin, kişinin verilerini parçalamaktan kaçınmak için değişmezi alıntılamak için kullanılacak karakteri mutlaka bilmesi gerektiğine dikkat edin.

  2. Yük

    " OR 1=1 -- 
    

    Yük, bu enjeksiyonu "karakterle tam anlamıyla başlatır . Belirli bir kodlama yok. Özel karakter yok. Garip bayt yok.

  3. mysql_real_escape_string ()

    $var = mysql_real_escape_string('" OR 1=1 -- ');
    

    Neyse ki, mysql_real_escape_string()SQL modunu kontrol ediyor ve davranışını buna göre ayarlıyor. Bakınız libmysql.c:

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }
    

    Bu nedenle escape_quotes_for_mysql(), NO_BACKSLASH_ESCAPESSQL modu kullanımdaysa , farklı bir temel işlev çağrılır . Yukarıda belirtildiği gibi, böyle bir işlevin, diğer tırnak karakterinin harfi harfine tekrarlanmasına neden olmadan onu tekrarlamak için harfi harfine alıntı yapmak için hangi karakterin kullanılacağını bilmesi gerekir.

    Bununla birlikte, bu işlev keyfi olarak dizenin tek tırnak karakteri kullanılarak alıntılanacağını varsayar' . Bakınız charset.c:

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }
    

    Bu nedenle, çift tırnak "karakterlerini değiştirmeden bırakır (ve tüm tek tırnaklı 'karakterleri ikiye katlar ) , gerçek karakterden bağımsız olarak alıntı yapmak için kullanılan gerçek karakterden bağımsızdır ! Bizim durumda $varsağlandığı argüman olarak tamamen aynı kalıntılar mysql_real_escape_string()kaçış yok yerini almıştır sanki -Bu hiç .

  4. Sorgu

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');
    

    Bir formalite, işlenen sorgu şudur:

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1
    

Bilgili arkadaşımın dediği gibi: tebrikler, bir programa başarıyla saldırdın mysql_real_escape_string()...

Kötü

mysql_set_charset()yardımcı olamaz, çünkü bunun karakter kümeleriyle hiçbir ilgisi yoktur; ne de yapamaz mysqli::real_escape_string(), çünkü bu aynı işlevin etrafındaki farklı bir sarmalayıcıdır.

Sorun, halihazırda açık mysql_real_escape_string() değilse de, daha sonra karar vermek için geliştiriciye bırakıldığından , çağrının değişmezin hangi karakterle alıntılanacağını bilememesidir . Bu nedenle, NO_BACKSLASH_ESCAPESmodda, bu işlevin keyfi alıntılarla kullanmak için her girdiden güvenli bir şekilde kaçabilmesinin hiçbir yolu yoktur (en azından, iki katına çıkarılmasını gerektirmeyen karakterleri ikiye katlamadan ve böylece verilerinizi parçalamadan).

Çirkin

Daha da kötüleşiyor. NO_BACKSLASH_ESCAPESstandart SQL ile uyumluluk için kullanılması gerekliliği nedeniyle vahşi doğada o kadar da nadir olmayabilir (örneğin, SQL-92 belirtiminin 5.3 bölümüne bakın , yani <quote symbol> ::= <quote><quote>dilbilgisi üretimi ve ters eğik çizgiye verilen herhangi bir özel anlamın olmaması). Ayrıca, ircmaxell'in gönderisinin açıkladığı (uzun zamandan beri düzeltilen) hataya geçici bir çözüm olarak kullanımı açıkça önerildi . Kim bilir, bazı DBA'lar, gibi yanlış kaçış yöntemlerinin kullanılmasını caydırmak için varsayılan olarak açık olacak şekilde bile yapılandırabilir .addslashes()

Ayrıca, yeni bir bağlantının SQL modu , yapılandırmasına göre sunucu tarafından ayarlanır (bir SUPERkullanıcı herhangi bir zamanda değiştirebilir); bu nedenle, sunucunun davranışından emin olmak için, bağlandıktan sonra istediğiniz modu her zaman açıkça belirtmeniz gerekir .

Kurtarıcı Zarafet

SQL modunu her zaman açıkça dahil etmeyecek şekilde ayarladığınız NO_BACKSLASH_ESCAPESveya tek tırnak karakterini kullanarak MySQL dizesi değişmezlerini alıntıladığınız sürece, bu hata çirkin başını escape_quotes_for_mysql()geri alamaz : sırasıyla kullanılmayacak veya hangi tırnak karakterlerinin tekrarlanmasını gerektirdiği varsayımı doğru ol.

Bu nedenle, tek tırnaklı dize değişmezlerinin alışılmış kullanımını zorlayacağından, kullanan herkesin modu NO_BACKSLASH_ESCAPESetkinleştirmesini tavsiye ederim ANSI_QUOTES. Bunun, çift tırnaklı değişmez değerlerin kullanılması durumunda SQL enjeksiyonunu engellemediğini unutmayın; yalnızca bunun gerçekleşme olasılığını azaltır (çünkü normal, kötü niyetli olmayan sorgular başarısız olur).

PDO'da, hem eşdeğer işlevi PDO::quote()hem de hazırlanmış deyim öykünücüsü çağırır mysql_handle_quoter()- tam olarak bunu yapar: kaçan değişmezin tek tırnak içinde alıntılanmasını sağlar, böylece PDO'nun bu hataya karşı her zaman bağışık olduğundan emin olabilirsiniz.

MySQL v5.7.6'dan itibaren bu hata düzeltildi. Değişiklik günlüğüne bakın :

İşlev Eklendi veya Değiştirildi

  • Uyumsuz Değişiklik: Yeni bir C API işlevimysql_real_escape_string_quote()yerine getirilmiştir,mysql_real_escape_string()çünkü ikinci işlevNO_BACKSLASH_ESCAPESSQL modu etkinleştirildiğindekarakterleri doğru şekilde kodlayamayabilir. Bu durumda,mysql_real_escape_string()tırnak karakterlerini iki katına çıkarmak dışında kaçamaz ve bunu doğru bir şekilde yapmak için, alıntı bağlamı hakkında mevcut olandan daha fazla bilgi bilmesi gerekir. mysql_real_escape_string_quote()alıntı bağlamını belirtmek için fazladan bir argüman alır. Kullanım detayları için mysql_real_escape_string_quote () 'ye bakınız.

     Not

    Uygulamalar , artık başarısız olan ve etkinleştirilirse bir hata üreten mysql_real_escape_string_quote()yerine kullanılmak üzere değiştirilmelidir .mysql_real_escape_string()CR_INSECURE_API_ERRNO_BACKSLASH_ESCAPES

    Referanslar: Ayrıca bkz. Hata # 19211994.

Güvenli Örnekler

İrcmaxell tarafından açıklanan hatayla birlikte ele alındığında, aşağıdaki örnekler tamamen güvenlidir (MySQL'in 4.1.20, 5.0.22, 5.1.11'den sonra kullanıldığı veya GBK / Big5 bağlantı kodlamasının kullanılmadığı varsayılırsa) :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*'); mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

... çünkü açıkça içermeyen bir SQL modu seçtik NO_BACKSLASH_ESCAPES.

mysql_set_charset($charset); $var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

... çünkü dize değişmezimizi tek tırnaklarla alıntılıyoruz.

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(["' OR 1=1 /*"]);

... çünkü PDO tarafından hazırlanan ifadeler bu güvenlik açığından muaftır (ve ircmaxell de PHP≥5.3.6 kullanıyor olmanız ve karakter setinin DSN'de doğru şekilde ayarlanmış olması koşuluyla; veya hazırlanmış ifade öykünmesinin devre dışı bırakılmış olması koşuluyla) .

$var = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... çünkü PDO'nun quote()işlevi sadece değişmezden kaçmakla kalmaz, aynı zamanda onu (tek tırnaklı 'karakterlerle) tırnak içine alır ; bu durumda ircmaxell'in hatasından kaçınmak için PHP≥5.3.6 kullanıyor olmanız ve karakter kümesini DSN'de doğru şekilde ayarlamış olmanız gerektiğini unutmayın .

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

... çünkü MySQLi tarafından hazırlanan ifadeler güvenlidir.

Sarma

Dolayısıyla, eğer:

  • yerel hazırlanmış ifadeler kullanın

VEYA

  • MySQL v5.7.6 veya üzerini kullanın

VEYA

  • içinde ilave en az biri ircmaxell özetinde çözümlerden bir, kullanım istihdam etmek:

    • PDO;
    • tek tırnaklı dize değişmezleri; veya
    • içermeyen açıkça ayarlanmış bir SQL modu NO_BACKSLASH_ESCAPES

... o zaman tamamen güvende olmalısınız (bir kenara kaçan dizge kapsamı dışındaki güvenlik açıkları).

19 Slava Apr 21 2011 at 15:01

%Joker karakter dışında bundan geçebilecek hiçbir şey yok . Eğer bunu filtrelemezseniz LIKEsaldırganın %giriş olarak koyabileceği bir ifade kullanıyorsanız tehlikeli olabilir ve kullanıcılarınızdan herhangi birinin şifresini zorla kullanmanız gerekir. Veriler, sorgunun kendisine bu şekilde müdahale edemeyeceğinden, insanlar genellikle% 100 güvenli hale getirmek için hazırlanmış ifadelerin kullanılmasını önerir. Ancak bu kadar basit sorgular için, büyük olasılıkla daha verimli olacaktır.$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);