Sessões ativas. Sessões

10.08.2022

Uma sessão (do latim - sessão - reunião, inglês - sessão) é um período de tempo que cobre o trabalho do usuário na Internet desde o momento em que o primeiro link é aberto até o último. Calculado como a diferença de tempo entre as solicitações inicial e final. Porém, a última página pode demorar um tempo diferente para ser visualizada pelo usuário, o que consequentemente torna mais difícil medir o tempo entre duas solicitações.

Como uma sessão está relacionada ao protocolo HTTP e aos COOKIES?

O que é uma sessão pode ser explicado com base no protocolo HTTP. Por si só, este protocolo não tem como salvar o estado entre duas operações. Ou seja, simplesmente, ao abrir uma página e depois passar dela para outra, o HTTP não conseguirá estabelecer que ambas as solicitações pertencem ao mesmo usuário. E aqui um método especial de rastreamento vem em socorro - gerenciamento de sessões (nossas sessões).
Assim, respondendo à questão do que é uma sessão, podemos dizer que se trata de um objeto lógico auxiliar que facilita a transferência de dados entre sucessivas solicitações HTTP de um usuário.
Os cookies, assim como as sessões, armazenam informações sobre o usuário enquanto ele navega no páginas diferentes e melhorar o desempenho do protocolo. Mas, diferentemente do segundo, onde os dados são armazenados em arquivos temporários no servidor, eles os salvam no computador do usuário na forma de pequenos fragmentos.

Para que servem as sessões?

A utilização de sessões torna-se indispensável quando se trabalha com sites como fóruns, fóruns e lojas online, pois neste caso é necessário salvar dados do usuário em diversas páginas.

Estágios da sessão

Toda a sessão pode ser dividida em três etapas:

  • abertura de sessão (quando o usuário começa a trabalhar com um site específico),
  • contabilização de variáveis ​​​​de sessão (ao acessar páginas diferentes),
  • final da sessão.

Devido ao fato de que os dados da sessão são armazenados em servidor de terceiros, então é melhor não armazenar grandes quantidades de informações neles, mas usar cookies.

Para exibir esta página corretamente você precisa de um navegador com suporte a JavaScript.

Sessões de autorização do usuário

Para acessá-lo você precisa seguir o link Usuários ativos No capítulo Administração – Pagina inicial no bloco Usuários(Figura 2); link Usuários ativos No capítulo Administração – Sistema – Licenças em linha Licenças competitivas(Fig. 3) - esta opção só é possível se o sistema ELMA utilizartipo de licenças competitivas ; pressionando um botão Usuários ativos No capítulo Administração - Usuários(Fig. 4).

Arroz. 2. Seção “Administração - Usuários”. Botão Usuários ativos

Arroz. 3. Seção “Administração - Sistema - Licenças”. Link de usuários ativos

Arroz. 4. Seção “Administração - Página inicial”. Link de usuários ativos

Nome de usuário– este campo contém o nome completo. usuário no sistema ELMA. O botão próximo ao nome do usuário permite abortar (desconectar o usuário correspondente) e remover da lista informações sobre todas as sessões deste usuário.

endereço de IP– este campo contém o endereço IP do usuário de quem foi realizada a autorização no sistema. Se o web farm ELMA for usado para trabalho, seus endereços IP reais serão exibidos para cada usuário ativo.

O botão próximo ao endereço IP permite encerrar a sessão correspondente e remover informações sobre ela da lista.

O ícone próximo ao endereço IP significa que o usuário atual está autorizado no sistema. Depois de decorrido um certo tempo desde a última resposta do usuário, a sessão ativa é suspensa. As sessões pausadas são marcadas com um ícone. O período de tempo limite após o qual a sessão ativa é suspensa é definido na seção Administração – Sistema – Configurações do sistema – Configurações de segurança.

Última resposta– este campo contém informações sobre a última ação realizada pelo usuário no sistema ou sobre a última solicitação enviada com sucesso da página ao servidor (uma vez por minuto uma solicitação web é enviada da página aberta no navegador para o servidor ELMA para verifique a conexão e colete estatísticas).

Última ação do usuário– este campo contém informações sobre a última ação realizada pelo usuário no sistema no formato Data – Hora – Link para a última página visitada pelo usuário. Este link é relativo. Para conseguir link completo você precisa adicionar o endereço do servidor a ele (Fig. 5).

Arroz. 5. Formação do endereço completo do link para a última página visitada pelo usuário

Se não houver atividade do usuário por um determinado período, a sessão atual será encerrada automaticamente. As configurações de duração do armazenamento da sessão são definidas na seção

Muitos usuários apreciam a sincronização perfeita entre dispositivos no Telegram. Devido ao fato de os aplicativos nativos deste mensageiro multiplataforma estarem instalados em todos sistemas operacionais(desktop e dispositivos móveis), as pessoas podem fazer login em suas contas em vários dispositivos simultaneamente.

Em uma atualização recente do Telegram, além das visualizações de links, foi criada uma nova seção nas configurações de segurança e privacidade do Telegram. É chamado de "Sessões Ativas".

A seção contém informações sobre o endereço IP do usuário do Telegram e sessões ativas. Todos têm a oportunidade de encerrar sessões não utilizadas, bem como aquelas sessões ativas que parecem suspeitas.

Autorização em duas etapas

Outra inovação também diz respeito à seção de segurança e privacidade. Esta é a autorização no Telegram em duas etapas (autorização em duas etapas).

Ele permite que você crie uma senha que (além da verificação via SMS) será solicitada sempre que você fizer login em um novo dispositivo.

Mas é preciso ter cuidado com isso: se a senha criada for perdida, o dono conta não poderá fazer login em sua própria conta do Telegram a partir de outro gadget.

  • configure a capacidade de recuperar sua senha por e-mail;

O servidor web não mantém conexão permanente com o cliente, e cada solicitação é processada como uma nova, sem qualquer conexão com as anteriores.
Ou seja, você não pode rastrear solicitações do mesmo visitante nem salvar variáveis ​​para ele entre visualizações páginas individuais. Foi para resolver esses dois problemas que as sessões foram inventadas.
Na verdade, as sessões, em poucas palavras, são um mecanismo que permite identificar exclusivamente um navegador e criar um arquivo para esse navegador no servidor no qual as variáveis ​​​​de sessão são armazenadas.

Não descreverei em detalhes a necessidade de tal mecanismo. São casos clássicos, como carrinho de compras em uma loja virtual, autorização, bem como problemas não inteiramente triviais, como proteção de partes interativas de um site contra spam.

Em princípio, é muito fácil criar seu próprio análogo de sessões, não tão funcional quanto o embutido no PHP, mas semelhante em essência. Em cookies e banco de dados.
Ao solicitar um script, verificamos se foi recebido um cookie com um nome específico. Se não houver cookie, configure-o e grave-o no banco de dados nova linha com dados do usuário. Se houver um cookie, lemos os dados do banco de dados. Com outra solicitação excluímos registros antigos do banco de dados e agora temos um mecanismo de sessão pronto. Não é nada difícil. Mas existem algumas nuances que tornam preferível usar o mecanismo de sessão integrado.

Se apenas o primeiro estiver habilitado, então no início da sessão (cada chamada session_start()) um cookie é definido para o cliente. O navegador retorna corretamente esse cookie a cada solicitação subsequente e o PHP possui um identificador de sessão. Os problemas começam se o navegador não retornar cookies. Neste caso, sem receber um cookie com identificador, o PHP sempre iniciará uma nova sessão e o mecanismo não funcionará.

Se apenas o segundo estiver habilitado, o cookie não será definido. E é isso que acontece, por isso, de fato, vale a pena usar o mecanismo de sessão integrado. Depois que o script faz seu trabalho e a página está completamente formada, o PHP verifica a página inteira e adiciona um identificador de sessão a cada link e a cada formulário. Parece algo assim:
Índice torna-se em
Índice
e um campo oculto é adicionado aos formulários

E o navegador, ao clicar em algum link, ou ao clicar em um botão do formulário, enviará na solicitação a variável que precisamos - o identificador da sessão!
Por razões óbvias, o identificador só é adicionado a links relativos.

Teoricamente, em nossas sessões caseiras sobre cookies e banco de dados, você pode atribuir manualmente a transferência de ID a todos os links - e então nosso próprias sessões funcionará independentemente dos cookies. Mas você concorda que é mais agradável quando outra pessoa faz esse trabalho? ;-)

Padrão em versões mais recentes PHP tem ambas as opções habilitadas. Como o PHP lida com isso? Cook é sempre exibido. E os links são preenchidos automaticamente somente se o PHP não detectar um cookie com um identificador de sessão. Quando um utilizador visita o site pela primeira vez durante esta sessão, é colocado um cookie e os links são concluídos. Na próxima solicitação, se os cookies forem suportados, o PHP vê o cookie e para de completar os links. Se os cookies não funcionarem, o PHP continuará adicionando id aos links corretamente e a sessão não será perdida.
Os usuários com cookies ativados verão o link longo com o ID apenas uma vez.

Ufa. A transferência de ID foi concluída.
Agora só falta vincular o arquivo de dados a ele no lado do servidor.
PHP fará isso por nós. Basta apenas escrever
sessão_start();
$_SESSION [ "teste" ]= "Olá mundo!" ;

E o PHP escreverá a variável de teste no arquivo associado a esta sessão.
Há uma observação muito importante aqui.
Variedade $_SESSION- especial.
Na verdade, ele contém as variáveis ​​que queremos disponibilizar em vários scripts.
Para colocar uma variável em uma sessão, basta atribuí-la ao elemento do array $_SESSION.
Para obter seu valor, basta acessar o mesmo elemento. Um exemplo estará abaixo.

O PHP também lida com a coleta de lixo – removendo arquivos desatualizados. Bem como codificação de dados e um monte de outras coisas necessárias. Como resultado desses cuidados, trabalhar com sessões fica muito simples.
Aqui chegamos ao exemplo de como as sessões funcionam.
Um pequeno exemplo:
sessão_start();

eco "Você atualizou esta página". $_SESSION["contador"]++. " uma vez. " ;
eco "
atualizar" ;
?>

Verificamos se temos uma variável contadora na sessão, caso contrário, criamos ela com o valor 0, exibimos seu valor e aumentamos em um. O valor aumentado será gravado na sessão, e na próxima vez que o script for chamado, a variável terá o valor 1, e assim por diante.
Tudo é muito simples.

Para ter acesso às variáveis ​​de sessão em qualquer página do site, você precisa escrever APENAS UMA (!) linha bem no início de CADA arquivo em que precisamos de sessões:
sessão_start();
E então acesse os elementos do array $_SESSION. Por exemplo, uma verificação de autorização seria mais ou menos assim:
sessão_start();
if ($_SESSION ["autorizado"]<> 1 ) {
header("Localização: /auth.php");
saída;
}

Removendo variáveis ​​de uma sessão.
Se você tiver register_globals=off , basta escrever
unset($_SESSION["var"]);
Se não então aproximar Eu preciso escrever com ela
session_unregister("var");

Os erros mais comuns que o PHP produz ao tentar trabalhar com sessões são os seguintes:
Dois deles
Aviso: Não é possível enviar cookie de sessão – cabeçalhos já enviados
Aviso: Não é possível enviar o limitador de cache de sessão – cabeçalhos já enviados

causado pelo mesmo motivo, a solução está descrita neste tópico
Terceiro,
Aviso: open(/tmp\sess_SID, O_RDWR) falhou: Arquivo ou diretório inexistente (2) em full_script_path no número da linha(anteriormente ela parecia Aviso: Falha ao gravar dados da sessão (arquivos). Verifique se a configuração atual de session.save_path está correta (/tmp)),
se traduzido do inglês, explica o problema em detalhes: o caminho para o diretório especificado em php.ini onde os arquivos da sessão são gravados não está disponível. Este erro é o mais fácil de corrigir. Basta registrar um diretório que exista e seja gravável, por exemplo,
session.save_path = c:\windows\temp
E não se esqueça de reiniciar o Apache depois disso.

Acontece que a inteligência humana não tem limites e, portanto, sou forçado a explicar:
uma mensagem sobre o terceiro erro (o diretório não pode ser encontrado) INEVITAVELMENTE levará ao aparecimento dos dois primeiros, uma vez que a mensagem de erro é enviada para o navegador e os cabeçalhos após ela não podem ser usados. Portanto, não se apresse em buscar uma conclusão prematura, mas primeiro anote o caminho certo!

O próximo problema mais comum ao trabalhar com sessões é o legado pesado de register_globals. NÃO forneça nomes de variáveis ​​de script que correspondam aos índices do array $_SESSION!
Com register_globals=on os valores serão sobrescritos e você ficará confuso.
E se register_globals=off, outro erro aparecerá: “Seu script possivelmente depende de um efeito colateral de sessão que existiu até o PHP 4.2.3.”, se o script tiver uma variável de sessão que não tem valor, e uma variável global com o mesmo nome . Para se livrar dele, você deve sempre inicializar as variáveis ​​antes de usá-las (ou pelo menos verificar sua existência) e não dar nomes às variáveis ​​globais que coincidam com os índices do array $_SESSION.

Se não funcionar, mas nenhuma mensagem for exibida, adicione duas linhas logo no início do script responsável por exibir TODOS os erros na tela - é bem possível que haja erros, mas você simplesmente não os vê.
ini_set("display_errors", 1);
relatório_erro(E_ALL);

ou veja erros em error_log. Em geral, o tópico de exibição de mensagens de erro está além do escopo deste artigo, portanto, certifique-se de pelo menos poder vê-las. Você pode ler um pouco mais sobre como encontrar erros nesta seção.

Se você tem certeza que não há erros, mas o exemplo dado não funciona mesmo assim, então talvez o PHP não habilite a passagem de id via URL, e os cookies por algum motivo não funcionam.
Veja o que há de errado com seus cookies.
Em geral, se suas sessões não estão funcionando, primeiro tente passar o identificador da sessão manualmente, ou seja, faça um link e atribua um identificador a ela:
sessão_start();
if (!isset($_SESSION [ "contador" ])) $_SESSION [ "contador" ]= 0 ;
eco "Você atualizou esta página". $_SESSION["contador"]++. " uma vez.

atualizar" ;
?>

Porém, você deve se certificar de que a diretiva session.use_only_cookies não está habilitada, o que impede o PHP de aceitar o ID da sessão caso ele tenha sido passado via URL

Se este exemplo não funcionar, então o problema é trivial erros de digitação(metade dos "problemas" com sessões vem de um nome de variável digitado incorretamente), ou muito versão antiga PHP: o suporte à sessão apareceu na versão 4.0, e o array $_SESSION- em 4.1 (Antes disso era usado $HTTP_SESSION_VARS).
Se funcionar, o problema está nos cookies. Monitore que tipo de cookie o servidor define para o navegador e se o navegador o retorna. É muito útil pesquisar observando a troca de cabeçalhos HTTP entre o navegador e o servidor.
Uma explicação de como funcionam os cookies foge ao escopo deste texto já muito longo, mas pelo menos certifique-se de que o servidor envia um cookie com um identificador e o navegador o retorna. E ao mesmo tempo os identificadores coincidem entre si =)
A configuração do cookie deve ser semelhante a
Conjunto de cookies: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
ou como
Conjunto de cookies: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; caminho=/
(se você estiver solicitando o script fora do diretório raiz)
A resposta do servidor deve ser semelhante a
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
ou
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
se o navegador retornar cookies diferentes do ID da sessão.

Se o navegador não retornar cookies, verifique se os cookies funcionam.
Certifique-se de que o domínio que você está acessando tenha um nome normal (com pelo menos um ponto e sem caracteres ilegais, como sublinhados) e limpe o cache do navegador – esses são dois motivos principais pelos quais os cookies podem não funcionar.

Se o exemplo daqui funcionar, mas o seu próprio código não, então o problema obviamente não está nas sessões, mas no algoritmo. Procure onde você perdeu a variável, transfira o exemplo daqui passo a passo e depure seu script.

Outro problema pode surgir se você usar o redirecionamento de cabeçalho ou a navegação JavaScript.
O fato é que o PHP anexa automaticamente o identificador de sessão apenas a links como , mas não faz isso para cabeçalhos, JavaScript, meta tags.
Portanto, você precisa adicionar o identificador manualmente, por exemplo, assim:
header("Local: /script.php?" . session_name(). "=" . session_id());

Além disso, um problema muito raro, e não está claro de onde ele vem, é que a configuração session.save_handler tem um valor diferente dos arquivos. Se não for assim, corrija.

Segurança
A segurança da sessão é um tópico amplo. Portanto, vou me concentrar em alguns pontos principais.
O mais didático é não passar o identificador pela barra de endereço. Isso está escrito até em php.ini, mas limita a funcionalidade das sessões. Se você decidir seguir este conselho, além de session.use_trans_sid = 0, não se esqueça de session.use_only_cookies = 1
É aconselhável vincular a sessão a um endereço IP: dessa forma, caso o identificador seja roubado, o vilão ainda não conseguirá utilizá-lo na maioria dos casos.
Recomenda-se usar a diretiva session.save_path, que permite definir seu próprio diretório para salvar arquivos de sessão. Isso é mais seguro do que armazená-los no diretório temporário compartilhado padrão do servidor.

Informações adicionais:

  • Além dos cookies, o mecanismo de sessão também envia cabeçalhos que proíbem o cache de páginas (o mesmo limitador de cache). Para HTML isso é correto e necessário. Mas quando você tenta enviar um arquivo usando um script que verifica a autorização, o Internet Explorer se recusa a baixá-lo. É por causa deste título. Chamar
    session_cache_limiter("privado");
    deve resolver o problema antes de iniciar a sessão.
  • Por mais estranho que pareça, mas na matriz $_SESSION Você não pode usar índices numéricos - $_SESSION[1], $_SESSION["10"]- as sessões não funcionarão.
  • Em algum lugar entre as versões 4.2 e 5.0 não foi possível definir session.use_trans_sid usando ini_set(). A partir do 5.0 já é possível novamente.
  • Antes da versão 4.3.3 do cookie, o PHP enviava um cookie apenas se não houvesse identificador na solicitação quando a sessão fosse iniciada. Agora um cookie é enviado em cada chamada session_start()

    Exemplo de autorização usando sessões
    Vamos ilustrar tudo isso com um pequeno exemplo:
    Vamos criar o arquivo auth.php:
    if (isset($_POST ["auth_name"]))
    {
    $sql= "SELECIONE * FROM usuários WHERE nome=?s";
    $row = $db -> getRow($sql, $_POST["auth_name"]);
    if ($row && password_verify ($_POST [ "auth_pass" ], $row [ "pass" ])) (
    $_SESSION [ "user_id" ] = $row [ "id" ];
    }
    header("Local: http://" . $_SERVER [ "HTTP_HOST" ]. $_SERVER [ "REQUEST_URI" ]);
    saída;
    }

    if (isset($_GET [ "ação" ]) AND $_GET [ "ação" ]== "logout" ) (
    sessão_start();
    session_destroy();
    header("Local: http://" . $_SERVER [ "HTTP_HOST" ]. "/" );
    saída;
    }

    if (!isset($_SESSION [ "user_id" ])) (
    ?>








    saída;
    }

    Agora tudo que você precisa fazer é escrever a linha em todos os scripts protegidos
    requer "auth.php";
    Este exemplo assume que a sessão já foi iniciada e uma conexão com o banco de dados foi criada usando a Classe para um trabalho seguro e conveniente com MySQL. Ele também pressupõe que a senha seja criptografada usando a função password_hash recomendada.
    Exemplo de arquivo protegido:

    sessão_start();
    inclua "safemysql.class.php";
    $db = novo safemysql ([ "db" => "teste" ]);
    inclua "auth.php";
    ?>
    segredo

    sair

    OPS! Links muito úteis:
    http://www.php.net/manual/ru/ref.session.php - as informações mais recentes sobre suporte a sessões em PHP na documentação oficial, além de vários comentários de usuários. Leitura altamente recomendada.
    http://phpclub.ru/manrus/f/ref.session.html - Uma tradução MUITO desatualizada deste capítulo para o russo, da documentação traduzida por Alexander Pyramidin.
    http://phpclub.ru/detail/article/sessions
    Um artigo com o título patético “A verdade sobre as sessões”. Deixa uma impressão ambivalente. No início, o autor fala MUITO claramente sobre o mecanismo de sessão, mas os métodos que ele oferece no final do artigo são completamente obscuros.

    Um artigo didático de Dmitry Borodin do site
    http://php.spb.ru/ NÃO é fortemente recomendado.
    Pessoal, está terrivelmente desatualizado. Não apenas contém imprecisões factuais, mas sessões em PHP simplesmente não são trabalhadas há muito tempo.
    Muito obrigado ao Dima por isso, este foi o primeiro artigo sobre sessões em russo, eu mesmo estudei, mas agora preciso mandá-lo para um merecido descanso.
    Infelizmente, muitos outros artigos na Internet que não são atualizados há anos também estão desatualizados.

  • Saudações, querida comunidade.

    Em primeiro lugar, quero agradecer-lhe pelo muito recurso útil. Mais de uma vez encontrei muitas ideias interessantes e conselhos práticos aqui.

    O objetivo deste artigo é destacar as armadilhas do uso de sessões em PHP. Claro, há documentação sobre PHP e muitos exemplos, e este artigo não tem a pretensão de ser guia completo. Ele foi projetado para revelar algumas das nuances do trabalho com sessões e proteger os desenvolvedores de perdas desnecessárias de tempo.

    O exemplo mais comum de uso de sessões é, obviamente, a autorização do usuário. Comecemos pela implementação mais básica para desenvolvê-la gradativamente à medida que surgem novas tarefas.

    (Para economizar espaço e tempo, limitaremos nossos exemplos apenas às próprias funções de sessão, em vez de construir aqui um aplicativo de teste completo com uma bela hierarquia de classes, tratamento abrangente de erros e outras coisas boas).

    Função startSession() ( // Se a sessão já foi iniciada, pare a execução e retorne TRUE // (o parâmetro session.auto_start no arquivo de configurações php.ini deve ser desabilitado - o valor padrão) if (session_id()) return true; else return session_start(); // Nota: Antes da versão 5.3.0, a função session_start() retornava TRUE mesmo se houvesse um erro // Se você estiver usando uma versão anterior à 5.3.0, faça uma verificação adicional para session_id() // após chamar a função session_start(). destroySession() ( if (session_id()) ( // Se houver uma sessão ativa, exclua os cookies da sessão, setcookie(session_name(), session_id(), time( )-60*60*24); // e destrói a sessão session_unset();

    Observação: Está implícito que conhecimento básico O leitor conhece as sessões PHP, portanto não abordaremos o princípio de funcionamento das funções session_start() e session_destroy() aqui. As tarefas de layout do formulário de login e autenticação do usuário não estão relacionadas ao tema do artigo, portanto também as omitiremos. Deixe-me apenas lembrar que para identificar o usuário em cada solicitação subsequente, no momento do login bem-sucedido, precisamos armazenar o identificador do usuário em uma variável de sessão (chamada userid, por exemplo), que estará disponível em todas as solicitações subsequentes dentro a vida da sessão. Também é necessário implementar o processamento do resultado da nossa função startSession(). Se a função retornar FALSE, exiba o formulário de login no navegador. Se a função retornou TRUE e existe uma variável de sessão contendo o identificador do usuário autorizado (no nosso caso - userid) - exiba a página do usuário autorizado (para obter mais informações sobre tratamento de erros, consulte o acréscimo datado de 2013-06- 07 na seção sobre variáveis ​​de sessão).

    Até agora está tudo claro. As dúvidas começam quando você precisa implementar o controle de inatividade do usuário (tempo limite da sessão), permitir que vários usuários trabalhem simultaneamente em um navegador e também proteger as sessões contra uso não autorizado. Isso será discutido abaixo.

    Monitorando a inatividade do usuário usando ferramentas PHP integradas

    A primeira questão que muitas vezes surge entre os desenvolvedores de todos os tipos de consoles para usuários é o encerramento automático da sessão em caso de inatividade por parte do usuário. Não há nada mais fácil do que fazer isso usando os recursos integrados do PHP. (Esta opção não é particularmente confiável ou flexível, mas iremos considerá-la para fins de integridade).

    Função startSession() ( // Tempo limite de inatividade do usuário (em segundos) $sessionLifetime = 300; if (session_id()) return true; // Define o tempo de vida do cookie ini_set("session.cookie_lifetime", $sessionLifetime); // Se o usuário o tempo limite de inatividade é definido, defina o tempo de vida da sessão no servidor // Nota: Para um servidor de produção, é recomendado predefinir esses parâmetros no arquivo php.ini if ​​($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime if (session_start(); )) ( setcookie(session_name(), session_id(), time()+$sessionLifetime); return true; ) else return false)

    Alguns esclarecimentos. Como você sabe, o PHP determina qual sessão precisa ser iniciada pelo nome do cookie enviado pelo navegador no cabeçalho da solicitação. O navegador, por sua vez, recebe esse cookie do servidor, onde a função session_start() o coloca. Se o cookie do navegador tiver expirado, ele não será enviado na solicitação, o que significa que o PHP não será capaz de determinar qual sessão iniciar e tratará isso como a criação de uma nova sessão. O parâmetro de configurações do PHP session.gc_maxlifetime, que é definido como igual ao tempo limite de inatividade do usuário, define o tempo de vida de uma sessão PHP e é controlado pelo servidor. O controle do tempo de vida da sessão funciona da seguinte maneira (aqui consideramos um exemplo de armazenamento de sessões em arquivos temporários como a opção mais comum e padrão em PHP).

    Quando uma nova sessão é criada, um arquivo chamado sess_ é criado no diretório definido como diretório de armazenamento da sessão no parâmetro de configurações do PHP session.save_path , Onde - identificador de sessão. A seguir, a cada solicitação, no momento de lançar uma sessão já existente, o PHP atualiza o horário de modificação deste arquivo. Assim, em cada próximo Solicitação PHP, pela diferença entre o horário atual e o horário da última modificação do arquivo da sessão, pode determinar se a sessão está ativa ou se seu tempo de vida já expirou. (O mecanismo para excluir arquivos de sessão antigos é discutido com mais detalhes na próxima seção.)

    Observação: Deve-se notar aqui que o parâmetro session.gc_maxlifetime se aplica a todas as sessões dentro de um servidor (mais precisamente, dentro de um processo PHP principal). Na prática, isso significa que se vários sites estiverem em execução no servidor e cada um deles tiver seu próprio tempo limite de inatividade do usuário, definir esse parâmetro em um dos sites levará à sua configuração para outros sites. O mesmo se aplica à hospedagem compartilhada. Para evitar essa situação, diretórios de sessão separados são usados ​​para cada site no mesmo servidor. A configuração do caminho para o diretório de sessões é feita usando o parâmetro session.save_path no arquivo de configurações php.ini ou chamando a função ini_set(). Após isso, as sessões de cada site serão armazenadas em diretórios separados, e o parâmetro session.gc_maxlifetime definido em um dos sites será válido apenas para sua sessão. Não consideraremos este caso em detalhes, principalmente porque temos uma opção mais flexível para monitorar a inatividade do usuário.

    Controlando a inatividade do usuário usando variáveis ​​de sessão

    Parece que a opção anterior, apesar de toda a sua simplicidade (apenas algumas linhas de código adicionais), oferece tudo o que precisamos. Mas e se nem todas as solicitações puderem ser consideradas resultado da atividade do usuário? Por exemplo, uma página possui um cronômetro que faz periodicamente uma solicitação AJAX para receber atualizações do servidor. Tal solicitação não pode ser considerada como atividade do usuário, o que significa que estender automaticamente o tempo de vida da sessão não é correto neste caso. Mas sabemos que o PHP atualiza o horário de modificação do arquivo da sessão automaticamente toda vez que a função session_start() é chamada, o que significa que qualquer solicitação levará a uma extensão do tempo de vida da sessão e o tempo limite de inatividade do usuário nunca ocorrerá. Além disso, a última observação da seção anterior sobre as complexidades do parâmetro session.gc_maxlifetime pode parecer muito confusa e difícil de implementar para alguns.

    Para resolver este problema, abandonaremos o uso de mecanismos PHP integrados e introduziremos diversas novas variáveis ​​de sessão que nos permitirão controlar o tempo de inatividade do usuário.

    Function startSession($isUserActivity=true) ( ​​​​$sessionLifetime = 300; if (session_id()) return true; // Define o tempo de vida do cookie antes de fechar o navegador (iremos controlar tudo no lado do servidor) ini_set("session. cookie_lifetime", 0) ; if (! session_start()) return false; $t = time(); if ($sessionLifetime) ( // Se o tempo limite de inatividade do usuário estiver definido, // verifica o tempo decorrido desde a última atividade do usuário // (hora da última solicitação) quando a variável de sessão lastactivity foi atualizada) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( // Se o tempo decorrido desde a última atividade do usuário, // é maior que o tempo limite de inatividade, o que significa que a sessão expirou e precisa ser encerrada destroySession() else ( // Se o tempo limite ainda não ocorreu, // e if; a solicitação veio como resultado da atividade do usuário, // atualiza a variável lastactivity com o valor do tempo atual, // estendendo assim o tempo da sessão por mais segundos sessionLifetime if ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) retorna verdadeiro; )

    Vamos resumir. Em cada requisição, verificamos se foi atingido o timeout desde a última atividade do usuário até o momento atual, e caso tenha sido atingido, destruímos a sessão e interrompemos a execução da função, retornando FALSE. Caso o tempo limite não tenha sido atingido e o parâmetro $isUserActivity com valor TRUE seja passado para a função, atualizamos o horário da última atividade do usuário. Tudo o que precisamos fazer é determinar no script de chamada se a solicitação é o resultado da atividade do usuário e, caso contrário, chamar a função startSession com o parâmetro $isUserActivity definido como FALSE.

    Atualização de 07/06/2013
    Processando o resultado da função sessionStart()

    Os comentários apontaram que retornar FALSE não fornece uma compreensão completa da causa do erro, e isso é absolutamente justo. Não publiquei aqui o tratamento detalhado de erros (a extensão do artigo já é bastante grande), pois não está diretamente relacionado ao tema do artigo. Mas dados os comentários, vou esclarecer.

    Como você pode ver, a função sessionStart pode retornar FALSE em dois casos. A sessão não pôde ser iniciada devido a alguns erros internos do servidor (por exemplo, configurações de sessão incorretas no php.ini) ou o tempo de vida da sessão expirou. No primeiro caso, devemos redirecionar o usuário para uma página com erro informando que há problemas no servidor e um formulário para contato com o suporte. No segundo caso, devemos transferir o usuário para o formulário de login e exibir nele uma mensagem correspondente informando que a sessão expirou. Para fazer isso, precisamos inserir códigos de erro e retornar o código correspondente em vez de FALSE, e no método de chamada, verificá-lo e agir de acordo.

    Agora, mesmo que ainda exista uma sessão no servidor, ela será destruída na primeira vez que for acessada caso o tempo limite de inatividade do usuário tenha expirado. E isso acontecerá independentemente do tempo de vida da sessão definido nas configurações globais do PHP.

    Observação: O que acontece se o navegador for fechado e o cookie do nome da sessão for automaticamente destruído? A solicitação ao servidor na próxima vez que o navegador for aberto não conterá os cookies da sessão, e o servidor não poderá abrir a sessão e verificar o tempo limite de inatividade do usuário. Para nós, isso equivale a criar uma nova sessão e não afeta de forma alguma a funcionalidade ou a segurança. Mas surge uma questão justa - quem destruirá a sessão antiga, se até agora a destruímos depois que o tempo limite expirou? Ou agora ficará pendurado no diretório de sessões para sempre? Para limpar sessões antigas em PHP, existe um mecanismo chamado coleta de lixo. Ele é executado no momento da próxima solicitação ao servidor e limpa todas as sessões antigas com base na data da última modificação dos arquivos da sessão. Mas o mecanismo de coleta de lixo não inicia a cada solicitação ao servidor. A frequência (ou melhor, a probabilidade) de lançamento é determinada por dois parâmetros de configuração session.gc_probability e session.gc_divisor. O resultado da divisão do primeiro parâmetro pelo segundo é a probabilidade de lançamento do mecanismo de coleta de lixo. Assim, para que o mecanismo de limpeza de sessão seja lançado a cada solicitação ao servidor, estes parâmetros devem ser configurados com valores iguais, por exemplo “1”. Esta abordagem garante um diretório de sessão limpo, mas é obviamente muito caro para o servidor. Portanto, em sistemas de produção, o valor padrão de session.gc_divisor é definido como 1000, o que significa que o mecanismo de coleta de lixo será executado com probabilidade de 1/1000. Se você experimentar essas configurações em seu arquivo php.ini, poderá notar que no caso descrito acima, quando o navegador fecha e limpa todos os cookies, ainda há sessões antigas no diretório de sessões por um tempo. Mas isso não deve preocupar você, porque... como já foi dito, isso não afeta de forma alguma a segurança do nosso mecanismo.

    Atualização de 07/06/2013

    Evitando o congelamento de scripts devido ao bloqueio de arquivos de sessão

    Os comentários levantaram a questão da execução simultânea de scripts congelados devido ao bloqueio do arquivo da sessão (a opção mais marcante é a pesquisa longa).

    Para começar, observo que esse problema não depende diretamente da carga do servidor ou do número de usuários. Claro que mais solicitações, mais lento será a execução dos scripts. Mas esta é uma dependência indireta. O problema aparece apenas dentro de uma sessão, quando o servidor recebe várias solicitações em nome de um usuário (por exemplo, uma delas é uma pesquisa longa e as demais são solicitações regulares). Cada solicitação tenta acessar o mesmo arquivo de sessão e, se a solicitação anterior não desbloquear o arquivo, a solicitação subsequente ficará aguardando.

    Para manter o bloqueio do arquivo de sessão no mínimo, é altamente recomendável fechar a sessão chamando a função session_write_close() imediatamente após todas as ações com variáveis ​​de sessão terem sido concluídas. Na prática, isso significa que você não deve armazenar tudo em variáveis ​​de sessão e acessá-las ao longo da execução do script. E se você precisar armazenar alguns dados de trabalho em variáveis ​​de sessão, leia-os imediatamente quando a sessão iniciar, salve-os em variáveis ​​locais para uso posterior e feche a sessão (ou seja, fechar a sessão usando a função session_write_close e não destruí-la usando session_destroy ).

    No nosso exemplo, isso significa que imediatamente após abrir uma sessão, verificando o seu tempo de vida e a existência de um usuário autorizado, devemos ler e salvar todos os dados adicionais exigido pela aplicação variáveis ​​de sessão (se houver), feche a sessão usando uma chamada para session_write_close() e continue executando o script, seja uma pesquisa longa ou uma solicitação regular.

    Protegendo sessões contra uso não autorizado

    Vamos imaginar a situação. Um de seus usuários recebe um Trojan que rouba os cookies do navegador (nos quais nossa sessão é armazenada) e os envia para o e-mail especificado. O invasor obtém o cookie e o utiliza para falsificar uma solicitação em nome do nosso usuário autorizado. O servidor aceita e processa com sucesso esta solicitação como se ela viesse de um usuário autorizado. Se a verificação adicional do endereço IP não for implementada, tal ataque levará a um hackeamento bem-sucedido da conta do usuário, com todas as consequências decorrentes.

    Por que isso foi possível? Obviamente, porque o nome e o identificador da sessão são sempre os mesmos durante todo o tempo de vida da sessão, e se você receber esses dados, poderá facilmente enviar solicitações em nome de outro usuário (é claro, durante o tempo de vida desta sessão). Este pode não ser o tipo de ataque mais comum, mas teoricamente parece bastante viável, especialmente considerando que tal Trojan nem precisa de direitos de administrador para roubar os cookies do navegador do usuário.

    Como você pode se proteger de ataques desse tipo? Novamente, obviamente, limitando a vida útil do identificador de sessão e alterando periodicamente o identificador dentro da mesma sessão. Também podemos alterar o nome da sessão excluindo completamente a antiga e criando uma nova sessão, copiando para ela todas as variáveis ​​de sessão da antiga. Mas isso não afeta a essência da abordagem, portanto, para simplificar, nos limitaremos apenas ao identificador da sessão.

    É claro que quanto menor o tempo de vida do ID da sessão, menos tempo um invasor terá para obter e usar cookies para falsificar uma solicitação do usuário. Idealmente, um novo identificador deve ser utilizado para cada solicitação, o que minimizará a possibilidade de utilização da sessão de outra pessoa. Mas consideraremos o caso geral quando o tempo de regeneração do identificador de sessão for definido arbitrariamente.

    (Omitiremos a parte do código que já foi discutida).

    Função startSession($isUserActivity=true) ( ​​// Tempo de vida do identificador de sessão $idLifetime = 60; ... if ($idLifetime) ( // Se o tempo de vida do identificador de sessão estiver definido, // verifica o tempo decorrido desde que a sessão foi iniciada criada ou a última regeneração // (hora da última solicitação quando a variável de sessão starttime foi atualizada) if (isset($_SESSION["starttime"])) ( if ($t-$_SESSION["starttime"] >= $ idLifetime) ( // Tempo de vida útil do identificador de sessão expirou // Gere um novo identificador session_regenerate_id(true); $_SESSION["starttime"] = $t) ) else ( // Chegamos aqui se a sessão acabou de ser criada // Defina o tempo de geração do identificador de sessão para hora atual$_SESSION["horário de início"] = $t; ) ) retorna verdadeiro; )

    Assim, ao criar uma nova sessão (que ocorre quando o usuário faz login com sucesso), definimos a variável de sessão starttime, que armazena para nós o horário da última geração do identificador de sessão, para um valor igual ao horário atual do servidor. A seguir, em cada solicitação, verificamos se passou tempo suficiente (idLifetime) desde a última geração do identificador e, em caso afirmativo, geramos um novo. Assim, se durante o tempo de vida definido do identificador o invasor que recebeu o cookie do usuário autorizado não tiver tempo de utilizá-lo, a solicitação falsa será considerada pelo servidor como não autorizada e o invasor será direcionado para a página de login .

    Observação: O novo ID de sessão entra no cookie do navegador quando a função session_regenerate_id() é chamada, que envia o novo cookie, semelhante à função session_start(), então não precisamos atualizar o cookie nós mesmos.

    Se quisermos tornar nossas sessões o mais seguras possível, basta definir o tempo de vida do identificador como um ou até mesmo remover a função session_regenerate_id() dos colchetes e remover todas as verificações, o que levará à regeneração do identificador em cada solicitar. (Não testei o impacto dessa abordagem no desempenho e só posso dizer que a função session_regenerate_id(true) executa essencialmente apenas 4 ações: gerar um novo identificador, criar um cabeçalho com o cookie de sessão, excluir o antigo e criar um novo arquivo de sessão).

    Digressão lírica: Se o Trojan for tão inteligente que não enviará cookies ao invasor, mas organizará o envio de uma solicitação falsa pré-preparada imediatamente após receber o cookie, o método descrito acima provavelmente não será capaz de proteger contra tal um ataque, pois entre o momento em que o Trojan recebe o cookie e o envio da solicitação falsa praticamente não haverá diferença, e há uma grande probabilidade de que neste momento o identificador da sessão não seja regenerado.

    Possibilidade de trabalho simultâneo em um navegador em nome de vários usuários

    A última tarefa que gostaria de considerar é a capacidade de vários usuários trabalharem simultaneamente em um navegador. Este recurso é especialmente útil na fase de testes, quando você precisa emular o trabalho simultâneo dos usuários, e é aconselhável fazer isso no seu navegador favorito, ao invés de usar todo o arsenal disponível ou abrir várias instâncias do navegador no modo de navegação anônima .

    Em nossos exemplos anteriores, não especificamos explicitamente um nome de sessão, então o nome PHP padrão (PHPSESSID) foi usado. Isso significa que todas as sessões que criamos até agora enviaram um cookie ao navegador com o nome PHPSESSID. Obviamente, se o nome do cookie for sempre o mesmo, não há como organizar duas sessões com o mesmo nome no mesmo navegador. Mas se usássemos nosso próprio nome de sessão para cada usuário, o problema estaria resolvido. Vamos fazer isso.

    Função startSession($isUserActivity=true, $prefix=null) ( ... if (session_id()) return true; // Se o prefixo do usuário for passado nos parâmetros, // defina um nome de sessão exclusivo que inclua isso prefix, // caso contrário, defina o nome comum para todos os usuários (por exemplo, MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); if (! session_start()) retorna falso;

    Agora tudo o que resta é garantir que o script de chamada passe um prefixo exclusivo para cada usuário para a função startSession(). Isto pode ser feito, por exemplo, passando um prefixo nos parâmetros GET/POST de cada requisição ou através de um cookie adicional.

    Conclusão

    Concluindo, fornecerei o código final completo de nossas funções para trabalhar com sessões PHP, incluindo todas as tarefas discutidas acima.

    Função startSession($isUserActivity=true, $prefix=null) ( $sessionLifetime = 300; $idLifetime = 60; if (session_id()) return true; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( destroySession(); return false; ) else ( if ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) if ($idLifetime ) ( if (isset($_SESSION["starttime"])) ( if ($t-$_SESSION["starttime"] >= $idLifetime) ( session_regenerate_id(true); $_SESSION["starttime"] = $t; ) ) else ( $_SESSION["starttime"] = $t; ) ) retorna true; função destroySession() ( if (session_id()) ( session_unset(); setcookie(session_name(), session_id(), time() -60* 60*24);sessão_destruição();

    Espero que este artigo economize algum tempo para aqueles que nunca se aprofundaram muito no mecanismo de sessão e forneça informações suficientes sobre esse mecanismo para aqueles que estão apenas começando a se familiarizar com o PHP.