Avatar billede Slettet bruger
06. marts 2011 - 15:40 Der er 15 kommentarer og
1 løsning

Login vha. sessions - Tidsramme?

Hej eksperter.

Jeg har opbygget mit login vha. session, og benytter derfor nedenstående:

session("login") = 1
session("id") = rs("id")
session("level") = rs("level")

Men jeg oplever, at mine sessions droppes af serveren nogle gange allerede efter et kvarter.

Er det muligt, at bede serveren om først, at droppe dem efter en meget længere interval, som fx 4 timer?
Eller endda, holde liv i sessions'ne indtil der logges af, eller browseren lukkes, som der f.eks. gøres på facebook?
Avatar billede keysersoze Guru
06. marts 2011 - 16:08 #1
Du kan forsøge at sætte session timeout højere op - men da du med en vis sandsynlighed ligger på shared hosting vil du stadig være alt for afhængig af hvad der ellers sker på serveren. Det bedste du derfor kan gøre er at lave en slags ping på din side - fx en iframe med en side der opdateres fx hvert 5. minut eller et stykke javascript der kører asynkront.
Avatar billede claes57 Ekspert
06. marts 2011 - 16:11 #2
http://www.w3schools.com/asp/prop_timeout.asp
"Default is 20 minutes"
med
Session.Timeout=240
retter du det til 4 timer.
Avatar billede softspot Forsker
06. marts 2011 - 16:22 #3
Normalt vil sessiontimeout være sat til 20 minutter, men det kan du bare ikke regne med, for hvis serveren beslutter at den workerprocess dit sitet kører under skal lukkes ned (f.eks. fordi der er timeout på processen, eller der er udstedt et givet antal sider og processen derfor skal recycles for at holde et nogenlunde stabilt miljø), så kan du lave nok så mange iframes der forsøger at opdatere sin session, det vil ikke hjælpe noget.

Hvis du vil ud over dette problem, skal du finde dig en anden strategi til at håndtere sessiondata end det indbyggede session-objekt. Dette kunne f.eks. være at kombinere en databasestyret session, i samspil med den indbyggede session, således det indbyggede sessionobjekt fungerer som en writethrough-cache ifht. databasen. Nøglen til data er så en eller anden afkodning af en cookie du sender frem og tilbage mellem klienten og serveren...

Denne metode vil også skalere meget bedre end brugen af session, da den ikke vil være helt så sårbar overfor udskalering, dvs. webgardening og webfarming, som en ensidig brug af session-objektet vil være. Optimalt set kigge du på et trediepartsprodukt til distribueret caching, men det er nok ikke noget din udbyder som standard arbejder i, kunne jeg forestille mig.
Avatar billede softspot Forsker
06. marts 2011 - 16:25 #4
Check evt. denne tråd, hvis jeg kommer lidt mere i dybden med en konkret løsning: http://www.eksperten.dk/spm/925871
Avatar billede softspot Forsker
15. marts 2011 - 12:24 #5
Er du kommet videre med dette...?
Avatar billede Slettet bruger
15. marts 2011 - 21:59 #6
Jeg har desværre ikke haft tid til at kigge på det :/
men det gør jeg i morgen og fredag, og så skal jeg nok lige give en melding tilbage :)

Tak for hjælpen indtil videre ihvert fald!
Avatar billede Slettet bruger
29. april 2011 - 00:45 #7
Hej Softspot.

Jeg håber, at du stadig kigger med.

Jeg har desværre ikke haft tid til at kigge på dette før nu :/
Men nu skal det tilgengæld også ske :)

Jeg har læst den anden tråd igennem, og løsningen med at gemme en cookie med en tilfældigt genereret nøgle, som er den der skal forbinde mem-sessionen (session("id")) med db-sessionen (rs("id"))

Din fremgangsmåde:
- check memory-session (mem-session).
-- hvis mem-session eksisterer, så anvend den og opdater timestamp i database-session (db-session).
-- hvis mem-session ikke eksisterer, så check om der er en db-session (på grundlag af den nøgle du selv har genereret).
--- hvis der findes en db-session, så indlæs session-data til mem-session.
--- sæt timestamp i databasen
- send svar til klienten...

Denne fremgangsmåde virker også ganske fornuftig.

Ligeledes har jeg fundet SilenceWar's funktion til at lave en tilfældig streng til cookien.

MEN, her det så jeg går i stå.
For jeg har ALDRIG nogensinde arbejdet med cookies før.
Så mit spørgsmål er, hvordan får jeg systemet til at kigge om cookien, mem-session og db-session findes?
Hvis det ikke gør, så oprettes.
Hvis det gør, så sammenligner mem-sessionen med db-sessionen, og i tilfælde af død mem-session, så cookien med db-sessionen og derefter oprettelsen af mem-sessionen påny.
Og det hele selvfølgelig med timestamp, så der kan forhåndsdefineres et tidsinterval på fx 4 timer?

Jeg er helt grøn på det her punkt, så håber du kan hjælpe.

Samtidigt, er min plan at gøre loginet SQL-injection sikkert vha. din guide (http://www.eksperten.dk/guide/1250), men det kan jeg vel begynde at kigge på når dette problem er klaret? :)

MEN, her kommer så mit problem.
Avatar billede softspot Forsker
04. maj 2011 - 08:45 #8
Du kan aflæse cookies via Request.Cookies("cookienavn") og sætte dem igen via Response.Cookies("cookienavn") (hvor "cookienavn", oplagt, er navnet på den cookie du vil gemme :-)).

Du skal blot være opmærksom på at når du sætter en cookie (via response), kan den ikke aflæses igen (via request), før klienten sender et nyt request til serveren, dvs. du kan ikke på samme side sætte response.cookies og aflæse den igen med request.cookies.

Hvis du vil sætte udløbet af en cookie kan du benytte expires på en cookie, f.eks.

Response.Cookies("cookie").expires = dateadd("h", 4, now)

for at cookien skal udløbe om 4 timer fra nu. Hvis du ønsker et glidende udløb af din cookie (dvs. at udløbet skal gælde fra sidste brug af cookien, skal du opfriske expires hver gang brugeren sender et request til serveren (ovenstående linje kan benyttes til dette formål). Hvis du ikke opfrisker en cookie's expires efter første gang du sætter den, vil den udløbe efter 4 timer i dette eksempels tilfælde.
Avatar billede Slettet bruger
04. maj 2011 - 12:00 #9
Så i princippet, behøver man slet ikke sessions til et login? Man kan blot tjekke cookien? Eller kræver det for mange ressourcer, kontra sessions?
Avatar billede softspot Forsker
04. maj 2011 - 12:39 #10
Du kan bruge cookien til at identificere brugeren ifht. de data som vedrører denne bruger (som så ligge et andet sted, f.eks. i en database). Det er typisk samme måde sessions fungerer på, sessions tilbyder bare en række faciliteter "ud af boksen", som du ellers selv skal kode - hvilket du så er i gang med nu... :-)

ASP's sessionstyring sker i den aktuelle workerproces' hukommelse, hvilket er årsag til flere problemstillinger (herunder at data går tabt når denne workerproces bliver genstartet af IIS). Det er derfor du selv skal håndtere sessiondata udenfor workerprocessens hukommelsen, således sessiondata bevares indtil du ikke ønsker de skal bevares længere...

Det jeg foreslog for SilenceWar var en hybridløsning, hvor session stadg benyttes, men blot som en cache, for at speede læsning af data op. Du kan i princippet helt undlade session i din webapplikation og bare styre data direkte i databasen, men det vil belaste din database, hvis du ikke har en eller anden form for caching til trivielle læsninger.

Dette kompliceres yderligere i miljøer, hvor du kører webgardening (dvs. at en application pool serviceres af flere workerprocesser) eller webfarms (dvs. at en webapplikation serviceres af flere servere), da en lokal caching af sessiondata vil kræve synkronisering, da du aldrig kan vide hvilken process der får brugerens request næste gang og derfor skal sikre dig at cachen er opdateret. I disse scenarier må det være centrale cachingløsninger du har behov for at abejde med, da den hjemmestrikkede nok bliver lidt kompleks...
Avatar billede Slettet bruger
04. maj 2011 - 13:36 #11
Kom godt til at tænke over løsningen med ren cookies efter jeg havde lavet kommentaren. Tror simpelthen ikke, at den er sikker nok mht lagring af brugerid og brugerniveau, da den ligger lokalt på computeren, og er du en haj nok til computeren, kan det vel ændres vha. diverse programmer.

Derfor har jeg valgt den løsning der hele tiden har været tale om, og vil da også gerne lige smide min kode herud.

Først og fremmest, funktionen til at generere den tilfældige kode til cookien:
function randomKeyString()
  Randomize()
 
  dim characterSetArray
  characterSetArray = Array( _
    Array( 2, "=?-*!#%" ), _
    Array( 5, "ABCDEFGHIJKLMNOPQRSTUVXYZ" ), _
    Array( 5, "abcdefghijklmnopqrstuvwxyz" ), _
    Array( 5, "0123456789" ) _
  )
 
  dim i
  dim j
  dim count
  dim chars
  dim index
  dim temp
  for i = 0 to ubound(characterSetArray)
    count = characterSetArray(i)(0)
    chars = characterSetArray(i)(1)
    for j = 1 to count
      index = int(rnd() * len(Chars)) + 1
      temp = temp & mid(chars, index, 1)
    next
  next
 
  dim tempcopy
  do until len(temp) = 0
    index = int(rnd() * len(temp)) + 1
    tempCopy = tempCopy & mid(temp, index, 1)
    temp = mid(temp, 1, index - 1) & mid(temp, Index + 1)
  loop
 
  randomKeyString = tempCopy
end function


Dernæst, er der oprettet en sql database tabel med følgende felter:
owner - int (brugerid)
level - int (brugerniveau)
period - date (datoen)
starttime - time(0) (logintidspunkt)
allowedTime - int (antal tilladte minutter i "idle"-tilstand
keyString - varchar(max) (cookie nøglen)

Når der logges ind, sker følgende:
  keyString = randomKeyString()
  session("id") = rs("id")
  session("level") = rs("level")
  session("login") = 1
  session.timeout = 240
  response.cookies(cookieName) = keyString
 
  sql = "insert into loginsTable (owner, level, period, starttime, allowedTime, keystring) values (" & session("id") & ", " & session("level") & ", getDate(), convert(varchar(8), getDate(), 108), 240, '" & keyString & "')"
  conn.execute(sql)


Den overordnede side for alle sider, tjekker altid om session("login") er lig 1, men er den ikke det, køres funktionen checkLogin(), og hvis den ER lig 1 køres funktionen updateLogin()

Funktionen for at sammenligne cookien og databasen i tilfælde af session-udløb er som følger:

function checkLogin()
  keyString = request.cookies(cookieName)
 
sql = "select * from loginsTable where keyString = '" & keyString & "'"
  set rs = conn.execute(sql)
 
  if rs.eof then
    response.cookies(cookieName) = ""
  else
    outOfTime = false
    period = rs("period")
    if realDate(period)<> date() then
      outOfTime = true
    else
      allowedTime = rs("allowedTime")
      startTimeSplitted = split(rs("startTime"), ".")
      currentTimeSplitted = split(time(), ".")
     
      if cint(datediff("n", startTimeSplitted(0), currentTimeSplitted(0))) <= cint(allowedTime) then
        owner = rs("owner")
        level = rs("level")
       
        session("id") = owner
        session("level") = level
        session("login") = 1
       
        response.redirect(rdName)
      else
        outOfTime = true
      end if
    end if
   
    if outOfTime = true then
      response.cookies(cookieName) = ""
     
      sql = "delete from " & loginsTable & " where keyString = '" & keyString & "'"
      conn.execute(sql)
     
      session("id") = ""
      session("level") = ""
      session("login") = ""
      session.abandon
    end if
  end if
end function

Og funktionen for at opdatere databasens starttid, hvis sessionen ikke har foretaget udløb er som følger:

function updateLogin()
  sql = "update loginsTable set period = getDate(), starttime = convert(varchar(8), getDate(), 108) where owner = " & session("id")
  conn.execute(sql)
end function


Nu vil jeg lade computeren stå 3 timers tid, og se om jeg bliver kastet af eller ej :)

Hvis du ser fejl softspot eller forbedringer, må du meget gerne sige til.
Næste projekt, bliver at sikre loginet imod SQL injections, men alt dette gøres jo inden hele denne process udføres :)

Og smid da også gerne et svar (også selvom du ikke er pointrytter) :)
Avatar billede Slettet bruger
04. maj 2011 - 13:50 #12
Jeg lavede lige et lille tweak på updateLogin()
I tilfælde af, at man logger ind via en anden computer, er det den sidste computer man har logget ind på, der fremover opdaterer starttiden i databasen, og det er også den computers cookies nøgle som databasen sammenligner sig selv med.
Er den første computers session så udløbet, er det bare ærgerligt, og den må altså logge ind igen af sikkerhedsmæssige årsager.

Det vil sjældent ske, men hvad nu hvis :)

Samtidigt, så kan tabellen i databasen nu også benyttes til et præcist overblik over hvem, hvornår og hvor mange der er logget på systemet, hvis man finder det behageligt :)

function updateLogin()
  keyString = request.cookies(cookieName)
 
  sql = "select * from loginsTable where owner = " & session("id") & " and keyString = '" & keyString & "'"
  set rs = conn.execute(sql)
 
  if not rs.eof then
    sql = "update loginsTable set period = getDate(), starttime = convert(varchar(8), getDate(), 108) where owner = " & session("id") & " and keyString = '" & keyString & "'"
    conn.execute(sql)
   
    sql = "delete from loginsTable where owner = " & session("id") & " and keyString <> '" & keyString & "'"
    conn.execute(sql)
  end if
end function
Avatar billede softspot Forsker
04. maj 2011 - 14:34 #13
Du skal vel, i forbindelse med selve login på den anden computer, generere en ny cookie, da den cookie der kommer fra denne maskine er tom. Det betyder samtidig at den første maskine automatisk er logget af, da der ikke længere er nogen matchende session til den oprindelige cookie... I denne forbindelse kan du slå en evt. eksisterende (database)session op på brugerens id i stedet for cookienøglen og så blot opdatere nøglen, hvis brugeren har en aktiv session (du har jo fat i brugerens id fra selve logintjekket).

Jeg forstår i øvrigt ikke helt formålet med feltet starttime. Det er i bedste fald blot en delmængde af værdien i period, så hvorfor vedligeholde den to steder? Det er bedre at gemme sidste sessionopdatering i et datetime-felt i databasen og så operere med dette ene felt alene. Du kan tjekke udløb således:

if dateadd("n", rs("allowedTime"), rs("period")) > now then
  ' do your stuff...
else
  ' session timed out!
end if

Alternativt kan du bare lade selve opslaget i databasen returnere et tomt resultat, hvis session er timed out.

select *
from loginsTable
where dateadd(minute, allowedTime, period) > getdate()
and keystring = ?

hvor spørgsmålstegnet er parameteren med keystring fra ASP-scriptet. Hvis resultatet er tomt er der timeout ellers er det ok at fortsætte denne session.

Mht. din kode til checkLogin, så har du introduceret en "angrebsvektor" (injectionrisiko) i og med du aflæser cookien og benytter den direkte i SQL-sætningen.

keyString = request.cookies(cookieName)
 
sql = "select * from loginsTable where keyString = '" & keyString & "'"

En lidt snedig bruger kunne faktisk fingere cookien til at indeholde en værdi som ændrede din SQL-sætning. Derfor bør alt hvad du laver mht. sessionstrying også benytte sig af parameteriserede databaseopslag - men det kommer du jo til senere (nu er du advaret om problemet her)... :-)

Desuden har jeg en anden funktion til nøglegenerering, som jeg synes er lidt mere kompakt (jeg har ikke helt gennemskuet hvad de ekstra løkker skal til for):

function generateCookieKey()
  dim i
  dim key: key = array()
  dim chars: chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  dim lenChars: lenChars = len(chars)
  const cookieKeyLen = 20
  redim key(cookieKeyLen)
 
  Randomize
  for i = 0 to cookieKeyLen - 1
    key(i) = mid(chars, int(rnd() * lenChars) + 1, 1)
  next
 
  generateCookieKey = join(key, "")
end function

I småtingsafdelingen er det ikke strengt nødvendigt at sætte session.timeout, da session jo gerne automatisk skulle blive indlæst fra databasen, hvis den ikke eksisterer i hukommelsen (udløbet eller fjernet).

Jeg lurer lige på resten af din kode og vender tilbage hvis der er yderligere kommentarer... :-)
Avatar billede softspot Forsker
04. maj 2011 - 14:46 #14
Linjeskiftet efter "chars =" i generateCookieKey er ikke tiltænkt, det er noget eksperten har gjort, fordi linien overskred bredden af kolonnen. Den skulle altså se således ud:

dim chars
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" & _
        "abcdefghijklmnopqrstuvwxyz" & _
        "0123456789"


Du får lige et svar, så du kan hæfte dine point på et eller andet, når du kommer så vidt ;-)
Avatar billede Slettet bruger
04. maj 2011 - 17:19 #15
"Du skal vel, i forbindelse med selve login på den anden computer, generere en ny cookie, da den cookie der kommer fra denne maskine er tom. Det betyder samtidig at den første maskine automatisk er logget af, da der ikke længere er nogen matchende session til den oprindelige cookie..."

Nu har jeg sat den til at rydde sessions hvis der ingen nøgle er i databasen som passer under kørsel af updateLogin() :)

"I denne forbindelse kan du slå en evt. eksisterende (database)session op på brugerens id i stedet for cookienøglen og så blot opdatere nøglen, hvis brugeren har en aktiv session (du har jo fat i brugerens id fra selve logintjekket)."

Hvis jeg opdaterer databasens indskrevne nøgle, med den nye nøgle, så kan jeg risikere at 2 computere kan være logget på samme bruger på samme tidspunkt, og så står de bare og opdatere nøglen konstant i databasen fra den ene til den anden og omvendt.
Og det giver vel en sikkerhedsrisiko i sidste ende, hvis ikke man bliver logget ud på pc 1, lige så snart pc 2 logger ind.

"Jeg forstår i øvrigt ikke helt formålet med feltet starttime. Det er i bedste fald blot en delmængde af værdien i period, så hvorfor vedligeholde den to steder? Det er bedre at gemme sidste sessionopdatering i et datetime-felt i databasen og så operere med dette ene felt alene."

Starttime og Period er delt op så starttime kun indeholder tiden i formatet hh:nn:ss, og period kun indeholder datoen i formatet mm-dd-yyyy. Grunden til dette, er fordi jeg gerne vil lave en oversigt over alle brugere der er logget ind.
Og så kunne jeg lige så godt dele det op på den måde :)
Dog har det den ulempe, at der hele tiden skal opdateres 2 ting, og sammenligningen med tidsintervallet over midnat garanteret logger brugeren ud. Så måske et fælles dato/tid felt er en bedre løsning til sammenligning heraf, og så senere hen filtrere dato og tiden ud fra denne.

"Alternativt kan du bare lade selve opslaget i databasen returnere et tomt resultat, hvis session er timed out."

Denne løsning vil jeg lige indføre når jeg får ændret de 2 felter nævn ovenfor :)

"En lidt snedig bruger kunne faktisk fingere cookien til at indeholde en værdi som ændrede din SQL-sætning. Derfor bør alt hvad du laver mht. sessionstrying også benytte sig af parameteriserede databaseopslag - men det kommer du jo til senere (nu er du advaret om problemet her)"

Den vil jeg lige følge op på, når jeg når til SQL injections :)
Men tak for advarslen :)

Desuden har jeg en anden funktion til nøglegenerering, som jeg synes er lidt mere kompakt (jeg har ikke helt gennemskuet hvad de ekstra løkker skal til for)

Fantastisk med en lettere kode til generering af den tilfældige streng :) Mange tak!

"I småtingsafdelingen er det ikke strengt nødvendigt at sætte session.timeout, da session jo gerne automatisk skulle blive indlæst fra databasen, hvis den ikke eksisterer i hukommelsen (udløbet eller fjernet)."

Denne var bare sat i det dejlige tilfælde, at serveren ikke laver en recycle på sessionerne, så holder de så længe som muligt og skal altså igennem hele checkLogin() funktionen, men kan så nøjes med updateLogin() funktionen, som er en del "lettere"



Generel:
Jeg siger mange tak for hjælpen, som gjorde mig en del klogere på cookies.
Nu vil jeg lave de rettelser som anvist, og så igang med SQL Injections ud fra den guide du førhen har skrevet. Det kan dog være, at jeg laver et nyt spørgsmål på eksperten herom, i tilfælde af, at jeg sidder fast :)
Men points givet! Tak for hjælpen igen igen! :)
Avatar billede softspot Forsker
04. maj 2011 - 17:31 #16
Velbekomme :-)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester