2011-09-21 8 views
9

Pracuję z pewnym kodem, który musi być uruchamiany jako strona, a jeśli jest uruchamiany jako Chrome Extension, chcę móc wykonywać dodatkowe czynności. Co używam to:Wykrywanie, czy kod jest uruchamiany jako rozszerzenie Chrome

<script> 
if (chrome && chrome.extension) { 
    // extension stuff 
} 
</script> 

To wydaje się dobrym capabilitydetection. Używanie ciągu agenta użytkownika sprawia mi kłopot, ponieważ jest taki sam, niezależnie od kontekstu (strona internetowa a rozszerzenie).

Pytanie: czy istnieją inne, bardziej niezawodne techniki wykrywania, czy fragment kodu działa w rozszerzeniu Chrome?

Aktualizacja: Zastanawiam się, czy jest coś, co mogę umieścić w moim pliku manifest.json, które można następnie odczytać z powrotem. Zauważ, że rozszerzenie, nad którym pracuję, nie jest pomyślane jako trwała rzecz, która działa przez cały czas, jest to aplikacja do obsługi treści, która działa w jednym oknie lub na karcie przeglądarki i nie ma potrzeby interakcji z innymi oknami lub kartami lub czymkolwiek innym. .

+1

You co Staraj się zdobyć umiędzynarodowione łańcuchy, chociaż sprawdzenie statusu rozszerzenia nie jest jego przeznaczeniem: http://code.google.com/chrome/extensions/i18n.html –

Odpowiedz

18

Tak wiele skomplikowanych odpowiedzi e, podczas gdy można łatwo wykryć, czy używasz w rozszerzeniu Chrome przez sprawdzanie istnienia i nie-pustki chrome.runtime.id:

if (window.chrome && chrome.runtime && chrome.runtime.id) { 
    // Code running in a Chrome extension (content script, background page, etc.) 
} 
+1

Doskonały punkt. 'chrome.runtime' zostało wydane w ramach Chrome 22 (https://developer.chrome.com/extensions/runtime#property-id) wydanego 25 września 2012 r. (http://googlechromereleases.blogspot.com/2012/09 /stable-channel-update_25.html) ... rok po opublikowaniu pytania i zaakceptowanej odpowiedzi. Aktualizuję zaakceptowaną odpowiedź. Dzięki za ekshumację! – artlung

+0

w obu przypadkach warunek jest prawdziwy, Albo sprawdzamy od wewnątrz wyskakującego okienka lub w widoku strony przeglądarki. –

4

Pragmatycznie to dobre podejście. Teoretycznie (nie wiem, czy jest to istotne, czy nie, np. Może zawierać luki), można je bardzo łatwo sfałszować. Przypuszczam, że to zależy od twojego kontekstu, jaki to ma znaczenie.

Oto nieco mocniejszy pomysł:

if (chrome && 
    chrome.windows && 
    chrome.windows.get && 
    typeof chrome.windows.get === 'function' && 
    chrome.windows.get.toString() === 'function get() { [native code] }') 

Pomysł jest takie same, choć jest nieco silniejszy, ponieważ AFAIK mając obiekt będzie funkcją io to toString() wartość ma ta wartość jest niemożliwe, ponieważ jest to niepoprawna składnia, więc nawet próba sfałszowania tej wartości nie zadziała, chyba że zmieniono kod natywny (który wymaga zupełnie innego poziomu hakera).

Nie od razu pamiętaj, że sprawdzanie takich rzeczy wymaga uprawnień, ale nie jest to jasne, mam nadzieję.

UPDATE

zdałem sobie sprawę, że "native code" składnia pomysł może się nabrać, przez aliasing istniejącą funkcję. Na przykład.

var FakeFn = Object.create; 
FakeFn.toString(); // "function create() { [native code] }" 

Ale można to załatwić przez staranny wybór, której funkcji używamy, ponieważ nazwa pojawia się w ciągu znaków. get jest prawdopodobnie zbyt powszechny, ale jeśli weźmiemy niejasną nazwę funkcji (np. captureVisibleTab z), która jest zaimplementowana tylko w rozszerzeniach chrome, to nadal jest bardzo przenośnym rozwiązaniem, ponieważ w przeciwieństwie do podstawowego sprawdzenia, gdzie kod może zostać oszukany przez innego użytkownika lokalnego kod, wiadomo z góry, że przeglądarki nie implementują żadnych natywnych funkcji o tej nazwie, więc jest nadal bezpieczny we wszystkich przeglądarkach i ze wszystkimi kodami użytkownika.

UPDATE

Jak @Mathew wskazał, ten pomysł jest foolable (choć pozornie tylko złośliwie). Pomyślałem, że mogę rozwiązać problem przez porównanie z Function.prototype.toString, ale doszedłem do wniosku, że nawet to można zmylić, aliasingując oryginalną metodę toString i tworząc nową, która dla pewnych funkcji zwraca fałszywe ciągi, a dla innych zwraca oryginalny ciąg.

Podsumowując, mój pomysł jest nieco silniejszy niż oryginał, ponieważ wyklucza praktycznie wszelką szansę przypadkowego zderzenia (nieco więcej niż idea PO), ale z pewnością nie jest obroną przed złośliwym atakiem, tak jak początkowo sądziłem może być.

+0

Ciekawe i działa zgodnie z oczekiwaniami, ale nie jestem przekonany jest bardziej przenośny niż mój. Zastanawiam się, czy jest coś, co mogę włożyć do mojego pliku 'manifest.json', ale mogę go jakoś odczytywać. Dzięki za dzwonienie. – artlung

+1

@artlung, każde takie podejście jest mniej przenośne niż moje, pozwól mi wyjaśnić: Cały interfejs API rozszerzenia (AFAIK, jestem wprawdzie niezbyt ekspertem od API) jest oparty na javascript. Wszystkie połączenia będą więc zgodne ze standardami JS i przyjmą zwykłą formę "obj.ref.fn()", która z definicji może zostać oszukana. Powodem, dla którego moje podejście jest bardziej przenośne, jest to, że nie da się go oszukać. Nie możesz utworzyć obiektu, który przejdzie przez te warunki, chyba że sam sobie poradzisz z przeglądarką, podczas gdy ja mogę łatwo utworzyć 'window.chrome = {extension: true}' w dowolnej przeglądarce, a twój kod będzie uważał, że jest to rozszerzenie chrome. – davin

+0

Doskonały punkt. Myślę, że moim zamiarem nie jest sztywne egzekwowanie nie-spoofingu, ale po prostu rozróżnianie prostego korzystania z Chrome (i wielu wariantów) i kodowanych ładowanych jako część extenson. Dziękuję bardzo za staranne przemyślenie tego! – artlung

2

Wiem, że jest stary, ale pomyślałem, że zaproponuję alternatywę. Możesz dodać inny plik javascript do rozszerzenia chrome, więc zawiera dwa pliki .js. manifest.json musiałaby:

"js": ["additionalscript.js", "yourscript.js"] 

W additionalscript.js może po prostu zadeklarować zmienną var isextension = true. yourscript.js może sprawdzić typeof isextension != 'undefined'.

Ale może bardziej interesujący przykład, może zadeklarować

var adminpassword='letmein' 

Teraz yourscript.js tylko ma dostęp do adminpassword gdy uruchomiony w rozszerzeniu.

(oczywiście nie byłoby umieścić hasło w pliku skryptu, jeśli wtyczki były na maszynie, które mogą być zagrożone)

+0

Myślę, że masz na myśli "skrypty", a nie "js". Zawsze chciałbyś zrobić test jako 'typeof isextension! = 'Undefined'' lub otrzymasz wyjątek JavaScript, gdy plik additionalalscript.js nie został wykonany. Należy również zauważyć, że znajduje się on w sekcji "manifest" w tle. –

+0

Dzięki @DanMcGrath - Mam sekcję "content_scripts" pliku manifest.json, która ma wpis "js": "content_scripts": [ { "mecze": ["*: //*.google.com/* "], " js ": [" One.js "," Two.js "] } ]. I sprawdzanie poprawności dla niezdefiniowanych –

1

zauważyłem, że w Chrome obiektu chromu, który jest własnością globalnej oknie obiekt, nie można usunąć. Jeśli jest to właściwość zdefiniowana przez użytkownika, operacja usuwania powiodła się. Więc można sprawdzić w ten sposób:

var isRunningInExtension = (!(delete window.chrome) && chrome.extension) ? 
          true : false; 

UPDATE: Linia powyżej naprawdę nie ma gwarancji, że kod działa w chrome rozszerzenia. Każdy może utworzyć obiekt o nazwie "chrome" z właściwością "extension", a następnie zamrozić/uszczelnić ten obiekt - wystarczy, że przejdzie on przez kontrolę i otrzyma niepoprawny wynik, że kod jest uruchomiony wewnątrz rozszerzenia chrome.

Upewnij się, że korzystasz z kodu w rozszerzeniu, musisz przetestować globalny obiekt chrome przed uruchomieniem obsługi javascript - w ten sposób uzyskasz gwarancję, że przed testowaniem nie zostanie utworzony fałszywy obiekt chrome.

Jednym z możliwych rozwiązań jest użycie elementu iframe - w poniższym przykładzie używam właściwości piaskownicy iframe, aby polecić iframe, aby nie wykonywał żadnych skryptów (nawet skryptów dołączonych do znacznika skryptu) - w ten sposób zapewniam, że żaden skrypt nie będzie możliwość modyfikowania globalnego obiektu window.chrome.

(function() { 
    var isChromeExtension = (function() { 
    var contentWindow, 
     iframe = document.createElement("iframe"), 
     isChromeExtension; 
    // test for sandbox support. It is supported in most recent version of Chrome 
    if ("sandbox" in iframe) { 
     try { 
     iframe.sandbox = "allow-same-origin"; 
     iframe.src=location.href; 
     iframe.style="display: none"; 
     document.body.appendChild(iframe); 
     contentWindow = iframe.contentWindow; 
     isChromeExtension = !!(contentWindow.chrome && contentWindow.chrome.extension); 
     document.body.removeChild(iframe); 
     } catch(e) {} 
    } 
    return isChromeExtension; 
    }()); 
}()); 

wynik mógłby być:

  • prawda - jeśli kod jest uruchomiony wewnątrz rozszerzenie Chrome
  • false - jeśli kod nie działa wewnątrz rozszerzenie Chrome
  • niezdefiniowana - jeśli przeglądarka nie obsługuje piaskownicy dla elementów iframe lub wystąpił błąd podczas testu
+0

Nie jest dla mnie jasne, że twoje rozumienie działania 'delete' działa tak, jak mówisz. Mogę również utworzyć obiekt, powiedz 'window.g = {x: 1}' i uruchom usuwanie na nim i uzyskaj podobny wynik false podczas próby usunięcia obiektu. ... może potrzebny jest pełniejszy przykład tego działania. – artlung

+0

Twoja nowa metoda nie jest zbyt wydajna, a także nie jest bezpieczna. Jeśli środowisko jest naprawdę złośliwe, wówczas każdy obiekt, w tym "dokument", może zostać sfałszowany. Nie ma sposobu, aby się przed tym obronić, dlatego powinieneś wybrać najbardziej czytelną i prostą metodę. –

+0

To naprawdę nie jest wydajne - ładuje całą stronę/style/skrypty do elementu iframe, ale żaden ze skryptów nie zostanie wykonany (wbudowany lub dołączony do znacznika script) - taka jest rola piaskownicy - więc nie ma mowy po stronie klienta, aby zmienić obiekty globalne, a nawet imitować obiekt dokumentu. Należy pamiętać, że obiekt chrome NIE może zostać usunięty, ale każdy może go zastąpić - tak, mogę wykonać następujące czynności: 'window.chrome = {runtime: {id: {}}};' i sugerowana odpowiedź powie, że mój kod działa wewnątrz rozszerzenia, a to nie jest prawda. – ttsvetkov

1

Chrome nie przewiduje żadnego bezpośredniego API, aby sprawdzić stan bieżących aplikacji jest uruchomiony w wyskakującym wyskakującym okienku lub w widoku strony Chrome.Jednak jedna pośrednia sztuczka może zadziałać, możemy dopasować rozdzielczość całego ciała, która jest równa lub mniejsza niż określona w CSS (w tym przypadku wyskakujące okno rozszerzenia będzie otwarte) lub większa (w tym przypadku widok strony internetowej będzie otwarty).

1

Potrzebowałem czegoś podobnego.

Ale nie musiałem przejmować się witrynami próbującymi oszukać kod.

const SCRIPT_TYPE = (() => { 
    if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) { 
     return 'BACKGROUND'; 
    } else if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() !== window) { 
     return 'POPUP'; 
    } else if (!chrome || !chrome.runtime || !chrome.runtime.onMessage) { 
     return 'WEB'; 
    } else { 
     return 'CONTENT'; 
    } 
})(); 

Powyższy powinien wykryć 4 scenariusze

  • JavaScript jest prowadzony na stronie tle
  • JavaScript jest prowadzony na stronie podręcznego
  • javascript prowadzony jest w skrypcie kontekstowego
  • javascript jest uruchamiany bezpośrednio na stronie internetowej
Powiązane problemy