Autor Zpráva
Kcko
Profil
Ahoj,

vlastním několik fotbalových portálů a byl jsem osloven, zda bych nemohl uvolnovat určitá data pro menší weby, které nemají možnost mít propracovaný systém (statistiky, tabulky, další výpisy ...)
Rozhodl jsem se to udělat pomocí JS. Konkrétně JSONP.

Implementace je z mojí strany jasná, taktéž i pro odběratele. Nicméně nejsem si zcela jist, jak ověřovat, že daný odběratel může skutečně ten skript u sebe spouštět.
Jediné co jsem vyzjistil z pole $_SERVER je HTTP_REFERRER, ale ten nemusí být 100%?

Jak to řeší jiné velké servery? V url vidím vždy nějaký API klíč, náležící k danému odběrateli, ale co s tím pak dělají, jak zjištují "totožnost"?

Díky
Str4wberry
Profil
Neznám řešení velkých serverů, ale napadá mě, že by ten JS mohl například ověřit, jestli se location.hostname shoduje s příslušnou hodnotou pro daný klíč.
Kcko
Profil
Str4wberry:
Ten JS? Co by mělo tedy být v skript.php?

klient volá
$.getJSON("MUJ_SERVER/skript.php?API=123456?callback=?", function(response){

// vystup response si docvakne do nejakeho sveho placeholderu a vypise data


}); 
Str4wberry
Profil
Ve skript.php je JS, který zavolá uživatelem vytvořenou funkci na stránce, kde se mají externí data zobrazovat, a předá jí potřebná data.

Tohle, předpokládám, znáš, ale jen pro ujasnění.

Na stránce uživatele API bude:
<script>
function nazevFunkce(data) {
  // Zpracování a výpis data.nějakáHodnota
}
</script>
<script src='MUJ_SERVER/skript.php?API=123456?callback=nazevFunkce'></script>

Na tvém serveru v souboru skript.php bude cílem vytvořit JS ve stylu:
nazevFunkce({nějakáHodnota: 'xxx'});

Moje myšlenka je, že si ten skript načte z databáze API klíčů příslušnou doménu, porovná ji s location.hostname a podle toho vrátí řádný výsledek nebo něco ve stylu „Neplatný API klíč“.
Kcko
Profil
Str4wberry:
Ano tohle vím. Neuměl jsem si představit toto: Moje myšlenka je, že si ten skript načte z databáze API klíčů příslušnou doménu, porovná ji s location.hostname

Už vím, mohlo by to vypadat nějak takto:

<? 
     $validApi = dibi::fetch("SELECT 1 FROM clients WHERE apiKey = %i", $_GET["API"]);
     if ($validApi)
     {
         // tady jedine pres JS
        ?>
        <script>
        var domain = location.hostname;
        var apiDomain = '<?= $validApi->domain ?>';
        
        if (domain == apiDomain)
        {
            alert('TED JE TO OK ... ');
        }
        else
        {
          alert('TOHLE NENI OK, API je správné, ale doména jiná, pokus o podvržení z jiné domény');
        }
        
        </script>
        <?
     }
     else
     {
       echo "nazevFunkce({'Neplatny klíč'});";
     }
     
?>

Jenže, když to udělám takhle tak mi JSON parser zahlásí chybu, protože tam tag <script> nemá co dělat, vystupem musí být pouze volani te funkce s daty.
Str4wberry
Profil
Nepoužívej alert, chyby předávej té funkci.
<? 
     $validApi = dibi::fetch("SELECT 1 FROM clients WHERE apiKey = %i", $_GET["API"]);
     if ($validApi)
     {
         // tady jedine pres JS
        ?>
        var domain = location.hostname;
        var apiDomain = '<?= $validApi->domain ?>';
        
        if (domain == apiDomain)
        {
            nazevFunkce({'TED JE TO OK ... '});
        }
        else
        {
          nazevFunkce({'TOHLE NENI OK, API je správné, ale doména jiná, pokus o podvržení z jiné domény'});
        }
        <?
     }
     else
     {
       echo "nazevFunkce({'Neplatny klíč'});";
     }
     
?>
Kcko
Profil
Str4wberry:
No jasně, to mělo být na ukázku, jenže tobě tam ke správnému využití chybí tag <script> a ten tam bude zlobit při přijimání dat na straně klienta. (Parse error nebo něco v tomhle smyslu, protože čeká surové zavolání funkce a nemůže to být obaleno tagem.)
Str4wberry
Profil
Žádný <script> tam nechybí, celé to je JavaScript, který si uživatel API přilinkuje a zavolá mu jeho funkci s předáním patřičných parametrů.
Kcko
Profil
Str4wberry:
Aha, tak to se omlouvám, myslel jsem, že to bude fungovat trošku jinak. Dává to smysl, díky za ideu.
Chamurappi
Profil
Reaguji na Kcka:
Já bych na JSON API, které dělá krom zavolání funkce s parametrem ještě něco jiného, pohlížel jako na podezřelé. Ono totiž čistě teoreticky může dělat úplně cokoliv se mu zlíbí, protože JSONP je vlastně plnohodnotný externí <script>, implementačně nemá nic společného s normálním AJAXem. Každý cizí JS může získat nad stránkou stoprocentní kontrolu (sledovat chování uživatele, číst mu cookie, chytat hesla, páchat škody v jeho účtu či jeho jménem). Ještě jsem neviděl, že by nadstandardních pravomocí nějaké seriózní JSON API využívalo, byť jen ke kontrole domény. Myslím si, že většinou kontrolují jen toho referera.

ale ten nemusí být 100%?
Nemusí být 100%, ale drtivá většina brouzdalů ho posílá a tvůrce webové stránky to nemá možnost ovlivnit, nemůže dynamicky vyrobit <script>, který referera zaručeně nikdy nepošle. Takže pokud je referer správný nebo chybí, je to OK, pokud je špatný, není to OK. Když někdo zkusí parazitovat na cizím API klíči, bude mu to fungovat jen u hrstky návštěvníků, kteří referera neposílají, čímž bude jeho záměr výrazně zmrzčen.
Kcko
Profil
Chamurappi:
Na principu JSONP funguje např. Twitter, Flicker, pak nějaká databáze filmů co jsem se včera koukal a spousty jiných.

Když někdo zkusí parazitovat na cizím API klíči, bude mu to fungovat jen u hrstky návštěvníků, kteří referera neposílají, čímž bude jeho záměr výrazně zmrzčen.
Zajímavý pohled na věc, tak by to šlo také.

Navrhuješ tedy ještě jiný postup krom výše zmíněného (mého tebou doplněného)?
Str4wberry
Profil
Na principu JSONP funguje např. Twitter, Flicker, pak nějaká databáze filmů co jsem se včera koukal a spousty jiných.

Ano, protože v určitém smyslu neexistuje lepší alternativa. Ale všechno to je o důvěře uživatelů v danou službu a v zásadě je to všechno potenciální bezpečnostní riziko.

Na Chamurappiho názoru něco je. Pokud by někdo chtěl standardním způsobem používat cizí API klíč, tak to bude stačit. A když to bude chtít obejít, tak ta JS ochrana moc nepomůže.
Chamurappi
Profil
Reaguji na Kcka:
a spousty jiných
… ale nikdo negeneruje JS podmínku na otestování domény. Alespoň jsem to nikdy neviděl.
Je to taková nepsaná dohoda autora stránky s dodavatelem dat: „dávám ti nejvyšší možná oprávnění, předáš mi data, ale nic víc dělat nebudeš“. Na tvém místě bych kontroloval jen toho referera.

Fakt ale je, že hodně autorů stránek si neuvědomuje bezpečnostní rizika a těm bude nejspíš docela jedno, co generuješ, dokud jim tím nezpůsobíš viditelné potíže.
Str4wberry
Profil
Reakce na Chamurappiho:
Je to taková nepsaná dohoda autora stránky s dodavatelem dat
To mi ani nepřijde. Tohle se třeba vkládá u Twitteru, když si chceš vložit své příspěvky na svůj web.

ale nikdo negeneruje JS podmínku na otestování domény
Podle rychlého hledání „location.host“ v tom skriptu bych skoro nevylučoval, že se tam nějaká kontrola děje.

Když používáš facebookovské prvky, tak taky načítáš externí JS s neznámým kódem. Google Plus? Co myslíš. Měřicí kódy, reklamní kódy? Všechno to jsou dlouhatánské všemohoucí skripty plné nečitelného kódu, který se může kdykoliv změnit. Ve srovnání s tím je zdejší kod nadmíru transparentní. :–)
Kcko
Profil
Díky za názor. Bezpečnostní riziko směrem JÁ => moji odběratelé skriptu řešit nechci, protože vím co jim budu posílat.
Stejně by mě zajímalo jak to větší služby řeší, např. google mapy (vím, že od verze V3 už API klíč není potřeba, omezují tam počet requestů atp, ale stále si myslíte, že taky pouze kontrolovali referer a v případě, že by byl zablokovaný (prázdný) tak by skript pustili na výstup?


Str4wberry:
Když tak na to kouk[ám tak dle tohoto [#6] se stejne uživatel, i v případě špatné autorizace dostane k funkci s daty i když jsou v jiné větvi.
Chamurappi
Profil
Reaguji na Str4wberryho:
Tohle se třeba vkládá u Twitteru
To není dodání dat, to je dodání widgetu, tam očekávám, že se vypíše widget. Dodání samotných dat vypadá u Twitteru jinak.

Když používáš facebookovské prvky, tak taky načítáš externí JS s neznámým kódem
S neznámým, proměnlivým a potenciálně nebezpečným (v různých fázích vývoje sestřeloval různé kombinace prohlížečů a flashe). Proto ho nenačítám, pokud nemusím. Ani Google Plus nenačítám, dokud člověk nepřejede přes maketu plusovacího tlačítka myší.

Měřicí kódy, reklamní kódy?
Od těch nečekám prosté dodání dat, ale měření nebo vložení reklamy a případná kontrola prostředí z jejich strany mi pomáhá. Od JSON API očekávám jen ten JSON. Cokoliv navíc je navíc.

Ve srovnání s tím je zdejší kod nadmíru transparentní. :–)
Přepisuje globální proměnné domain a apiDomain. Frameworkem zmlsaný člověk si zavolá $.getJSON a pak bude týden hledat, proč se mu jiná část skriptu rozbila. Nepočítá s tím, že je u datového zdroje i výkonná složka (třebaže transparentní).


Reaguji na Kcka:
a v případě, že by byl zablokovaný (prázdný) tak by skript pustili na výstup?
Když se seznamuji s nějakým API, nalistuji si výstup normálně do prohlížeče a zkouším měnit v adrese parametry. V tu chvíli se referer žádný neposílá a data normálně dostávám. Stejným způsobem si můžeš prozkoušet všechna možná API a uvidíš…

i v případě špatné autorizace dostane k funkci s daty i když jsou v jiné větvi
Bude-li kód zpracovávat něčím jiným než interpreterem JavaScriptu v prohlížeči, tak ano. Ale za běžné situace se JS do té druhé větve nedostane. Musel by stáhnout ten skript AJAXem, což nepůjde, protože je na jiné doméně.
Str4wberry
Profil
Reakce na Chamurappiho:
Ano, u některých služeb existují i možnosti, jak dostat skoro jenom JSON. Pointa byla ale v tom, že v zásadě všichni weboví tvůrci vkládají do svých webů javascriptové magické skřínky a kromě Chamurappiho a pár dalších bez nějaké kontroly, co ten kód dělá.


Reakce na Kcka:
1) Když jsem před časem zkoušel použít Google Maps API s cizím klíčem na jiné doméně, tak mi to nefungovalo. Kromě DJPW referer neposílám.
2) Je zde smutný fakt, že když dáme data na internet, tak se k nim uživatel dostane. :–) Pořád zde jsou možnosti jako navštívit daný skript svým skriptem s podvrhnutým refererem a vytvořit si tak na vlastní doméně své vlastní API apod.
Kcko
Profil
Str4wberry:
add 1) to znamená, že testují tedy něco jiného než referer ne? :-)

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: