Gräns
Det finns en sådan begränsning på LinkedIn - . Det är mycket sannolikt att du, precis som jag fram till nyligen, aldrig har stött på eller hört talas om det.

Poängen med begränsningen är att om du använder personlig sökning för människor utanför dina kontakter för ofta (det finns inga exakta mått, algoritmen bestämmer baserat på dina handlingar - hur ofta och mycket du har sökt och lagt till människor), så kommer sökresultatet att begränsas till tre profiler istället för 1000 (vanligtvis 100 sidor med 10 profiler per sida). Begränsningen återställs i början av varje månad. Naturligtvis, .
Men inte så länge sedan började jag leka mycket med sökningen på LinkedIn för ett litet projekt och plötsligt fick jag denna begränsning. Naturligtvis tyckte jag inte om det, eftersom jag inte använde det för några kommersiella syften, så min första tanke var att studera begränsningen och försöka kringgå den.
[En viktig påpekande - materialet i artikeln är endast avsedda för informativa och utbildande ändamål. Författaren uppmuntrar inte deras användning för kommersiella syften.]
Studera problemet
Vi har: istället för tio profiler med paginering visar sökningen endast tre, efter vilka en ruta med "rekommendationer" för premiumkonto visas, och nedanför finns suddiga och icke-klickbara profiler.
Nästan omedelbart sträcker sig handen efter utvecklarkonsolen för att se dessa dolda profiler - kanske kan vi ta bort några stilar som sätter suddighet, eller hämta information från rutan i markupen. Men, som väntat, är dessa profiler bara och lagrar ingen information.

Bra, nu kollar vi fliken Nätverk och ser om det verkligen fungerar med det alternativa sökresultatet som returnerar endast tre profiler. Vi letar upp den intressanta förfrågan till “/api/search/blended” och tittar på svaret.

Profilerna kommer i arrayen `included`, men det finns hela 15 entiteter i den. I det här fallet är de tre första objekten med ytterligare information, varje objekt innehåller information om en specifik profil (till exempel om profilen är en premium).

De följande 12 är riktiga profiler - sökresultat, av vilka vi endast får se tre. Som man redan kan gissa, visar det endast de som får mer information (de tre första objekten). Till exempel, om man tar svaret från en profil utan begränsning, kommer 28 entiteter - 10 objekt med ytterligare information och 18 profiler.
Svar för profil utan begränsning

Varför kommer fler profiler än 10, även om det begärs exakt 10, och de deltar inte alls i visningen; de kommer inte att finnas på nästa sida heller - jag vet fortfarande inte. Om man analyserar URL:en i förfrågan kan man se att count=10 (hur många profiler som ska returneras i svaret, max 49).
Jag ser fram emot alla kommentarer om detta.
Vi experimenterar
Bra, det viktigaste vi nu vet är att fler profiler kommer i svaret än vi får se. Det betyder att vi kan hämta mer data, trots begränsningen. Låt oss försöka kalla API:et själva, direkt från konsolen, med hjälp av fetch.

Som förväntat får vi ett fel, 403. Detta är relaterat till säkerhet, här skickar vi inte CSRF-tokenet (. Om vi ska sammanfatta – en unik token läggs till varje förfrågan, som valideras på servern för äkthet).

Den kan kopieras från vilken annan framgångsrik förfrågan som helst eller från cookies där den lagras i fältet ‘JSESSIONID’.
Var hittar man tokenRubrik för en annan förfrågan:

Eller från cookies, direkt via konsolen:

Vi provar igen, denna gång skickar vi in inställningar i fetch, där vi anger vår csrf-token som en parameter i header.

Framgång, vi får alla 10 profiler. :tada:
På grund av skillnader i rubriker är strukturera svaret något annorlunda än vad som kommer i den ursprungliga förfrågan. Man kan få samma struktur om man lägger till 'Accept: 'application/vnd.linkedin.normalized+json+2.1', i vårt objekt, bredvid csrf-token.
Exempel på svar med tillagd rubrik
Vad händer härnäst?
Därefter kan man redigera (manuellt eller automatisera) parametern `start`, som anger indexet från vilket vi får 10 profiler (standard = 0) från hela sökresultatet. Med andra ord, om man ökar det med 10 efter varje förfrågan får vi en vanlig sidindelning, med 10 profiler per gång.
I det här skedet hade jag tillräckligt med data och frihet för att fortsätta arbetet med projektet. Men det vore synd att inte försöka visualisera dessa data på plats, nu när de är tillgängliga. Vi kommer inte att gå in i Ember, som används på framsidan. jQuery var kopplat på webbplatsen, och med lite grundläggande syntax från minnet kan man på några minuter skapa följande.
Kod på 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="/sv/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="/sv/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>`
);
};
// дергаем апи, получаем данные и рендерим профили
const fetchProfiles = () => {
// токен
const csrf = 'ajax:9082932176494192209';
// объект с настройками запроса, передаем токен
const settings = { headers: { 'csrf-token': csrf } }
// урл запроса, с динамическим индексом старта в конце
const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&filters=List(geoRegion-%3Ejp%3A0,network-%3ES,resultType-%3EPEOPLE)&origin=FACETED_SEARCH&q=all&queryContext=List(spellCorrectionEnabled-%3Etrue,relatedSearchesEnabled-%3Etrue)&start=${nextItemIndex}`;
/* делаем запрос, для каждого профиля в ответе вызываем рендер блока, и после инкрементируем стартовый индекс на 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// удаляем все профили из списка
$('.search-results__list').find('li').remove();
// вставляем кнопку загрузки профилей
$('.search-results__list').after('<button id="load-more">Ladda fler</button>');
// lägger till funktionalitet för knappen
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// sätter som standard profilindex för förfrågan
window.nextItemIndex = 0;
Om du kör detta direkt i konsolen på söksidan kommer det att lägga till en knapp som laddar 10 nya profiler vid varje knapptryckning och renderar dem som en lista. Självklart bör token och URL ändras till de som behövs. Profilblocket kommer att innehålla namn, titel, plats, länk till profilen och en placeholder-bild.

Slutsats
På så sätt kunde vi med minimal insats hitta en sårbar punkt och återfå sökfunktionen utan begränsningar. Det räckte med att analysera data och deras väg, samt att titta på själva begäran.
Jag kan inte säga att detta är ett allvarligt problem för LinkedIn, eftersom det inte medför någon hotbild. Maximal påverkan är förlorad intäkt på grund av sådana "omvägar" som gör att man kan undvika att betala för premiumtjänster. Det är möjligt att ett sådant serversvar är nödvändigt för att andra delar av webbplatsen ska fungera korrekt, eller så är det bara lathet från utvecklarna och brist på resurser som gör att det inte kan göras bra. (Begränsningen infördes i januari 2015, tidigare fanns inget tak).
P.S.
Naturligtvis är koden för jQuery ett ganska primitivt exempel på möjligheterna. Just nu har jag skapat en webbläsartillägg för mina egna behov. Den lägger till kontrollknappar och renderar kompletta profiler med bilder, inbjudningsknapp och gemensamma kontakter. Dessutom samlar den dynamiskt in filter för platser, företag och mer, och hämtar token från cookies. Så det behövs ingen hårdkodning längre. Den lägger också till ytterligare inställningsfält, som "hur många profiler som ska begäras åt gången, upp till 49".

Jag arbetar fortfarande med detta tillägg och planerar att släppa det offentligt. Hör av dig om du är intresserad.
Källa: habr.com


