[TYPO3-dev] Tangled-up user authentication

Christopher Lörken christopher at loerken.net
Sun Apr 19 15:24:50 CEST 2009


Hello everybody,

I've got some questions regarding the Typo3 4.2 code for authenticating 
FE sessions and logging in users.

In short, the background is that we have experienced some performance 
issues on our site. We are running a browser-game which uses a Java 
backend application (same physical server) that causes quite some load 
on the database leaving us with a mere 20% of SELECT queries in total. I 
tried to profile our DB and why it was running slow using Jet Profiler 
(if anyone has tips for better programs, please tell me!!). To my 
surprise, Jet Profiler showed that roughly 60% of the time the database 
needed was spend in fe_users and especially in fe_sessions tables....

60% of MySQL time spend in those two tables? That puzzled me so I 
XCLASSed t3lib_db to see what queries are executed and I found, that:

- for not logged in users, each page hit causes (summed)
	- 2 SELECT call on fe_session_data
	- 1 SELECT call on fe_sessions
	- 2 SELECT calls on fe_sessions joined with fe_users
	- 2 DELETE calls on fe_sessions
- for a logged in user with a valid cookie:
	- 1 SELECT call on fe_sessions
	- 1 SELECT call on fe_session_data
	- 1 DELETE call on fe_session_data (every 100th call)
	- 2 SELECT calls on fe_sessions joined with fe_users
	- 2 UPDATE calls on fe_sessions
	- 1 Update on fe_users if "is_online" it at least 60 seconds old.


So in total there are 7 DB calls for not logged in users with every page 
hit and 6-8 DB calls for logged in users with every page hit. This seems 
to me like an unnecessarily high number...

Wouldn't the following steps suffice:

1) SELECT from fe_sessions the row with the set ses_id
    a) if IPlock, ses_tstamp, and so on are correct: Proceed with 2
    b) if they are not correct, DELETE the row from fe_sessions (maybe 
fe_session_data?), keep the session ID (or assign new?) and assume this 
is a guest user. Done.
    c) if there is no such record, check the session_data table if there 
are extensions using that table (would be a nice config flag I 
guess...). Keep the session ID. Done.
2) Session data is valid, so select the fe_user and (if necessary) the 
corresponding session_data.
    a) If the user is invalid (deleted, etc.) void the session, assume 
Guest user, done.
3) Every 60 seconds, update fe_sessions.tstamp
4) Every 60 seconds, update fe_user.is_online

In other words, what I see necessary are 1-2 calls for not logged in 
users and 2-5 calls for logged in users... That is a difference (current 
worst case against possible best case) of 12 database queries...

Please, do tell me if I miss something here...




Some words about the actual implementation:

**1. Calls to fetchUserSession**
The really redundant calls that are simply executed twice in a row are 
caused by fetchUserSession(). fetchUserSession tries to get the session 
and the corresponding user with a joined SELECT over fe_sessions and 
fe_user. If it has found the row, it will (always) update the tstamp in 
fe_sessions. (There is actually a comment in the 4.2 source that it is 
probably not necessary to _always_ do that.)
That method is first called in authenticateUser() which simply does not 
use the result but merely sets $this->loginFailure to false... And what 
we find as code in t3lib_userauth is:

	$this->checkAuthentication();
	unset($this->user);
	$this->user = $this->fetchUserSession();

So as just said: checkAuthentication calls fetchUserSession and 
afterwards it is fetched right again, duplicating the JOIN SELECT and 
the fe_sessions tstamp UPDATE. While the SELECT is probably still in the 
MySQL query cache, the UPDATE will probably at least cause some load 
(although the second time at least nothing needs to be physically written).


**2. isExistingSessionRecord**
As I understand the Hotfix against session fixation... It is responsible 
for one of the SELECTs on fe_sessions in both, logged in and logged out 
cases and, when logged out, as well for 1 SELECT on session_data. The 
result of that call is again only true or false. The call only selects a 
count(*) instead of reading that row for later usage.


**3. isExistingSessionRecords influence on the session ID and the MySQL 
query cache**
When a user is not logged in, the Method results in a change of the 
fe_typo_user session ID with every page a user loads. This effectively 
voids any possible positive effect of MySQL query cache since the looked 
for key is never the same. Remember here that there are two selects, one 
on fe_sessions, one on fe_session_data wich use that key. Changing the 
sessionID every time does not only make no use of the MySQL query cache, 
it also spams it with queries which are never again to be experienced...


**4. fe_session_data**
We are not using that many extensions, but I did not yet come along one 
which actually uses the fe_session_data table. I know there are some 
basket case implementations and such, but I'd guess that there are many 
Typo3 installations who do not actually need that table. Here it would 
be nice to get rid of the overload of checking that table with every 
pagevisit (up to 2 times!) and from time to time issueing DELETE queries 
on an anyway emtpy table. I'd suggest a config flag which disables that 
table and skips those unnecessary database calls.


**5. JOINED select on fe_sessions and fe_users**
I'm very unsure about this last point, as I'm not an expert when it 
comes to databases, but I kind of wonder, if there may be performance 
problems and slow queries by having fe_users a MyISAM table... The table 
is read heavily but is also frequently updated by changing is_online 
tstamps... Since those updates lock the whole table they probably would 
block reads and joined reads even back in the fe_sessions table... My 
proposal above would split the calls to fe_sessions and fe_users, mainly 
to be able to know that there is a session record for an ID even by 
ignoring an IP lock or ses_id of a disabled user.

Well... I Don't really know where I'm aiming at here but I've got an 
unsure feeling ;)



So... I'm about to XCLASS tslib_feuserauth to rewrite necessary parts 
for a "slim" authentication. What I would really like to know is if I 
have overlooked some crucial part in my simple setup... I

especially do not understand the famous session fixation problem and why 
the ID always changes...

All comments on any of this are highly appreciated.

Have a nice Sunday,
Christopher




More information about the TYPO3-dev mailing list