O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Limite

Há uma limitação no LinkedIn — Limite de uso comercial. É muito provável que você, assim como eu até pouco tempo atrás, nunca tenha se deparado e nem ouvido sobre isso.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

A essência do limite é que, se você usar a busca de pessoas fora dos seus contatos com muita frequência (não há métricas exatas, isso é decidido pelo algoritmo com base em suas ações — com que frequência e quanto você buscou e adicionou pessoas), o resultado da busca será limitado a três perfis, ao invés de 1000 (por padrão 100 páginas, com 10 perfis por página). O limite é redefinido no início de cada mês. Naturalmente, contas premium não têm essa limitação.

Mas não faz muito tempo, para um projeto pessoal, comecei a brincar muito com a busca no LinkedIn e de repente me deparei com essa limitação. Naturalmente, eu não gostei disso, pois não estava usando para fins comerciais, então meu primeiro pensamento foi estudar a limitação e tentar contorná-la.

[Importante esclarecer — os materiais neste artigo são apresentados exclusivamente para fins informativos e educacionais. O autor não incentiva seu uso para fins comerciais.]

Estudando o problema

Temos: em vez de dez perfis com paginação, a busca retorna apenas três, após os quais aparece um bloco com a “recomendação” de conta premium e abaixo estão perfis borrados e não clicáveis.

Automaticamente, minha mão vai para o console do desenvolvedor, para ver esses perfis ocultos — talvez possamos remover alguns estilos, que criam o borrão, ou extrair informações do bloco no markup. Mas, como era de se esperar, esses perfis são apenas imagens de placeholder e não armazenam informações.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Ótimo, agora vamos olhar na aba Network e verificar se realmente está ocorrendo uma emissão alternativa de resultados de busca, retornando apenas três perfis. Encontramos a consulta de nosso interesse a “/api/search/blended” e olhamos para a resposta.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Os perfis vêm em um array `included`, mas há 15 entidades nele. Neste caso, os três primeiros são objetos com informações adicionais, cada objeto contém informações sobre um perfil específico (por exemplo, se o perfil é premium).

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Os 12 seguintes são perfis reais — resultados de busca, dos quais apenas três serão mostrados. Como já se pode imaginar, só são exibidos aqueles para os quais há informações adicionais (os primeiros três objetos). Por exemplo, ao pegar a resposta de um perfil sem limite, chegam 28 entidades — 10 objetos com informações adicionais e 18 perfis.

Resposta para perfil sem limiteO escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.
O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Por que chegam mais de 10 perfis, embora solicitemos exatamente 10, e eles não participam de forma alguma na exibição, nem mesmo na próxima página, não sei ainda. Se analisarmos a URL da solicitação, podemos ver que count=10 (quantos perfis devolver na resposta, no máximo 49).

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Ficarei feliz com qualquer comentário sobre isso.

Estamos experimentando

Bem, o mais importante é que agora sabemos — os perfis que chegam na resposta são mais do que os que vemos. Isso significa que podemos obter mais dados, apesar do limite. Vamos tentar chamar a API nós mesmos, diretamente do console, usando fetch.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Como esperado, recebemos um erro, 403. Isso está relacionado à segurança, aqui não enviamos o token CSRF (CSRF na Wikipédia. Em poucas palavras — a cada solicitação, um token exclusivo é adicionado, que é verificado no servidor quanto à autenticidade).

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Ele pode ser copiado de qualquer outra solicitação bem-sucedida ou dos cookies, onde é armazenado no campo ‘JSESSIONID’.

Onde encontrar o tokenCabeçalho de outra solicitação:

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Ou dos cookies, diretamente através do console:

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Tentamos novamente, desta vez passamos nas configurações do fetch, nas quais indicamos o parâmetro no cabeçalho nosso csrf-token.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Sucesso, recebemos todos os 10 perfis. :tada:

Devido à diferença nos cabeçalhos, a estrutura da resposta é um pouco diferente da que chega na solicitação original. É possível obter a mesma estrutura se adicionarmos ‘Accept: ‘application/vnd.linkedin.normalized+json+2.1’ ao nosso objeto, ao lado do token csrf.
Exemplo de resposta com cabeçalho adicionadoO escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Mais sobre o cabeçalho Accept

E agora?

Em seguida, podemos editar (manualmente ou automatizar) o parâmetro `start`, que indica o índice a partir do qual receberemos 10 perfis (por padrão = 0) de todo o resultado da busca. Em outras palavras, ao incrementar isso em 10 após cada solicitação, obtemos uma exibição paginada normal, com 10 perfis por vez.

Neste estágio, eu tinha dados e liberdade suficientes para continuar trabalhando no meu projeto pessoal. Mas era um pecado não tentar exibir esses dados diretamente no local, já que estavam em mãos. Não vamos mexer no Ember, que é usado na parte frontal. O site tinha o jQuery conectado e, relembrando o básico da sintaxe, é possível criar o seguinte em poucos minutos.

Código em 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="/pt/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="/pt/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>`
 );
};

// chamando a API, obtendo dados e renderizando perfis
const fetchProfiles = () =&gt; {
 // token
 const csrf = 'ajax:9082932176494192209';
 
 // objeto com configurações da requisição, passando o token
 const settings = { headers: { 'csrf-token': csrf } }

 // URL da requisição, com índice dinâmico de início no final
 const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&amp;filters=List(geoRegion-jp0,network-S,resultType-PEOPLE)&amp;origin=FACETED_SEARCH&amp;q=all&amp;queryContext=List(spellCorrectionEnabled-true,relatedSearchesEnabled-true)&amp;start=${nextItemIndex}`; 
 /* fazemos a requisição, para cada perfil na resposta chamamos renderizar bloco, e depois incrementamos o índice inicial em 10 */
 fetch(url, settings).then(response =&gt; response.json()).then(data =&gt; {
 data.elements[0].elements.forEach(createProfileBlock);
 nextItemIndex += 10;
});
};


// removendo todos os perfis da lista
$('.search-results__list').find('li').remove();
// inserindo botão de carregar perfis
$('.search-results__list').after('<button id="load-more">Carregar Mais</button>');
// adicionamos funcionalidade ao botão
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);

// definimos o índice do perfil para a requisição como padrão
window.nextItemIndex = 0;

Se você executar isso diretamente no console da página de busca, ele adicionará um botão que carrega 10 novos perfis a cada clique, renderizando-os em uma lista. Claro, o token e a URL devem ser alterados conforme necessário. O bloco de perfil conterá o nome, cargo, localização, link para o perfil e uma imagem de espaço reservado.

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Conclusão

Assim, com o mínimo de esforço, conseguimos encontrar uma vulnerabilidade e restaurar a busca sem limitações. Bastou analisar os dados e seu caminho, e dar uma olhada na própria solicitação.

Não posso dizer que isso representa um problema sério para o LinkedIn, pois não oferece nenhuma ameaça. No máximo, é uma receita perdida devido a "desvios" desse tipo, que permitem não pagar pelo premium. Talvez essa resposta do servidor seja necessária para o funcionamento correto de outras partes do site, ou talvez seja apenas preguiça dos desenvolvedores, uma falta de recursos que impede um bom trabalho. (A limitação surgiu em janeiro de 2015; antes disso, não havia limites).

P.S.

Naturalmente, o código em jQuery é um exemplo bastante primitivo das possibilidades. Neste momento, criei uma extensão para o navegador para minhas necessidades. Ela adiciona botões de controle e renderiza perfis completos com imagens, botão de convite e conexões em comum. Além disso, coleta dinamicamente filtros de localização, empresas e outros, obtendo o token dos cookies. Assim, não é mais necessário hardcodar nada. Também adiciona campos adicionais de configuração, como "quantos perfis solicitar de cada vez, até 49".

O escritório de advocacia Tycko & Zavareei entrou com uma ação judicial relacionada a.

Ainda estou trabalhando nesta extensão e planejo disponibilizá-la publicamente. Escreva se você estiver interessado.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster