Mokėjimai.lt saugumo spragos ir dviprasmiška specifikacija

Rugpjūtis26

Žymės: php,saugumas,mokėjimai

Pasidalink

Mokėjimai.lt

Norint Internete vystyti verslą, kuomet reikia realizuoti mokėjimus Internete, yra 2 galimybės: sudaryti su keliais bankais sutartis, arba rinktis tarpininką. Pirmasis variantas yra gana komplikuotas ir brangus: sutartys su bankais ir mėnesiniai mokesčiai yra dideli, be to, techninė realizacija reikalauja specifinių technių žinių, kai kurie bankai reikalauja ir SSL sertifikato. Kitas, daugeliu atveju paprastesnis variantas yra rinktis tarpininką, kuris turi vieningą interfeisą visiems mokėjimų tipams, o kainą, ypač smulkesniam projektui, gali pasiūlyti itin konkurencingą. Vienas populiariausių tokių servisų Lietuvoje - mokėjimai.lt.

EVP, sukūrę mokėjimai.lt, eparasas.lt, manoid.lt ir kitus projektus, eina teisinga kryptimi, tačiau kai kurie požymiai rodo, jog gražiame įpakavime galima rasti neaišku ką... Vien jų OpenID implementacija, kuomet priimami tik manoid.lt vartotojai, rodo, jog jie nesupranta ir nepalaiko šios sistemos. Kai kurių sistemų siunčiama "User Agent" reikšmė yra "IDAMAS XML Sender", kas irgi rodo, jog kai kurių produktų kūrimu užsiima ne jie. Tiesiog outsource'ingas ar per mažas žinių ir patirties bagažas?

Grįžtant prie mokėjimai.lt, Diegiant ją į savo sistemą, vadovautis jų pateikta specifikacija nepavyks, kadangi ji... neteisinga! Funkcija, kuri turėtų patikrinti, ar mokėjimas teisingas, lietuviškoje ir angliškoje specifikacijos versijoje aprašytos skirtingai.

Lietuviškasis variantas:

 

//DĖMESIO: Nepamirškite funkcijoje įrašyti savo www.mokejimai.lt slaptažodį.
$your_pass = 'slaptazodis'; //irasome slaptazodi is jusu mokejimai.lt sistemos
$test_mode = 1; //1- kuomet testuojate. 0 - kuomet jau veikia sistema tikru rezimu.
if( mk_check ( $your_pass, $_REQUEST['orderid'],$_REQUEST['merchantid'] ) != $_REQUEST['_ss1'] || $_REQUEST['test'] <> $test_mode){
    die ("šaltinis netikras"); 
} 

function mk_check ( $password, $id, $mechant_id ) {
    return md5("{$password}|{$id}|{$_SERVER['REMOTE_ADDR']}|{$mechant_id}");
}

 

Tuo tarpu angliškas (webtopay.com) variantas:

 

    $your_webtopay_pass = md5("mypassword"); // please, enter your webtopay.com password
    if ( TestTransaction( $_GET['_ss1'], $your_webtopay_pass, $_GET['orderid'] ) ){ //verification of information source.
        //Everything is OK
    }else{
        //Something is wrong
    }

    function TestTransaction( $transaction, $userPassword, $ordeID, $test = 0, $status = 1 ){
        return ( $transaction == md5("{$userPassword}|{$ordeID}|{$test}|{$status}") );
    }

 

Kodai akivaizdžiai skiriasi, tačiau, nors angliškasis variantas arčiausiai tiesos, testiniame režime kintamasis $test vistiek bus lygus 0, ir md5 hash'ai nesutaps.

Tiesa, jie visus duomenis pasirašo viešojo rakto principu naudodami SSL sertifikatą, todėl yra ir kitas būdas patikrinti duomenų validumą.

Žiūrint į šią funkciją galima pastebėti vieną dalyką: žinant GET perduodamus duomenis (orderid ir _ss1) galime ir sužinoti prisijungimo prie mokėjimai.lt sistemos slaptažodį. Tai tik teorinė prielaida, tačiau ji turėtų suveikti. Kas nors galbūt ją ir patiktins ;)

Mokėjimo sistemos veikimo schema yra tokia:

1. Vartotojas formos duomenis (čia turime Order ID) perduoda mokėjimai.lt sistemai

2. Vartotojas nukreipiamas į banką ir ten sumoka pinigėlius. Grįžta į mokėjimai.lt

3. Siunčiama užklausa (su _ss1) kliento sistemai. Klientas pasižymi, kad už jo paslaugas ar prekes buvo apmokėta.

4. Vartotojas grąžinamas pas klientą

Viskas, ką reikia padaryti, kad gauti _ss1 reikšmę, yra pakeisti callback URL (duomenys juk nepasirašomi) kliento puslapyje (tam gali pasitarnauti ir Firebug). Tada 3 punkto užklausa bus siunčiama ne klientui, o ten, kur jūs pageidausite. Štai čia ir sužinosime _ss1 reikšmę.

O toliau elementarus brute-force, arba paieška iš rainbow table (juk jokia druskelė neįmaišoma į MD5 algoritmą).

Ir štai - turime vartotojo slaptažodį, kuriuo prisijungsime prie mokėjimai.lt sistemos.

Apie mokėjimai.lt saugumą taip pat rašė Steponas Kazakevičius.

Komentarai

Stepas (2009-08-26 09:12:36)
Ačiū už pastabas, jos pasiekė "kitą galą" ;)

Dėl OpenID tai vėl ta pati daina... Mokėjimų sistema reikalauja didesnio saugumo nei pvz. skelbimų portalas ar tinklaraščio komentarų skiltis. ManoID siūlo tai, ko reikia identifikuoti asmenį unikaliai ir užtikrintai. Paprastas OpenID be šio praplėtimo yra nepakankamai saugus. Todėl nesu linkęs pritarti teiginiui "nesupranta ir nepalaiko".

Dėl tam tikrų User Agent string'ų, tai neparodo nieko kito, negu kad kažkas yra naudojama. Šiuo atveju tai galiu sulyginti su posakiu, kad "jei kažkas naudojasi CakePHP, tai jam trūksta žinių". Kokį pagrindą naudoti - naujai kuriančios produktą įmonės pasirinkimo laisvė. Ir ji renkasi tai, kas tuo metu atrodo geriausia.

Dėl kitų punktų komentarų neturiu... Ten tiesiog reikia įdėti darbo :)

Sėkmės!
OpenID Paulius (2009-08-26 09:33:34)
Stepai, dėl to, ką paminėjai - viskas ok. Rašinio esmė nebuvo kritikuoti EVP politiką - tikslas buvo iškelti problemą, kad pateikiama specifikacija neatitinka realybės ir klaidina klientus, o sistemos saugumas turi spragų. Būtent toks ir pavadinimas :)
Rolandas (2009-08-26 09:35:21)
Sekmes bandant nubruteforsinti - po kokiu 40 metu turetum pataikyti :)
Rolandas (2009-08-26 09:52:25)
_ss1 skaiciuojamas taip
md5("[32 simboliai]|[nemazai simboliu]|[1 simbolis]|[1 simbolis]") tai gaunasi kaip minimum 38 simboliai. Rainbow tables iskarto atkrenta del naudojamo paipo (|) ir stringo ilgio reiskiasi tau lieka nubruteforsinti 38 simbolius generuojant visiems md5. Gal nori paskaiciuoti kiek tai variantu ir kiek tai uztruks? Dauguma mokejimo sistemu naudoja toki pat duomenu autentiskumo patvirtinimo metoda ir kazkaip neizvelgia cia saugumo spragu...
OpenID Paulius (2009-08-26 10:33:57)
Emmm... Nereikia 38 simbolių bruteforcint. Kadangi kiti parametrai apart slaptažodžio hash'o nekinta, pakanka keisti tik šią hash'o dalį. Taigi gaunasi, jog bruteforcinamo teksto ilgis yra netgi ne 32 simboliai, o tiek, koks slaptažodžio ilgis. Turint žinių apie slaptažodžių suteikimo strategiją galima dar labiau palengvinti šią užduotį.
Povilas (2009-08-26 14:56:06)
IDAMAS XML Sender? Uuuu.. Susijusi nuoroda apie jų kurtą Elektroninį dienyną:
http://blog.programisiai.lt/2008/08/15/1-pamoka-kaip-gauti-administratoriaus-teises-elektroniniame-dienyne-per-2-minutes/

Jei jie ir prie mokejimai.lt - tai man jau truputį baisoka...
Stepas (2009-08-26 15:39:55)
Pastaba: elektroninis dienynas buvo kurtas Vilniaus Savivaldybės programuotojų. Idamas jį tik perėmė. Tai nesusiję su jų kurta TVS.
Povilas (2009-08-26 16:15:33)
Stepai: sorry, nežinojau, kiek teko internete skaityt pletkų apie tą istoriją - viskas buvo suversta Idamui. Na bet anyway manau galėjo užtikrinti saugumą ar bent peržiūrėti šitas vietas, nes parduoda tai kaip savo produktą.
Stepas (2009-08-27 20:46:40)
Faktas, kad atsakingi jie... Tačiau pradžia nebuvo jų.

Kita vertus, su laiku įmonės labai keičiasi. Ir programuotojai, ir vadyba ir visa kita. Geros tampa blogomis, blogos - geromis. Taigi neskubėkime spręsti pagal praeitį ar ankstesnes klaidas - galbūt iš jų pasimokyta :)

Kita vertus... Žinant Lietuvos įmonių šefų supratimą apie tam tikrus "nereikalingus dalykus" (kaip refaktoringą, kodo dalių visišką perrašymą, perėjimą prie kitos sistemos), kurie tik "kainuoja pinigus, bet naudos neduoda", tai... ;(
Povilas Poderskis (2009-09-01 23:58:58)
šiaip tai yra ten ss2 autorizacija, kuri ir naudojama. ss1 tipo jeigu nėra openssl plugino, ir šiaip jis nerekomenduojamas naudoti, turbūt paliktas tik dėl backward compatibility su senom implementacijom, nes taip nuimtų jį, tai pyst ir pradėtų visiem nebeveik, ot gražu būtų.

kas dėl viso kito, tai aišku įdomu.
OpenID Paulius (2010-02-26 14:22:52)
Mokejimai.lt mikromokėjimų (SMS) patvirtinimui (_SS1) siunčia du kartus MD5 šifruotą mokėjimai.lt vartotojo slaptažodį, pvz.: md5(md5('slaptazodis'))

Saugumo atžvilgiu tai yra bloga praktika, kadangi MD5 algoritmas generuoja 128 bitų skaičių, kurio šešioliktainę išraišką grąžina funkcija MD5.
Taigi vietoje visų įmanomų simbolių (skaitmenų, didžiųjų bei mažųjų ne tik lotyniškų raidžių ir kitų simbolių), yra naudojami tik 16 simbolių (0-9 ir A-Z).
OpenID Paulius (2010-03-16 10:18:41)
mokejimai.lt niekam nieko nepranešę nebesiunčia parametro „paytext“, nors specifikacijoje jis nurodytas!



(Tinklalapis arba OpenID)