Grænse
Der er en sådan begrænsning på LinkedIn — . Det er meget sandsynligt, at du, ligesom jeg indtil for nylig, aldrig har stødt på eller hørt om det.

Essensen af grænsen er, at hvis du søger efter folk uden for dine kontakter for ofte (der er ingen præcise målinger, det bestemmer algoritmen baseret på dine handlinger — hvor ofte og hvor meget du har søgt og tilføjet folk), så vil søgeresultatet være begrænset til tre profiler i stedet for 1000 (som standard 100 sider med 10 profiler pr. side). Grænsen nulstilles i begyndelsen af hver måned. Naturligvis, .
Men for ikke så længe siden, for et lille projekt, begyndte jeg at lege meget med søgningen på LinkedIn og pludselig fik jeg denne begrænsning. Naturligvis var jeg ikke så begejstret, da jeg ikke brugte det til kommercielle formål, så den første tanke var at undersøge begrænsningen og forsøge at omgå den.
[Vigtig præcisering — materialerne i artiklen præsenteres udelukkende til informations- og uddannelsesformål. Forfatteren tilskynder ikke deres brug til kommercielle formål.]
Vi undersøger problemet
Vi har: i stedet for ti profiler med pagination, viser søgningen kun tre, hvorefter der indsættes en sektion med 'anbefalingen' af premium-kontoen, og nedenunder kommer slørede og ikke-klikkelige profiler.
Hånden strækker sig straks mod udviklerkonsollen for at se disse skjulte profiler — måske kan vi fjerne nogle stilarter, der skaber sløret, eller udtrække oplysninger fra sektionen i markup. Men, ganske forventeligt, er disse profiler kun og indeholder ingen information.

Godt, lad os nu kigge i fanen Netværk og se, om den alternative søgeresultatafgivelse, som kun returnerer tre profiler, faktisk fungerer. Vi finder den interessante anmodning til '/api/search/blended' og ser på svaret.

Profilerne kommer i arrayet `included`, men der er hele 15 enheder i det. I dette tilfælde er de første tre af dem objekter med yderligere information, hvert objekt indeholder information om en bestemt profil (f.eks. om profilen er en premium).

De følgende 12 er rigtige profiler - søgeresultater, hvoraf vi kun vil vise tre. Som man allerede kan gætte, vises kun dem, der modtager yderligere information (de første tre objekter). For eksempel, hvis vi tager svaret med en profil uden begrænsning, vil der komme 28 entiteter - 10 objekter med yderligere information og 18 profiler.
Svar for profil uden begrænsning

Hvorfor er der flere profiler, der kommer, end de 10, der anmodes om, og de deltager ikke på nogen måde i visningen, selv ikke på den næste side - det ved jeg ikke endnu. Hvis man analyserer URL-forespørgslen, kan man se, at count=10 (hvor mange profiler der returneres i svaret, maksimum 49).
Jeg er glad for enhver kommentar om dette.
Vi eksperimenterer
Godt, det vigtigste er, at vi nu ved med sikkerhed - der kommer flere profiler i svaret, end vi ser. Det betyder, at vi kan få flere data, på trods af begrænsningen. Lad os prøve at kalde API'et selv, direkte fra konsollen, ved hjælp af fetch.

Som ventet får vi en fejl, 403. Dette er relateret til sikkerheden, vi sender ikke CSRF-tokenet (). Kort sagt - til hver anmodning tilføjes et unikt token, som verificeres på serveren for ægthed).

Det kan kopieres fra enhver anden vellykket anmodning eller fra cookies, hvor det opbevares i feltet 'JSESSIONID'.
Hvor man kan finde tokenetOverskriften for en anden anmodning:

Eller fra cookies, direkte via konsollen:

Lad os prøve igen, denne gang sender vi indstillingerne i fetch, hvor vi angiver vores csrf-token i headeren som parameter.

Succes, vi modtager alle 10 profiler. :tada:
På grund af forskellen i overskrifter er strukturen af svaret lidt anderledes end det, der modtages i den originale anmodning. Man kan få den samme struktur, hvis man tilføjer 'Accept: 'application/vnd.linkedin.normalized+json+2.1' til vores objekt, sammen med csrf-tokenet.
Eksempel på svar med den tilføjede header
Hvad nu?
Dernæst kan man redigere (manuelt eller automatisere) parameteren `start`, som angiver indekset, hvorfra vi vil få 10 profiler (standard = 0) fra det samlede søgeresultat. Med andre ord, ved at inkrementere det med 10 efter hver anmodning, får vi en almindelig pagineret visning, 10 profiler ad gangen.
På dette punkt havde jeg tilstrækkeligt med data og frihed til at fortsætte arbejdet med mit pet-projekt. Men det ville være synd ikke at prøve at vise disse data direkte på siden, nu hvor de var til stede. Vi vil ikke dykke ind i Ember, som bruges på fronten. Der var tilsluttet jQuery på siden, og ved at grave i hukommelsen af grundlæggende syntaks kan man på et par minutter skabe følgende.
Koden i jQuery
/* рендер блока, принимаем данные профиля и вставляем блок в список профилей используя эти данные */
const createProfileBlock = ({ headline, publicIdentifier, subline, title }) => {
$('.search-results__list').append(
`<li class="search-result search-result__occluded-item ember-view">
<div class="search-entity search-result search-result--person search-result--occlusion-enabled ember-view">
<div class="search-result__wrapper">
<div class="search-result__image-wrapper">
<a class="search-result__result-link ember-view" href="/da/in/${publicIdentifier}/">
<figure class="search-result__image">
<div class="ivm-image-view-model ember-view">
<img class="lazy-image ivm-view-attr__img--centered EntityPhoto-circle-4 presence-entity__image EntityPhoto-circle-4 loaded" src="http://www.userlogos.org/files/logos/give/Habrahabr3.png" />
</div>
</figure>
</a>
</div>
<div class="search-result__info pt3 pb4 ph0">
<a class="search-result__result-link ember-view" href="/da/in/${publicIdentifier}/">
<h3 class="actor-name-with-distance search-result__title single-line-truncate ember-view">
${title.text}
</h3>
</a>
<p class="subline-level-1 t-14 t-black t-normal search-result__truncate">${headline.text}</p>
<p class="subline-level-2 t-12 t-black--light t-normal search-result__truncate">${subline.text}</p>
</div>
</div>
</div>
<li>`
);
};
// vi henter api, får data og renderer profiler
const fetchProfiles = () => {
// token
const csrf = 'ajax:9082932176494192209';
// objekt med indstillinger for forespørgslen, vi sender token
const settings = { headers: { 'csrf-token': csrf } }
// url for forespørgslen, med en dynamisk startindeks i slutningen
const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&filters=List(geoRegion-jp0,network-S,resultType-PEOPLE)&origin=FACETED_SEARCH&q=all&queryContext=List(spellCorrectionEnabled-true,relatedSearchesEnabled-true)&start=${nextItemIndex}`;
/* laver forespørgslen, for hver profil i svaret kalder vi render-blokken, og incrementerer startindekset med 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// fjerner alle profiler fra listen
$('.search-results__list').find('li').remove();
// indsætter knappen til indlæsning af profiler
$('.search-results__list').after('<button id="load-more">Indlæs flere</button>');
// tilføjer funktionaliteten til knappen
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// sætter standardprofilindekset for forespørgslen
window.nextItemIndex = 0;
Hvis dette udføres direkte i konsollen på søgeresultatsiden, vil det tilføje en knap, der indlæser 10 nye profiler ved hvert klik og viser dem på listen. Selvfølgelig skal token og URL ændres til det nødvendige. Profildelen vil indeholde navn, stilling, placering, link til profilen og et pladsholderbillede.

Konklusion
Dermed, med minimal indsats, kunne vi finde et sårbart punkt og få ubegrænsede søgeresultater tilbage. Det var nok at analysere dataene og deres sti, kigge ind i selve forespørgslen.
Jeg kan ikke sige, at dette er et alvorligt problem for LinkedIn, da det ikke udgør nogen trussel. Maksimalt er det tabt indtjening på grund af sådanne 'omgange', der gør det muligt ikke at betale for premium. Måske er et sådant serverrespons nødvendigt for at de øvrige dele af siden fungerer korrekt, eller også er det blot udviklernes dovenskab og mangel på ressourcer, der forhindrer en bedre løsning. (Begrænsningen kom i januar 2015, inden da var der ikke nogen grænse).
P.S.
Naturligvis er koden i jQuery et ret primitivt eksempel på mulighederne. I øjeblikket har jeg lavet et browserextension til mine behov. Det tilføjer kontrolknapper og renderer komplette profiler med billeder, invitationsknap og fælles forbindelser. Plus, det indsamler dynamisk filtre til placeringer, virksomheder og mere, og henter token fra cookies. Så der er ikke længere behov for at hardkode noget. Og det tilføjer ekstra indstillingsfelter, såsom 'hvor mange profiler der skal anmodes om ad gangen, op til 49'.

Jeg arbejder stadig på dette supplement og planlægger at gøre det offentligt tilgængeligt. Skriv, hvis du er interesseret.
Kilde: habr.com


