Az alkalmazások lehetőségeit bemutatva írtam már róla, hogy az alkalmazás kommunikálni tud a külvilággal is, és megemlítettem, hogy lehetőség van ezt a kommunkációt hitelesíteni (ez az aláírt kommunikáció), vagyis bizonyíthatóan igazolni, hogy mely féltől érkezett a kérés. A bejegyzésben arról írok, hogy hogyan működik, mire használható ez a lehetőség.
Aláírt kommunikációra akkor van (leginkább) szükségünk, amikor egy felhasználó nevében saját szolgáltatáshoz fordulunk, és szeretnénk tudni, hogy:
- valóban az iWiW felől jött-e a HTTP kérés
- mely felhasználótól jött a kérés
- mely alkalmazástól jött a kérés
A felhasználói azonosítót ugyan átadhatjuk egy sima GET paraméterben is, továbbá azt is nézhetjük hogy mely IP címek felől érkezett a kérés, ellenben ezek nem sorolhatóak a biztonságos és jó megoldások körébe, mivel nem túl megbízhatóak. Például lehetséges egy adott alkalmazás oldalon, annak nevében kéréseket indítani - ekkor ha az alkalmazás teszi be a felhasználó azonosítóját a paraméterek közé akkor azt könnyen ki lehet cserélni, és más felhasználó nevében is lehet ténykedni (vagy olyan információhoz is hozzá lehet jutni, mely nem tartozik a trükközőre). Az IP címek is változhatnak idővel különösebb értesítés nélkül, az pedig nem jó ha emiatt akármennyi időre is működésképtelen lesz az alkalmazásunk, mert nem fogadja el az új címről a kéréseket.
A biztonságos megoldást az aláírt kérések jelentik, melyek használata nagyon egyszerű: a kérésben elküldött (GET/POST) paramétereket az iWiW OpenSocial motorja kiegészíti további paraméterekkel, többek között egy olyan ellenőrzőösszeggel, mely segítségével ellenőrizhető a kérés forrása. A kiegészítő információk a következők:
- opensocial_owner_id: az alkalmazás tulajdonosa
- opensocial_viewer_id: az alkalmazás nézője
- opensocial_app_id: az alkalmazás azonosítója
- opensocial_app_url: az alkalmazás XML-jének url-je
Az ellenőrzőösszeget a folyamat az összes paramétert figyelembe véve állítja elő felhasználva egy speciális kulcs értéket is, a végeredmény pedig a az oAuth szabvány aláírás szabályainak megfelelően képződik (az aláírt kéréseknek ennyi, és csak ennyi a köze az oAuthhoz, amiről még lesz szó, és ami amúgy teljesen másra jó). Az ellenőrzőösszeget kell leellenőriznünk a szerver oldalon, mert ez az, amit csak és kizárólag az iWiW fog tudni létrehozni, más “támadó harmadik fél” nem. Ha ez stimmel, akkor biztosak lehetünk benne, hogy az előbb felsorolt paraméterek is hitelesek, megbízhatóak.
Az oAuth szabvány nem definiálja kizárólagosan hogy mely algoritmust kell használni az aláírások készítéséhez, ezt a megvalósításra bízza rá. Az iWiW a következő algoritmusokat támogatja:
- RSA-SHA1: Egy egy nyílt kulcsú titkosítást használó algoritmus, avagy egy titkos és egy publikus kulcs segítségével történik az aláírás. Ezt az algoritmus az alapértelmezett iWiW kulccsal használható signed request kérések esetén. A titkos kulcs az iWiW-nél van és ezzel készül az ellenőrzőösszeg, míg a publikus kulcs segítségével ellenőrizhető hogy valóban az iWiW-től jött-e a kérés.
- HMAC-SHA1: Ez egy megosztott rejtett kulcsot használó algoritmus, avagy egy darab kulcs van csak, melyet mind titkosításkor, mind pedig ellenőrzéskor használni kell. Ez az algoritmus a fejlesztő által feltöltött kulcsokkal használható signed request és oAuth kérések esetén.
- HMAC_SYMMETRIC: ugyanúgy működik, mint a HMAC-SHA1.
Ennyit az elméletről, lássuk hogy hogyan működik mindez a gyakorlatban. Alapértelmezett esetben egy alkalmazásnál az RSA-SHA1 algoritmussal, az iWiW alapértelmezett kulcsaival működik az aláírás. A kérés indítása az alkalmazásunkban:
gadgets.io.makeRequest( "http://iwiw.example.com/request.url?param=iWiW", function(data){ alert(data.text); }, { METHOD: gadgets.io.MethodType.GET, CONTENT_TYPE: gadgets.io.ContentType.TEXT, AUTHORIZATION: gadgets.io.AuthorizationType.SIGNED } );
A szerver oldalt PHP-ben valósítottam meg, ehhez egy kész oAuth függvénykönyvtárat használtam, az oAuth.php fájl kellett belőle, ez lett betöltve. Más szerver oldali nyelvet használóknak segítséget nyújthat az oAuth oldalán levő felsorolás, az elv mindenhol ugyanaz, az elterjedtebb programozási nyelvekhez kész kódok érhetőek el.
A PHP kód tehát:
<? include "oAuth.php"; class IWiWSignatureMethod extends OAuthSignatureMethod_RSA_SHA1 { protected function fetch_public_cert(&$request) { return <<<EOD -----BEGIN CERTIFICATE----- MIICRTCCAa4CCQCJTQjR2NRBSTANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJI VTERMA8GA1UECBMIQnVkYXBlc3QxETAPBgNVBAcTCEJ1ZGFwZXN0MREwDwYDVQQK EwhJV0lXIEx0ZDENMAsGA1UECxMEaVdpVzEQMA4GA1UEAxMHaXdpdy5odTAeFw0w ODA5MTgxMDAzMDVaFw0wOTA5MTgxMDAzMDVaMGcxCzAJBgNVBAYTAkhVMREwDwYD VQQIEwhCdWRhcGVzdDERMA8GA1UEBxMIQnVkYXBlc3QxETAPBgNVBAoTCElXSVcg THRkMQ0wCwYDVQQLEwRpV2lXMRAwDgYDVQQDEwdpd2l3Lmh1MIGfMA0GCSqGSIb3 DQEBAQUAA4GNADCBiQKBgQCgSymvAxyuISIufaSFEAiP1WpivlIlvIa7eIzpjA7H JqmGtJzosi6IihqFTwthiriZ09d1J4R6jTeqm/CXKGcmUwTqBULxbZC1Yw6dGJMw FjacooFvu5iHVAM3OFyG0DsMZWwT/XSexybc1+vPHdwkMSgZOg/FgoivahZlV3Mt 8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACfd/y/nAXGXFShDXzFhsDC43izjIg13 STzhIUzsXVFg+zhik7XghjMsohMF99n6GYdRFIoRZpqMxIYQnnw5i10FIu57U39j kwo4wtCVsSDq/C5un2kcTPJL+kH6P/8OzsaRRuuCn5N7rjGNtB4LvX/JIIxEfS+/ KR+yjqXrml+O -----END CERTIFICATE----- EOD; } } $request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST)); $signature_method = new IWiWSignatureMethod(); @$signature_valid = $signature_method->check_signature($request, null, null, $_GET["oauth_signature"]); if ($signature_valid == true) { echo 'Hello '.$_GET['param'].'!'; // echo $_GET['opensocial_owner_id']; // echo $_GET['opensocial_viewer_id']; } else { echo 'Sikertelen hitelesítés.'; } ?>
Először definiálunk egy IWiWSignatureMethod osztályt mely a OAuthSignatureMethod_RSA_SHA1 osztály leszármazottja lesz, s amit az ellenőrzőösszeg kiszámításához fogunk felhasználni. Létrehozunk egy OAuthRequest objektumot, ami mind a GET-es, mind pedig a POST-os paramétereket megkapja, példányosítjuk az előbb létrehozott IWiWSignatureMethod osztályt, és meghívjuk a check_signature metódusát, mely le fogja ellenőrizni, hogy az oauth_signature GET paraméterben megfelelő érték jött-e. Ha igen, akkor visszaküldjük a kapott paramétert egy “Hello” előtaggal, ha nem, akkor megmondjuk hogy rossz volt a kérés.
Ha feltöltöttünk már egy kulcsot, akkor az aláírt kérések nem az iWiW alap kulcsát, hanem az általunk feltöltött kulcsot fogják használni, ezekhez HMAC_SHA1 vagy HMAC_SYMMETRIC kódolást választhatunk. Ezek ahogy az előbb is említettem, nem publikus és titkos kulcspárt használnak, hanem egy megosztott rejtett kulcsot. Ez a kulcs “bármilyen” szövegrészlet lehet, persze érdemes egy megfelelően hosszú, véletlenszerűhöz közelítő, nem kitalálható kulcsot választani. A következő tehát rossz példa:

Az előbb használt PHP-s oAuth kódkönyvtár a HMAC-SHA1 kódolást támogatja, ezért én ezt választottam. A kliens oldali kódunk teljesen változatlan, míg a szerver oldali kód a következőképpen alakul:
<? include "oAuth.php"; $request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST)); $consumer = new OAuthConsumer($_GET['oauth_consumer_key'], 'teszt kulacs', NULL); $signature_method = new OAuthSignatureMethod_HMAC_SHA1(); @$signature_valid = $signature_method->check_signature($request, $consumer, null, $_GET["oauth_signature"]); if ($signature_valid == true) { echo 'Hello '.$_GET['param'].'!'; // echo $_GET['opensocial_owner_id']; // echo $_GET['opensocial_viewer_id']; } else { echo 'Sikertelen hitelesítés.'; } ?>
Először létrehozunk egy OAuthRequest objektumot, ami mind a GET-es, mind pedig a POST-os paramétereket megkapja, példányosítunk egy OAuthConsumer objektumot és egy oAuthSignatureMethod_HMAC_SHA1 osztályt, és meghívjuk ez utóbbi check_signature metódusát, mely le fogja ellenőrizni, hogy az oauth_signature GET paraméterben megfelelő érték jött-e. Végül ha igen, akkor ahogy az előbb is, visszaküldjük a kapott paramétert egy “Hello” előtaggal, ha nem, akkor megmondjuk hogy rossz volt a kérés.
A kulcs nevének érdemes egy a kulcs használóját, funkcióját beazonosító karaktersorozatot választani, például “example.com”, “example.com/app1″, de ezek nem kötöttségek, csak javaslatok.
A funkció használatához jellemzően elegendő lehet ennyi ismeret, de célszerű lehet utána olvasni az oAuth-nak, a kriptográfiának általában, a nyílt kulcsú titkosításnak, az RSA eljárásnak és az egyéb kapcsolódó témaköröknek, hogy tudjuk pontosan mit használunk, és hogyan működik.
Hello!
Ugy latom a HMAC-DSA1-et nem nagyon szeretitek
Minden pelda itt es is a wikin is RSA. Ez szep es jo, de en nem csak abban szeretnek biztos lenni hogy a keres az iwiw containerbol jott hanem hogy az en alkalmazasom kuldte. A HMAC-DSA1 pedig lehetoseget biztosit arra (mint olvashattuk) hogy sajat (egyedi) kulcsot allitsak ki.
A problemam teljesen egyszeru: en nem azt a signature-t kapom ugyanazokkal az adatokkal amit kuldtok. Ambar lehet (sot, remelem) hogy *most*
tevedek, igy aki kedvet erez hozza cafoljon meg kerem szepen.
A consumer_secret -em (azaz a “titkos kulcs”)
0xeiHcIib7Uk0Kp1CBLDDNmzzqT8k3hRGrQFpCA1TqA
A teljes alairt request, ahogy az access logomban latom
84.2.37.110 - - [07/Jan/2009:11:07:17 +0000] “GET /opensocial/ping?opensocial_owner_id=sandbox.iwiw.hu%3AB9hDSW6tQ8&opensocial_viewer_id=sandbox.iwiw.hu%3AB9hDSW6tQ8&opensocial_app_id=2018662519&opensocial_app_url=http%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial.xml&oauth_version=1.0&oauth_timestamp=1231330978&oauth_consumer_key=ahWDDmGMkB4amIlKywHGfA&oauth_signature_method=HMAC-SHA1&oauth_nonce=1231330978966564000&oauth_signature=zAbMelOluxldSp5e2o0oDqTxt1c%3D HTTP/1.0″ 401 21
ebbol a kovetkezoket banyaszom elo:
base_string, a kovetkezo szerint allitottam ossze: http://wiki.oauth.net/TestCases:
Ebbol:
GET http://wgw.collisioncourse.cc/opensocial/ping?opensocial_owner_id=sandbox.iwiw.hu:B9hDSW6tQ8&opensocial_viewer_id=sandbox.iwiw.hu:B9hDSW6tQ8&opensocial_app_id=2018662519&opensocial_app_url=http://wgw.collisioncourse.cc/opensocial.xml&oauth_version=1.0&oauth_timestamp=1231330978&oauth_consumer_key=ahWDDmGMkB4amIlKywHGfA&oauth_signature_method=HMAC-SHA1&oauth_nonce=1231330978966564000
Lett ez:
GET&http%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial%2Fping%3Fopensocial_owner_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_viewer_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_app_id%3D2018662519%26opensocial_app_url%3Dhttp%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial.xml%26oauth_version%3D1.0%26oauth_timestamp%3D1231330978%26oauth_consumer_key%3DahWDDmGMkB4amIlKywHGfA%26oauth_signature_method%3DHMAC-SHA1%26oauth_nonce%3D1231330978966564000
oauth_signature ( ezt kene kapnom ) :
zAbMelOluxldSp5e2o0oDqTxt1c=
egy nagyon rovid ruby scripttel probalok letrehozni ezekbol signature-t a kovetkezokepp
require ‘cgi’
require ‘rubygems’
require ‘hmac-sha1′
require ‘base64′
def escape(value)
CGI.escape(value.to_s).gsub(”%7E”, ‘~’).gsub(”+”, “%20″)
end
def signature(base_string, consumer_secret,token_secret=”)
secret=”#{escape(consumer_secret)}&#{escape(token_secret)}”
Base64.encode64(HMAC::SHA1.digest(secret,base_string)).chomp.gsub(/\n/,”)
end
ezt a ket metodust a googlebol halasztam ki, de nagyon egyszeru az egesz: fogom a consumer secretet (azaz a megosztott titkos kulcsot) es a base stringet (plusz a token_secretet ami most ugye ures mert nem tortenik teljes oauth ciklus, csak az alairast ellenorzom), es a ketto osszegevel (siman concat es kell egy “&” kozejuk) kapom meg az alairast.
Lefuttatva viszont..
irb(main):014:0> base_string = ‘GET&http%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial%2Fping%3Fopensocial_owner_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_viewer_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_app_id%3D2018662519%26opensocial_app_url%3Dhttp%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial.xml%26oauth_version%3D1.0%26oauth_timestamp%3D1231330978%26oauth_consumer_key%3DahWDDmGMkB4amIlKywHGfA%26oauth_signature_method%3DHMAC-SHA1%26oauth_nonce%3D1231330978966564000′
=> “GET&http%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial%2Fping%3Fopensocial_owner_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_viewer_id%3Dsandbox.iwiw.hu%3AB9hDSW6tQ8%26opensocial_app_id%3D2018662519%26opensocial_app_url%3Dhttp%3A%2F%2Fwgw.collisioncourse.cc%2Fopensocial.xml%26oauth_version%3D1.0%26oauth_timestamp%3D1231330978%26oauth_consumer_key%3DahWDDmGMkB4amIlKywHGfA%26oauth_signature_method%3DHMAC-SHA1%26oauth_nonce%3D1231330978966564000″
irb(main):015:0> consumer_secret = ‘0xeiHcIib7Uk0Kp1CBLDDNmzzqT8k3hRGrQFpCA1TqA’
=> “0xeiHcIib7Uk0Kp1CBLDDNmzzqT8k3hRGrQFpCA1TqA”
irb(main):016:0> signature(base_string,consumer_secret)
=> “zRKR2mdMR0TBSWpOwiJr2cHlFUc=”
ez nem egyezik a beerkezo alairassal (zAbMelOluxldSp5e2o0oDqTxt1c=)
Mit csinalok rosszul? Rossz a base_string? A containerben nem igy all ossze?
Bocs a hosszu kommentert, abban se vagyok biztos hogy fog ez megjelenni :-/
andras: a válasz is hosszú lesz.
1) “en nem csak abban szeretnek biztos lenni hogy a keres az iwiw containerbol jott hanem hogy az en alkalmazasom kuldte”
Akár nem töltesz fel kulcsot, és az iWiW publikus kulcsát használod az aláírás ellenőrzésére, akár pedig saját megosztott rejtett kulcsod feltöltése, és az azzal történő ellenőrzés lehetőséget kínál erre. Az iWiW titkos kulcsát nem tudhatja senki sem, így nem tud kérést hamisítani, és ugyanez igaz az általad az iWiW Fejlesztői Portálra feltöltött rejtett kulccsal is. Mind a két felállásban a kéréssel párhuzamosan érkezik az alkalmazás azonosítója is, így abban is biztos lehetsz, hogy az alkalmazásodtól jön a kérés.
2) Miért nem egy biztosan működő Ruby library-val próbálkozol?
Végigmegyek a lépéseken, és megnézem mi lehet a hiba, de biztosan jobban járnál egy kész könyvtárral.
Koszonom a valaszt. Azzal probalkozom. Oauth gem + ruby oauth plugin. Invalid oauth request, 401
odaig latom a logban hogy megtalalja a kulcsot a consumer_key -el, tehat tuti a megfelelo secret-el allitja elo a digest-et a base_url -re.. Csak azt szeretnem tudni a contenerben hogy all ossze a base_url
Andras: Meg kéne nézned a PHP kódot, szerintem elég átlátható.
Megneztem es en nem latom benne
Lehet hogy felreertettel. Az oAuth specifikacio szerint az alairt request azt jelenti hogy a base_url -t, azaz a http metodus + hivott url + parameterek osszeget “irja ala” (HMAC-DSA1-el, a fenti ruby kodban ez HMAC::SHA1.digest(secret,base_string) ) a consumer_secret -el. Ebbol lesz a digest amit hozzaad a requesthez.
Ennek a specifikacio szerint kell igy tortennie. Eleg jol leirjak ezen a cimen: http://oauth.net/core/1.0/#anchor14
Atfogalmazva akkor: a consumer (iwiw container) hogy allitja ossze a 9.1. Signature Base String -et.
koszi
A
Amugy most vettem eszre hogy a base_string -bol ki kell hagyni a oauth_signature parametert. Megnezem igy is mas signature-t kapok-e
Nem tartom jó ötletnek, hogy te próbálod meg összerakni a base_stringet. Nem csak az oauth_signature-t kell kihagyni, hanem a kulcsok szerint rendezni is kell a GET/POST paramétereket, és ha egy értékhez több paraméter is van, akkor azoknál érték szerint is kell csinálni egy rendezést - az OAuth szabvány szerint. Az egészet nem az iWiW találta ki, hanem egy teljesen szabványos dolog lett megvalósítva. A szabvány leprogramozása viszont nem könnyű és triviális…
Nagyon alapos, jó leírás, ez alapján sikerült működésre bírnom Python (Google App Engine) környezetben. A szerveroldali kód itt (webapp használata esetén):
import ouath # Másoljuk a http://oauth.googlecode.com/svn/code/python/oauth/oauth.py fájlt a könyvtárba
#…
def get(self):
self.response.headers['Content-Type'] = ‘text/html’
request = oauth.OAuthRequest.from_request(’GET’, self.request.url, headers=self.request.headers, query_string=self.request.query_string)
consumer = oauth.OAuthConsumer(self.request.get(’oauth_consumer_key’), ‘teszt kulacs’)
signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
signature_valid = signature_method.check_signature(request, consumer, None, self.request.get(’oauth_signature’))
if signature_valid == True:
self.response.out.write(’Hello ‘+self.request.get(’param’)+’!')
else:
self.response.out.write(’Sikertelen hitelesites’)
#…
Köszönjük a többi Pythonos nevében is.
andras, devblogger: a rubys probléma megoldását, és további tippeket lásd a Virgo Undercode blogon: http://www.virgo.hu/undercode/2009/01/gadgetfejlesztes-rubyval-gadgeteer/